[路飞]_前端算法第五十五弹-二叉搜索树的第k大节点

227 阅读2分钟

给定一棵二叉搜索树,请找出其中第k大的节点。

示例 1:

输入: root = [3,1,4,null,2], k = 1
   3
  / \\
 1   4
  \\
   2
输出: 4

示例 2:

输入: root = [5,3,6,2,4,null,null,1], k = 3
       5
      / \\
     3   6
    / \\
   2   4
  /
 1
输出: 4

对于二叉搜索树,我们可以得知若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

中序遍历

由此性质我们可以将树形结构转换为相应的数组结构,用合适的遍历方法(中序遍历)便可以按从小到大的顺序数组排列,只需找到倒数第K个元素便是所查节点。

根据中序遍历为左,根,右的顺序,我们可以得出将中序遍历倒序遍历则可得到右,根,左的顺序遍历。

var kthLargest = function (root, k) {
    let res = [];
    dfs(root);
    return res[k-1]
    function dfs(root) {
        if (!root) return;
        dfs(root.right);
        res.push(root.val);
        dfs(root.left)
    }
};

二叉搜索树性质

根据二叉搜索树的性质,根节点右侧的结点都要大于根节点,而根节点左侧的结点都要小于根节点,我们假设根节点为第k大的值,那么根节点右子结点的所有子节点数cur_r一定为K-1个。所以,当k≤根节点的所有右子结点时,证明第k大的结点在根节点的右侧,而当k==根节点的所有右子结点的值cur_r+1时,这是便证明根节点为第k大的值,否则的话,第k大的值在根节点的左侧,此时的k=k-cur_r-1

var kthLargest = function (root, k) {
    function getCount(root) {
        if (!root) return 0;
        return getCount(root.left) + getCount(root.right) + 1
    }
    let cur_r = getCount(root.right)
    if (k <= cur_r) return kthLargest(root.right, k);
    if (k == cur_r + 1) return root.val;
    return kthLargest(root.left, k - cur_r - 1)
};