面试题答案
一键面试设计思路
- 节点结构:设计一个
TreeNode
结构体,包含节点数据、指向父节点的弱引用(Weak
)、指向子节点的Rc<RefCell<TreeNode>>
向量以及指向兄弟节点的Option<Rc<RefCell<TreeNode>>>
。使用RefCell
来在运行时检查可变借用规则,从而突破编译期的借用检查限制。 - 动态操作:为
TreeNode
结构体实现添加子节点和删除子节点的方法。添加子节点时,更新父节点和子节点的关系;删除子节点时,更新父节点和兄弟节点的关系。 - 借用冲突与生命周期管理:通过
RefCell
的borrow
和borrow_mut
方法来获取不可变和可变引用。在操作涉及多个节点时,合理安排借用顺序,避免出现循环借用。使用Weak
引用避免循环引用导致的内存泄漏。
关键代码实现
use std::cell::RefCell;
use std::rc::{Rc, Weak};
// 定义树形节点
#[derive(Debug)]
struct TreeNode<T> {
data: T,
parent: Weak<RefCell<TreeNode<T>>>,
children: Vec<Rc<RefCell<TreeNode<T>>>>,
sibling: Option<Rc<RefCell<TreeNode<T>>>>,
}
impl<T> TreeNode<T> {
// 创建新节点
fn new(data: T) -> Rc<RefCell<TreeNode<T>>> {
Rc::new(RefCell::new(TreeNode {
data,
parent: Weak::new(),
children: Vec::new(),
sibling: None,
}))
}
// 添加子节点
fn add_child(&mut self, child: Rc<RefCell<TreeNode<T>>>) {
let weak_parent = Rc::downgrade(&Rc::clone(&self));
child.borrow_mut().parent = weak_parent;
if let Some(last_sibling) = self.children.last_mut() {
last_sibling.borrow_mut().sibling = Some(Rc::clone(&child));
}
self.children.push(Rc::clone(&child));
}
// 删除子节点
fn remove_child(&mut self, index: usize) {
if index >= self.children.len() {
return;
}
let removed_child = self.children.remove(index);
if let Some(next_sibling) = removed_child.borrow().sibling.take() {
if let Some(last_child) = self.children.last_mut() {
if Rc::ptr_eq(&next_sibling, &last_child) {
last_child.borrow_mut().sibling = None;
}
}
}
}
}
解释
- TreeNode 结构体:包含节点数据
data
,父节点的Weak
引用parent
,子节点的Rc<RefCell<TreeNode<T>>>
向量children
,以及兄弟节点的Option<Rc<RefCell<TreeNode<T>>>>
。 - new 方法:创建新的树形节点,初始时父节点为
Weak::new()
,子节点和兄弟节点为空。 - add_child 方法:将新的子节点添加到当前节点的子节点列表中,并更新父节点和兄弟节点的关系。
- remove_child 方法:从当前节点的子节点列表中删除指定索引的子节点,并更新兄弟节点的关系。
通过这种设计,可以有效管理树形结构中的动态添加和删除操作,同时处理好RefCell
带来的借用冲突和生命周期管理问题。