[JavaScript] 二叉搜索树中第K小的元素 Morris 中序遍历

力扣  230题 二叉搜索树中第K小的元素

给定一个二叉搜索树的根节点  root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。

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

       输出:3

 

这题一般使用中序遍历 栈与递归 的做法,但是空间复杂度会是 O(n),部分企业可能要求空间复杂度为 O(1) 的优化算法,这时应该使用 Morris 中序遍历。


栈:

// 栈
var kthSmallest = function(root, k) {
    const stack = [];
    while(stack.length || root != null) {
        while(root != null) {
            stack.push(root);
            root = root.left;
        }
        root = stack.pop();
        k--;
        if(k == 0) break;
        root = root.right;
    }
    return root.val;
};

 递归:

// 递归
var kthSmallest = function(root, k) {
    let res = -1;
    let now = 1;

    const Inorder = (root) => {
        if(root == null || res != -1) return;
        Inorder(root.left, now);
        if(now == k) res = root.val;
        now++;
        Inorder(root.right, now);
    }

    Inorder(root, 1);
    return res;
};

Morris 中序遍历:

Morris 遍历算法整体步骤如下(假设当前遍历到的节点为 x)【搬运自力扣】:

力扣icon-default.png?t=M85Bhttps://leetcode.cn/problems/binary-tree-inorder-traversal/

1. 如果 x 无左孩子,先将 x 的值加入答案数组,再访问 x 的右孩子,即 x = x.right。
2. 如果 x 有左孩子,则找到 x 左子树上最右的节点(即左子树中序遍历的最后一个节点,x 在中序遍历中的前驱节点),我们记为 predecessor。根据 predecessor 的右孩子是否为空,进行如下操作:
如果 predecessor 的右孩子为空,则将其右孩子指向 x,然后访问 x 的左孩子,即 x = x.left。
如果 predecessor 的右孩子不为空,则此时其右孩子指向 x,说明我们已经遍历完 x 的左子树,我们将 predecessor 的右孩子置空,将 x 的值加入答案数组,然后访问 x 的右孩子,即 x = x.right。
3. 重复上述操作,直至访问完整棵树。

// Morris 中序遍历
var kthSmallest = function(root, k) {
    // 设置前驱节点
    predecessor = null;
    while(root) {
        // 左子树不为空,遍历左子树
        if(root.left) {
            predecessor = root.left;
            while(predecessor.right && predecessor.right != root) {
                predecessor = predecessor.right;
            }
            // 不成环,将 predecessor 的右子树设为 root
            if(!predecessor.right) {
                predecessor.right = root;
                root = root.left;
            }
            // 成环,说明此时左子树已经遍历完毕,准备遍历右子树
            else {
                predecessor.right = null;
                k--;
                if(k == 0) break;
                root = root.right;
            }
        }
        // 左子树为空,说明此时 root.val 为最小值,准备遍历右子树
        else {
            k--;
            if(k == 0) break;
            root = root.right;
        }
    }
    return root.val;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值