LeetCode刷题记录----230.二叉搜索树中第K小的元素(Medium)

2025/8/9

题目:


我的思路:

我们之前已经知道了二叉搜索树的中序遍历访问的数字是升序的。而这里要我们求出第K小的元素值。而我们一开始又不知道这颗树有几个节点,因此没办法很快地通过二分查找的方式去得到。所以只能先按中序遍历的方式,去遍历到第K个元素即可

①用递归的方式去中序遍历

②每次访问到一个元素的时候让k = k-1

③如果k == 0,则说明这个就是目标元素,直接返回即可

④如果发现已经得到了结果值,那就不必再中序遍历右子树

具体代码如下:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
        #中序遍历查找
        
        self.res = -1
        self.k = k
        def midVisit(root:Optional[TreeNode]) -> int:
            if not root:
                return -1

            #查找左子树
            midVisit(root.left)
            #访问根节点
            self.k -= 1
            if self.k == 0:
                self.res = root.val
            #访问右子树(如果还没有找到结果的话)
            if self.res == -1:
                midVisit(root.right)

        midVisit(root)
        return self.res

时间复杂度:O(N)

空间复杂度:O(N)


优化思路:

1.二叉搜索树

如果我们需要更加频繁地去查找第K个元素,上述这种方法就会不那么方便。而我们之所以上面要这样逐个遍历每个节点主要是因为我们不知道这个树的节点数。因此,我们可以先对这颗树进行预处理,记录它每个节点作为根节点的子树的节点数。比如这个值为4的节点作为根节点的子树的节点数是3

对于这些节点对应的节点数,我们可以把它存在一个哈希表中以快速获取。

那我们的算法就可以改进成:

①获取当前节点的左子树的节点数left

②如果left == k-1 说明当前这个节点的值就是目标值

③如果left > k-1 说明目标值在左子树中;此时需要把节点指针往左子树移动

④如果left < k-1 说明目标值在右子树中;此时需要把节点指针往右子树移动,同时更新k的值(k = k - left - 1【因为已经排除了左子树和当前节点是目标值的可能】)

⑤一直循环步骤二三四直到返回了为止

其中对这颗树进行预处理主要是处理获得其哈希表:

①当前节点作为根节点的子树的节点数 = 1 + 其左子树节点数 + 其右子树节点数

可以看出来是可以进行递归计算处理的 

具体代码如下:

#二叉搜索树
class MyBst:
    def __init__(self, root:TreeNode):
        #预处理这颗树,获取每个节点作为根节点的子树的节点树(哈希表存储)
        self.root = root
        self.node_num = {}

        self.count_node_num(root)

    def count_node_num(self, root:TreeNode) -> int:
        #计算当前这颗树的节点数量
        if not root:
            return 0
        
        self.node_num[root] = 1 + self.count_node_num(root.left) + self.count_node_num(root.right)
        return self.node_num[root]

    def get_node_num(self, root:TreeNode) -> int:
        #获取这颗树的节点数
        return self.node_num[root] if root else 0


    def get_minKNum(self, k) -> int:
        #获取当前这颗树第K小的节点值
        node = self.root
        while True:
            left = self.get_node_num(node.left)
            if left == k-1:
                return node.val
            elif left > k-1:
                #说明值在左子树中
                node = node.left
            else:
                #说明值在右子树中
                node = node.right
                k -= left+1     #更新k的值(减去左子树的节点数量+根节点)

    def print_nodeNum(self):
        #打印查看每个节点作为根节点的子树的节点数量
        for k in self.node_num:
            print(k.val, self.node_num[k])

class Solution:
    def kthSmallest(self, root: TreeNode, k: int) -> int:
        #实例化这个二叉搜索树
        bst = MyBst(root)

        #然后调用里面的方法
        return bst.get_minKNum(k)

时间复杂度:O(N) 预处理 + O(H) 查询

空间复杂度:O(N)

这里也算是用到了面向对象的思想来处理解决


其他:

那如果这颗树会被频繁修改且需要频繁访问这个第K小的元素的时候又该怎么办呢?

这个问题我们用平衡二叉搜索树解决,但是这个看起来有点复杂,我还没有细看的。只知道它有这么个用处。具体去LeetCode中查看官方题解

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


总结:

①这里我们又用到了中序遍历是搜索二叉树的升序排序的结论来做题

②如果要频繁访问,那就要预处理这个搜索二叉树(知道每个节点作为根节点的子树的节点数)

③如果要对这颗树会进行增删改查的时候,那就需要用到平衡二叉搜索树

④Python中对字典的迭代器访问是访问其Key值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萘柰奈

谢谢老板喵

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值