【问题标题】:Implement the recursive insert method of binary search tree in RUST在RUST中实现二叉搜索树的递归插入方法
【发布时间】:2023-01-13 10:57:02
【问题描述】:

我正在学习 Rust 并尝试实现一个简单的二叉搜索树(实际上它正在重写下面的 Java 实现)。这是我所做的:

use std::cmp::Ordering;

// Node of this BST, the two generic types are key and value
struct Node<K:Ord, V> {
    key: K,
    value: V,
    left: Option<Box<Node<K, V>>>,
    right: Option<Box<Node<K, V>>>,
    number_of_nodes: i32,
}

impl<K: Ord, V> Node<K, V> {
    // Create a new node
    fn new(key: K, value: V, number_of_nodes: i32) -> Node<K, V>{
        Node {
            key,
            value,
            left: None,
            right: None,
            number_of_nodes,
        }
    }
}

struct BST<K: Ord ,V> {
    root: Option<Box<Node<K, V>>>,
}

impl<K: Ord, V> BST<K, V> {
    // Get the size of this BST
    fn size(&self) -> i32 {
        size(&self.root)
    }

    // Search for key. Update value if found, otherwise insert the new node
    fn put(&self, key: K, value: V) {
        self.root = put(&self.root, key, value)
    }
}

// Function for recursively get the size of a sub BST 
fn size<K: Ord, V>(node: &Option<Box<Node<K, V>>>) -> i32 {
    match node {
        Some(real_node) => real_node.number_of_nodes,
        None => 0,
    }
}

// Function for recursively put a new node to this BST
fn put<K: Ord, V>(node: &Option<Box<Node<K, V>>>, key: K, value: V) -> &Option<Box<Node<K, V>>>{
    match node {
        None => {
            let new_node = Some(Box::new(Node::new(key, value, 1)));
            return &new_node;
        },
        Some(real_node) => {
            match key.cmp(&real_node.key) {
                Ordering::Less => real_node.left = *put(&real_node.left, key, value),
                Ordering::Greater => real_node.right = *put(&real_node.right, key, value), 
                Ordering::Equal => real_node.value = value,
            }
            real_node.number_of_nodes = size(&real_node.right) + size(&real_node.left) + 1;
            node
        },
    }
}

但是这段代码无法编译,在 self.root = put(&amp;self.root, key, value) 行,我得到一个错误:

不匹配的类型
找到预期的枚举“Option<Box<Node<K, V>>>” 参考 '&Option<Box<Node<K, V>>>'

我不知道如何解决这个问题,我尝试将 &amp;self 参数更改为 self,或将 self.root 更改为 *self.root,但我遇到了更多错误。我对 Rust 中的引用感到很困惑,我只想用 Rust 重写以下 Java 代码。

public class BST<Key extends Comparable<Key>, Value>
{
    private Node root;              //root of BST

    private class Node
    {
        private Key key;            // key
        private Value val;          // associated value
        private Node right, left;   // left and right subtrees
        private int N;              // number of nodes in subtree

        public Node(Key key, Value val, int N)
        {
            this.key = key;
            this.val = val;
            this.N = N;
        }
    }

    // Returns the number of key-value pairs in this symbol table.
    public int size()
    {
        return size(root);
    }

    // Return number of key-value pairs in BST rooted at x
    private int size(Node x)
    {
        if (x == null) return 0;
        else return x.N;
    }

    public void put(Key key, Value val)
    {
        root = put(root, key, val);
    }

    private Node put(Node x, Key key, Value val)
    {
        if (x == null) return new Node(key, val, 1);
        int cmp = key.compareTo(x.key);
        if (cmp < 0) x.left = put(x.left, key, val);
        else if (cmp > 0) x.right = put(x.right, key, val);
        else x.val = val;
        x.N = size(x.left) + size(x.right) + 1;
        return x;
    }
} 

Java 中非常简单,因为我不需要处理引用。所以这是我的问题:

  1. 我该如何修复不匹配的错误?
  2. 递归函数put的正确返回类型是什么, &amp;Option&lt;Box&lt;Node&lt;K, V&gt;&gt;&gt;Option&lt;Box&lt;Node&lt;K, V&gt;&gt;&gt;?有什么不同?
  3. 我重写Java代码的方法是否正确? rust-analyzer 只报告了这个不匹配的错误,但我不知道它是否会像我预期的那样工作。老实说我不 完全理解当我处理 Rust 中的引用时我在做什么 特别是当它是 struct 或 enum 的引用时

    学习 Rust 很难,因为我在系统编程语言方面没有太多经验,感谢你们的帮助 :)

【问题讨论】:

    标签: java algorithm rust binary-search-tree


    【解决方案1】:

    最简单的选择是对节点进行可变引用:

    impl<K: Ord, V> BST<K, V> {
        // ...
    
        fn put(&mut self, key: K, value: V) {
            put(&mut self.root, key, value)
        }
    }
    
    fn put<K: Ord, V>(node: &mut Option<Box<Node<K, V>>>, key: K, value: V) {
        match node {
            None => {
                *node = Some(Box::new(Node::new(key, value, 1)));
            }
            Some(real_node) => {
                if key < real_node.key {
                    put(&mut real_node.left, key, value);
                } else {
                    put(&mut real_node.right, key, value);
                }
    
                real_node.number_of_nodes = size(&real_node.right) + size(&real_node.left) + 1;
            }
        }
    }
    

    或者,您可以按值获取节点并返回修改后的节点:

    impl<K: Ord, V> BST<K, V> {
        // ...
    
        pub fn put(&mut self, key: K, value: V) {
            self.root = put(self.root.take(), key, value);
        }
    }
    
    fn put<K: Ord, V>(node: Option<Box<Node<K, V>>>, key: K, value: V) -> Option<Box<Node<K, V>>> {
        match node {
            None => Some(Box::new(Node::new(key, value, 1))),
            Some(mut real_node) => {
                if key < real_node.key {
                    real_node.left = put(real_node.left, key, value);
                } else {
                    real_node.right = put(real_node.right, key, value);
                }
    
                real_node.number_of_nodes = size(&real_node.right) + size(&real_node.left) + 1;
                Some(real_node)
            }
        }
    }
    

    如果您还没有阅读 Rust 书的 ownership and references chapter,我建议您阅读。

    【讨论】:

    • 谢了哥们。可变引用方法非常清楚。但是如果我们插入一个已经存在的新键,节点的数量不会增加。当我们放置一个已存在的节点时,我们会更新该键的值。我实现了 get 方法并测试了几次,这个 BST 的大小应该没问题
    • 在您的第二个代码块中,递归方法put 的第一个参数是Option&lt;Box&lt;Node&lt;K, V&gt;&gt;&gt;self.root 也是Option&lt;Box&lt;Node&lt;K, V&gt;&gt;&gt;。但是我们不能直接将self.root 放入该函数中。这就是为什么我使用引用而不是让该函数取得所有权的原因。这有点奇怪,因为我们可以在其他数据类型(如String)中执行此操作,但在这种情况下,我们需要使用take() 函数。我想这就是让我感到困惑的事情,无论如何谢谢男人
    猜你喜欢
    • 1970-01-01
    • 2011-12-15
    • 2021-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-16
    相关资源
    最近更新 更多