如何在Rust中为二叉树编写删除功能?

问题描述

我试图学习一些Rust,并从实现二叉树开始。 尽管插入和按顺序遍历似乎很容易,但是在delete方法中使用借位检查器时遇到了一些麻烦。

到目前为止的代码是:

use std::cmp::{Ordering,PartialOrd};
use std::fmt::display;

struct Node<T> {
    value: T,left: Option<Box<Node<T>>>,right: Option<Box<Node<T>>>,}

impl<T: display + PartialOrd> Node<T> {
    fn new(val: T) -> Self {
        Node {
            value: val,left: None,right: None,}
    }

    fn print_inorder(&self) {
        print!("[");
        self.inorder(|x| print!("{},",x));
        print!("]\n")
    }

    fn inorder(&self,f: fn(&T)) {
        if let Some(ref x) = self.left {
            (*x).inorder(f);
        }
        f(&self.value);
        if let Some(ref x) = self.right {
            (*x).inorder(f);
        }
    }

    fn insert(&mut self,val: T) {
        let mut node = self;

        loop {
            if val == node.value {
                return;
            }

            let child = match val.partial_cmp(&node.value).expect("Key comparison Failed") {
                Ordering::Less => &mut node.left,Ordering::Equal => return,Ordering::Greater => &mut node.right,};

            match child {
                Some(ref mut c) => node = c,None => { *child = Some(Box::new(Node::new(val))); return}
            }
        }
    }

    fn delete(&mut self,val: T) -> bool {
        if self.value == val {
            unimplemented!{"Cannot remove root (yet) :/"};
        }

        let mut node = self;

        loop {
            if val < node.value {
                if let Some(ref mut c) = node.left {

                    if c.value == val {
                        if c.left.is_none() && c.right.is_none() {
                            // Error: cannot assign to node.left here
                            node.left = None;
                        }
                        else if c.left.is_some() && c.right.is_none() {
                            // Error: again cannot assign to node.left but cannot even access c.left
                            node.left = c.left;
                        }
                        else if c.left.is_none() && c.right.is_some() {
                            node.left = c.right;
                        }
                        else {
                            // Todo: walk through child tree and get rightmost element
                        }
                        return true;
                    } else {
                        node = c;
                    }
                }
                else {
                    return false;
                }
            }
        }
    }

}

struct Tree<T> {
    root: Option<Node<T>>
}

impl<T: PartialOrd + display> Tree<T> {

    fn new() -> Self {
        Tree{root: None}
    }

    fn insert(&mut self,val: T) {
        match self.root {
            Some(ref mut n) => n.insert(val),None => self.root = Some(Node::new(val))
        }
    }

    fn print(&self) {
        match self.root {
            Some(ref n) => n.print_inorder(),None => println!("[]")
        }
    }
}

fn main() {
    println!("Hello,world!");

    let mut t = Tree::<i64>::new();
    t.print();
    t.insert(14);
    t.print();
    t.insert(7);
    t.insert(21);
    t.print();
}

因此,在删除功能中,我无法分配给我向下遍历的节点。但是要删除一个节点,我必须为其父节点分配一个不同的值。

我找到了https://codereview.stackexchange.com/questions/133209/binary-tree-implementation-in-rust?rq=1,但是那个人不适合我编译(再次出现借位检查器问题)。

解决方法

作为起点,我们可以使用递归解决方案来规避迭代解决方案所面临的许多借用问题。

为了能够删除根节点,关键思想是要求delete函数返回根于该节点的 new 子树;为了使事物协调一致,必须对Tree进行少量修改:

struct Tree<T> {
    root: Option<Box<Node<T>>>
}

我添加了Box层,因为Node包含Option<Box<Node<T>>>;这使我们可以像处理另一个子节点一样处理根。

然后,我编写递归删除代码:

    fn delete(mut this: Box<Node<T>>,target: &T) -> Option<Box<Node<T>>> {
        if target < &this.value {
            if let Some(left) = this.left.take() {
                this.left = Self::delete(left,target);
            }
            return Some(this);
        }

        if target > &this.value {
            if let Some(right) = this.right.take() {
                this.right = Self::delete(right,target);
            }
            return Some(this);
        }

        assert!(target == &this.value,"Faulty Ord implementation for T");

        match (this.left.take(),this.right.take()) {
            (None,None) => None,(Some(left),None) => Some(left),(None,Some(right)) => Some(right),(Some(mut left),Some(right)) => {
                if let Some(mut rightmost) = left.rightmost_child() {
                    rightmost.left = Some(left);
                    rightmost.right = Some(right);
                    Some(rightmost)
                } else {
                    left.right = Some(right);
                    Some(left)
                }
            },}
    }

    //  Returns the rightmost child,unless the node itself is that child.
    fn rightmost_child(&mut self) -> Option<Box<Node<T>>> {
        match self.right {
            Some(ref mut right) =>  {
                if let Some(t) = right.rightmost_child() {
                    Some(t)
                } else {
                    let mut r = self.right.take();
                    if let Some(ref mut r) = r {
                        self.right = std::mem::replace(&mut r.left,None);
                    }
                    r
                }
            },None => None
        }
    }

通过以下方式从Tree进行呼叫:

    fn delete(&mut self,target: &T) {
        if let Some(root) = self.root.take() {
            self.root = Node::delete(root,target);
        }
    }

完整的代码可在运动场上找到:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=760b9e3d68c9cda33ef463248a7eba25

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...