前端刷题路-Day36:二叉搜索树节点最小距离(题号783)

546 阅读3分钟

二叉搜索树节点最小距离(题号783)

题目

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

注意:本题与 530:leetcode-cn.com/problems/mi… 相同

示例 1:

img

输入:root = [4,2,6,1,3]
输出:1

示例 2:

img

输入:root = [1,0,48,null,null,12,49]
输出:1

提示:

  • 树中节点数目在范围[2, 100]
  • 0 <= Node.val <= 105

链接

leetcode-cn.com/problems/mi…

解释

这题啊,这题是根本没想道点子上。

首先看题目,是一个二叉搜索树,只要我们拿到二叉搜索树所有节点的值,之后进行排序,最后循环数组,取其相邻的两个数的差,即可得出最小值。

重点就是在怎么取所有节点的值,笔者想的很简单,不管用什么遍历方法,拿到所有节点值之后排序一下就行了,其实不然。

由于是二叉搜索树,那么只要进行中序遍历的话,拿到的结果就是按照从小到大的顺序进行排列的,此时在进行差值比较即可。

原来这题考的是中序遍历,那说到中序遍历,这题就简单了。

自己的答案(暴力解法)

var minDiffInBST = function(root) {
  var arr = [root]
      nums = []
      min = Number.MAX_SAFE_INTEGER
  while (arr.length) {
    var item = arr.shift()
    nums.push(item.val)
    item.left && arr.push(item.left)
    item.right && arr.push(item.right)
  }
  nums.sort((a, b) => a- b)
  for (let i = nums.length - 1; i > 0; i--) {
    min = Math.min(nums[i] - nums[i - 1], min)    
  }
  return min
};

这里随便遍历了一下,拿到了所有二叉树的值,之后排序,排序之后再循环就可以拿到最小差值了。

完全没有想到重点。

更好的方法(中序-遍历)

既然是中序遍历,首先说说遍历方法👇:

var minDiffInBST = function(root) {
  var stack = []
      prv = Number.MIN_SAFE_INTEGER
      min = Number.MAX_SAFE_INTEGER
  while (root || stack.length) {
    while (root) {
      stack.push(root)
      root = root.left
    }
    root = stack.pop()
    min = Math.min(root.val - prv, min)
    prv = root.val
    root = root.right
  }
  return min
};

遍历的代码十分简单,首先需要有个stack来存储root数组,之后开始进行遍历操作。

首先开始外层遍历,在外层遍历中,第一步要做的就是内层遍历。

如果root存在,那么对root进行解析操作。在内层遍历中,首先将root推入stack中,之后取到root的左子树,然后再将root的左子树推入到到stack中,以此类推,直到rootnull

root处理完成后即可开始外层循环的真正内容,先将root赋值为stack的最后一个元素,此时rootval就是遍历中应该取的值,所以在此时进行比较和赋值。

完事了再将root赋值为它的右子树,完成一轮遍历。

然后就完事了,这样遍历的结果就是中序遍历了,相对比较简单,如果不理解背下来就好,代码量也不多。

更好的方法(中序-递归)

递归的方法比遍历更容易理解,而且感觉上更简单一些👇:

var minDiffInBST = function(root) {
  var prv = Number.MIN_SAFE_INTEGER
      min = Number.MAX_SAFE_INTEGER
  function inorder(root) {
    if (!root) return
    inorder(root.left)
    min = Math.min(root.val - prv, min)
    prv = root.val
    inorder(root.right)
  }
  inorder(root)
  return min
};

prvmin就不说了,针对这题的特殊变量。

递归的中序遍历首先是一个函数,这里叫inorder。函数内部首先判断root是否存在,如果不存在,直接返回;如果存在,先执行对其左子树进行递归操作——inorder(root.left),之后对rootval进行处理和比较;完事了再进行root右子树的递归操作,就好了。

其逻辑真的十分简单,对每个节点都进行递归操作,如果有左子树先执行左子树,之后执行当前节点,最后执行右子树,简直不要太简单。

更好的方法(中序-Morris)

这个方法就有点绕了,笔者是真的看不懂,后来在同事的解释下才勉强理解,前前后后花了快4个小时,这里就放下官方的答案吧,后续会单独出一篇文章来讲解Morris遍历的。

官方地址:leetcode-cn.com/problems/bi…



PS:想查看往期文章和题目可以点击下面的链接:

这里是按照日期分类的👇

前端刷题路-目录(日期分类)

经过有些朋友的提醒,感觉也应该按照题型分类
这里是按照题型分类的👇

前端刷题路-目录(题型分类)

有兴趣的也可以看看我的个人主页👇

Here is RZ