You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中实现带兄弟指针的二叉树节点及setSibling功能的规范方式

问题描述

我想实现C语言中经典的setSibling练习的Rust等价代码。以下是C语言实现(假设树为完全平衡状态,即最低层节点完全填充):

// Assume the tree is fully balanced, i.e. the lowest level is fully populated.
struct Node {
   Node * left;
   Node * right;
   Node * sibling;
}

void setSibling(Node * root) {
   if (!root) return;
   if (root->left) {
      root->left->sibling = root->right;
      if (root->sibling) root->right->sibling = root->sibling->left;
      setSibling(root->left);
      setSibling(root->right);
   }
}

由于Rust的所有权机制与C不同,我在尝试实现时遇到了问题,以下是我的初步尝试:

struct TreeNode<'a> {
    left: Option<&'a TreeNode<'a>>,
    right: Option<&'a TreeNode<'a>>,
    sibling: Option<&'a TreeNode<'a>>,
    value: String
}

fn BuildTreeNode<'a>(aLeft: Option<&'a TreeNode<'a>>, aRight: Option<&'a TreeNode<'a>>, aValue: String) -> TreeNode<'a> {
    TreeNode {
        left: aLeft,
        right: aRight,
        value: aValue,
        sibling: None
    }
}

fn SetSibling(node: &mut Option<&TreeNode>) {
    match node {
        Some(mut n) => {
            match n.left {
                Some(mut c) => {
                    //c*.sibling = n.right;
                    match n.sibling {
                        Some(s) => { n.right.unwrap().sibling = s.left },
                        None => {}
                    }
                },
                None => {}
            }
        },
        None => return
    }
}

请问在Rust中,表示这类图节点的规范方式是什么?


解决方案

在Rust中处理这类带共享可变引用的图/树节点,最规范的方式是结合Rc(引用计数,实现共享所有权)和RefCell(内部可变性,允许在共享引用下修改内部字段)。你的初始尝试使用了普通引用,但普通引用受限于严格的生命周期规则,无法处理节点间的交叉引用,也不能在不可变引用下修改sibling字段。

核心思路

  1. Rc:让多个节点共享同一个节点的所有权,解决跨节点引用的所有权问题,每次克隆会增加引用计数,计数归零时自动释放节点。
  2. RefCell:提供内部可变性,允许在持有共享引用的情况下修改内部字段,其借用规则在运行时检查,而非编译时。

完整实现代码

use std::cell::RefCell;
use std::rc::Rc;

// 定义带共享所有权和内部可变性的树节点
#[derive(Debug)]
struct TreeNode {
    left: Option<Rc<RefCell<TreeNode>>>,
    right: Option<Rc<RefCell<TreeNode>>>,
    sibling: Option<Rc<RefCell<TreeNode>>>,
    value: String,
}

impl TreeNode {
    // 创建新节点的辅助函数
    fn new(value: String) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(TreeNode {
            left: None,
            right: None,
            sibling: None,
            value,
        }))
    }
}

fn set_sibling(node: Option<Rc<RefCell<TreeNode>>>) {
    if let Some(node_rc) = node {
        let mut node_ref = node_rc.borrow_mut();
        // 确保当前节点同时有左右子节点
        if let (Some(left_rc), Some(right_rc)) = (&node_ref.left, &node_ref.right) {
            // 左子节点的兄弟设为右子节点
            left_rc.borrow_mut().sibling = Some(right_rc.clone());
            
            // 如果当前节点有兄弟,右子节点的兄弟设为当前兄弟的左子节点
            if let Some(sibling_rc) = &node_ref.sibling {
                if let Some(sibling_left_rc) = &sibling_rc.borrow().left {
                    right_rc.borrow_mut().sibling = Some(sibling_left_rc.clone());
                }
            }
            
            // 递归处理左右子节点
            set_sibling(Some(left_rc.clone()));
            set_sibling(Some(right_rc.clone()));
        }
    }
}

// 测试示例
fn main() {
    // 构建完全平衡树
    let root = TreeNode::new("root".to_string());
    let left = TreeNode::new("left".to_string());
    let right = TreeNode::new("right".to_string());
    let left_left = TreeNode::new("left_left".to_string());
    let left_right = TreeNode::new("left_right".to_string());
    let right_left = TreeNode::new("right_left".to_string());
    let right_right = TreeNode::new("right_right".to_string());

    root.borrow_mut().left = Some(left.clone());
    root.borrow_mut().right = Some(right.clone());
    left.borrow_mut().left = Some(left_left.clone());
    left.borrow_mut().right = Some(left_right.clone());
    right.borrow_mut().left = Some(right_left.clone());
    right.borrow_mut().right = Some(right_right.clone());

    set_sibling(Some(root.clone()));

    // 验证结果
    assert_eq!(left.borrow().sibling.as_ref().unwrap().borrow().value, "right");
    assert_eq!(left_right.borrow().sibling.as_ref().unwrap().borrow().value, "right_left");
    println!("测试通过!");
}

补充说明

  • 如果场景不需要跨节点共享所有权(比如树是一次性构建且无交叉引用),可以使用Box<T>,但sibling这种需要跨节点引用的场景下,Box的独占所有权特性无法满足需求。
  • RefCell的运行时借用检查会在出现非法借用(比如同时持有多个可变引用)时触发 panic,编写代码时需注意避免此类情况。

内容的提问来源于stack exchange,提问作者Paperino

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 09:30:58