Rust 如何正确地插入生锈的AVL树?
我对锈病很陌生,我正在尝试创建一棵AVL树。我使用Rc是因为我希望每个节点都由它上面的节点拥有,并且RefCell使它在内部是可变的 我已经开始构建“insert”方法,但它并没有在正确的位置插入新节点,而是用新节点替换“root”节点,我很难理解为什么。我相信这可能是所有权问题,但我不确定Rust 如何正确地插入生锈的AVL树?,rust,avl-tree,ownership,Rust,Avl Tree,Ownership,我对锈病很陌生,我正在尝试创建一棵AVL树。我使用Rc是因为我希望每个节点都由它上面的节点拥有,并且RefCell使它在内部是可变的 我已经开始构建“insert”方法,但它并没有在正确的位置插入新节点,而是用新节点替换“root”节点,我很难理解为什么。我相信这可能是所有权问题,但我不确定 use std::cell::RefCell; use std::cmp::Ordering; use std::rc::Rc; #[derive(Debug)] pub struct BaseNode
use std::cell::RefCell;
use std::cmp::Ordering;
use std::rc::Rc;
#[derive(Debug)]
pub struct BaseNode {
pub key: u32,
pub left: AvlTree,
pub right: AvlTree,
}
pub type AvlNode = Rc<RefCell<BaseNode>>;
pub type AvlTree = Option<AvlNode>;
impl BaseNode {
fn new(key: u32) -> Self {
Self {
key: key,
left: None,
right: None,
}
}
}
trait AvlNodeTrait {
fn new_node(key: u32) -> Self;
}
impl AvlNodeTrait for AvlNode {
fn new_node(key: u32) -> Self {
Rc::new(RefCell::new(BaseNode::new(key)))
}
}
pub trait AvlTreeTrait {
fn new() -> Self;
fn left(&self) -> Self;
fn right(&self) -> Self;
fn insert(&mut self, key: u32);
fn dupe(&self) -> Self;
fn set(&mut self, node: AvlNode);
}
impl AvlTreeTrait for AvlTree {
fn new() -> Self {
None
}
fn left(&self) -> Self {
if let Some(node) = self {
return node.borrow().right.dupe();
}
panic!("Trying to get Left of None!")
}
fn right(&self) -> Self {
if let Some(node) = self {
return node.borrow().right.dupe();
}
panic!("Trying to get right of None!")
}
fn dupe(&self) -> Self {
match self {
Some(node) => Some(Rc::clone(&node)),
None => None,
}
}
fn set(&mut self, node: AvlNode) {
*self = Some(Rc::clone(&node));
}
fn insert(&mut self, key: u32) {
let node = AvlNode::new_node(key);
let mut curr_tree = self;
let mut curr_key = 0;
while !curr_tree.is_none() {
if let Some(node) = &curr_tree {
curr_key = node.borrow().key;
if key > curr_key {
*curr_tree = curr_tree.left()
} else if key < curr_key {
*curr_tree = curr_tree.right()
} else {
return;
}
}
}
*curr_tree = Some(Rc::clone(&node));
}
}
fn main() {
let mut tree = AvlTree::new();
println!("{:?}", tree); // None
tree.insert(5);
println!("{:?}", tree); // Some(RefCell { value: BaseNode { key: 5, left: None, right: None } })
tree.insert(56);
println!("{:?}", tree); // Some(RefCell { value: BaseNode { key: 2, left: None, right: None } })
}
使用std::cell::RefCell;
使用std::cmp::Ordering;
使用std::rc::rc;
#[导出(调试)]
发布结构BaseNode{
发布键:u32,
酒吧左:AvlTree,
酒吧右:AvlTree,
}
pub类型AvlNode=Rc;
发布类型AvlTree=选项;
impl基节点{
fn新(键:u32)->自{
自我{
钥匙:钥匙,
左:没有,
右:没有,
}
}
}
性状{
fn新节点(键:u32)->Self;
}
为AvlNode插入AvlNodeTrait{
fn新节点(键:u32)->Self{
Rc::new(RefCell::new(BaseNode::new(key)))
}
}
pub trait AvlTreeTrait{
fn new()->Self;
fn左(&self)->self;
fn右(和自)->self;
fn插入(&mut self,键:u32);
fn dupe(&self)->self;
fn集合(&mut self,节点:AvlNode);
}
为AvlTree执行AvlTreeTrait{
fn new()->Self{
没有一个
}
fn左(&self)->self{
如果让某些(节点)=自身{
return node.borrow().right.dupe();
}
惊慌失措!(“试图什么都不留下!”
}
fn右侧(&self)->self{
如果让某些(节点)=自身{
return node.borrow().right.dupe();
}
惊慌失措!(“试图纠正任何错误!”)
}
fn重复(和自我)->自我{
匹配自我{
Some(node)=>Some(Rc::clone(&node)),
无=>无,
}
}
fn集合(&mut self,节点:AvlNode){
*self=Some(Rc::clone(&node));
}
fn插入(&mut self,键:u32){
let node=AvlNode::新建_节点(键);
让mut curr_tree=self;
让mut curr_key=0;
while!curr_tree.is_none(){
如果让某些(节点)=&curr\u树{
curr_key=node.borrow().key;
如果键>当前键{
*curr_tree=curr_tree.left()
}否则,如果键<当前键{
*curr_tree=curr_tree.right()
}否则{
返回;
}
}
}
*curr_tree=Some(Rc::clone(&node));
}
}
fn main(){
让mut-tree=AvlTree::new();
println!(“{:?}”,树);//无
树。插入(5);
println!(“{:?}”,tree);//Some(RefCell{value:BaseNode{key:5,左:无,右:无})
插入(56);
println!(“{:?}”,tree);//Some(RefCell{value:BaseNode{key:2,左:无,右:无})
}
我想说,在这种情况下,使用RefCell是完全不必要的,而且可能是不安全的。RefCell将所有权/借用检查交给运行时,而不是在编译时进行。这可能会导致您的程序在违反任何借用规则时出现恐慌
我更喜欢像这样的“递归”类型:
struct AVLTree<T> {
val: T,
left: Option<Box<AVLTree<T>>>,
right: Option<Box<AVLTree<T>>>
}
struct AVLTree{
瓦尔:T,
左:选项,
右:选项
}
这当然会由于内存分配而带来一些开销。想象一下,只运行一次代码。您有(
self:&mut…
),您有curr\u tree=self。你知道当你运行*curr\u tree=curr\u tree.left()
时结果是什么吗?你读过吗?在谈论生锈时,使用“不安全”时要小心,它有非常具体的含义RefCell
使用起来并不不安全,正如您所注意到的,它们可以动态检测借用问题——这是一项功能——因此会延迟问题的诊断(如果有),这并不安全。请原谅我回答中的错误措辞。我应该去别的地方,因为我的意思不是“不安全”。我想说的是,对我来说,如果其他方法不起作用,RefCell是最后的选择。因为它很容易错过失败的案例,然后整个程序就崩溃了。没问题,我也同意你的担忧,以及作为最后手段使用的想法,我只想“修正”答案,以确保不会误导未来的读者。