[路飞]_leetcode刷题_230. 二叉搜索树中第K小的元素

893 阅读3分钟

题目

230. 二叉搜索树中第K小的元素

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

 

示例 1:

image.png

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

示例 2:

image.png

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

思路1:

中序遍历刚好就是一个升序序列,那我们中序遍历,取第k个值即可。

这里用迭代法实现二叉搜索树的中序遍历。

代码如下:

/**
 * @param {TreeNode} root
 * @param {number} k
 * @return {number}
 */
var kthSmallest = function(root, k) {
    let stack = [];
    let res = [];
    while(k && (stack.length>0 || root)){
        while(root){
            stack.push(root);
            root = root.left;
        }
        root = stack.pop();
        res.push(root.val);
        k--;
        root = root.right;
    }
    return res[res.length-1]
};

复杂度分析

时间复杂度:O(H+k),其中 H是树的高度。在开始遍历之前,我们需要 O(H)到达叶结点。当树是平衡树时,时间复杂度取得最小值 O(log H + k);当树是线性树(树中每个结点都只有一个子结点或没有子结点)时,时间复杂度取得最大值 O(H+k)。

空间复杂度:O(H),栈中最多需要存储 H 个元素。当树是平衡树时,空间复杂度取得最小值 O(log N);当树是线性树时,空间复杂度取得最大值 O(N)。

思路2:

如果我们知道了当前节点root的左子树的节点数量,其实就可以做以下三种情况的判断:

  • 左子树的节点数left小于k-1,那么说明第k个树在右子树上,那么我们可以让root = root.right,并且k = k-(left + 1);
  • 左子树的节点数left等于k-1,那么说明第k个数正好是当前的root
  • 左子树的节点数left大于k-1,那么说明第k个数在左子树上,那么我们让root = root.left,继续向下搜索

这里,我们需要做几件事情

  1. 创建一个hash表,提前遍历整个数,然后将节点和他对应的子树的节点数量放入hash表
  2. 从根节点开始遍历整个二叉树,然后根据上面分析的判断,向下一直搜索,直到left = k-1

代码如下:

var kthSmallest = function(root, k) {
    const bst = new MyBst(root);
    return bst.kthSmallest(k);
};

class MyBst {
    constructor(root) {
        this.root = root;
        this.nodeNum = new Map();
        this.countNodeNum(root);
    }

    // 返回二叉搜索树中第k小的元素
    kthSmallest(k) {
        let node = this.root;
        while (node != null) {
            const left = this.getNodeNum(node.left);
            if (left < k - 1) {
                node = node.right;
                k -= left + 1;
            } else if (left === k - 1) {
                break;
            } else {
                node = node.left;
            }
        }
        return node.val;
    }

    // 统计以node为根结点的子树的结点数
    countNodeNum(node) {
        if (node == null) {
            return 0;
        }
        this.nodeNum.set(node, 1 + this.countNodeNum(node.left) + this.countNodeNum(node.right));
        return this.nodeNum.get(node);
    }

    // 获取以node为根结点的子树的结点数
    getNodeNum(node) {
        return this.nodeNum.get(node) || 0;
    }
}

复杂度分析:

时间复杂度:O(n),n为节点数,因为我们需要提前遍历整个二叉树,去做节点和节点的子节点数的映射。

空间复杂度:O(n),需要去将所有节点的映射都存起来。

思路3:

平衡二叉搜索树。这个老铁们去leetcode自行看题解吧