[C++&Rust]LeetCode No.783 二叉搜索树节点最小距离(每日一题)

大家好!今天给大家带来的是每日一题,是一道关于树的题目呢!
原帖地址:http://leanote.com/blog/post/6075795bab644142160032fa

题目

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。

思路分析

说到二叉树,特别是二叉搜索树,大家第一个想到的是什么呢?
当然是二分查找啦!二叉搜索数和二分查找,简直是天造地设的一对.因为他们是几乎一模一样的操作.大家想想二分查找的操作,如果target < [mid],我们就去左边找,因为左边的所有数字都小于[mid].而对应到二叉搜索树中,几乎是相同的操作,一个数左边的所有数都比它小,右边的所有数都比它大,所以对我们而言,二叉搜索树和排序过后的数组其实是同一个东西,只是存储方式略有不同.
既然是排过序的数组,又是要求最小差值,那非常简单,我们直接从左到右两两比较,直接就出结果了.唯一的问题就是如何在二叉搜索树中模拟数组的行为呢?结果当然是中序遍历.只有中序遍历可以将一个二叉搜索数还原成排过序的数组.我们当然可以新建一个数组,往里面塞数字,最后再比较.当然更好的办法是不占用额外空间,直接在树上遍历,因为其实遍历数组和遍历树没有什么本质上的区别.

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    void dfs(TreeNode* root, int& pre, int& ans) {
        if (root == nullptr) {
            return;
        }
        dfs(root->left, pre, ans);
        if (pre == -1) {
            pre = root->val;
        } else {
            ans = min(ans, root->val - pre);
            pre = root->val;
        }
        dfs(root->right, pre, ans);
    }
    int minDiffInBST(TreeNode* root) {
        int ans = INT_MAX, pre = -1;
        dfs(root, pre, ans);
        return ans;
    }
};

树是非常有意思的结构,其中三种遍历(前中后序遍历)方式,更是基础中的基础.
以下是三种遍历方式的示意图(伪代码)

func(TreeNode node) {
    /* 前序遍历:中前后 */
    func(node.left)
    /* 中序遍历:前中后 */
    func(node.right)
    /* 后序遍历:前后中 */
}

这样还是能比较直观的表现这三个名字的由来.
以下是rust代码

use std::rc::Rc;
use std::cell::RefCell;
pub fn dfs(root: Option>>, pre: &mut i32, res: &mut i32) {
    if let Some(rc_recell) = root.clone() {
        let mut node = rc_recell.borrow_mut();
        dfs(node.left.take(), pre, res);
        if *pre == -1 {
            *pre = node.val;
        } else {
            *res = (*res).min(node.val - *pre);
            *pre = node.val;
        }
        dfs(node.right.take(), pre, res);
    }
}
impl Solution {
    pub fn min_diff_in_bst(root: Option>>) -> i32 {
        let mut res = i32::MAX;
        let mut pre = -1;
        dfs(root, &mut pre, &mut res);
        res
    }
}

对于rust这种注重安全与所有权的语言来说,链表和树简直是噩梦(尤其是树),在C++中一个简单的结构体指针能表达的事情,在safe Rust中就是Option>>,太可怕了
其中Option就是一个枚举,表示一个东西是Some还是None(等价于C++中的nullptr但是并非指针,无安全隐患)
Rc是一个智能指针,主要是为了共享变量的,因为Rust中规定一个变量只能有一个拥有者,而Rc就使得可以存在多个拥有者,而且都是只读拥有者(即没有更改权限)
RefCell也是一个智能指针,主要是为了修改不可变的值(在这里就有了更改的权限)
在这么一整套流程下来,实在是太麻烦了,但是却能帮助我们对所有权机制有了更好的了解(以及C++所有权的混乱,正是这种混乱导致了大量内存泄漏问题).

你可能感兴趣的:(LeetCode题解)