剑指 Offer 54. 二叉搜索树的第k大节点

出处

出镜率很高的一道题 https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof/

77在这里插入图片描述

统计右子树结点

首先要读清楚题目,搞清楚第k大到底是指什么
然后就可以开始做了

首先输入是一颗二叉搜索树,满足左小右大的定义,输入数据k不会越界
观察规律发现,如果k=1并且当前节点没有右孩子时,当前根节点就是要找的结点 —— 边界情况
继续观察,设lnum为根节点左子树结点个数,rnum为根节点右子树结点个数,那么当k-rnum==1时,当前结点就是要找的结点 —— 一般情况
根据一般情况推导,我们发现k-rnum还有另外两种可能

k - rnum < 1 —— 意味着我们要往右子树寻找
k - rnum > 1 —— 意味着我们要往左子树寻找

回到实际意义
k - rnum小于1的时候,表示右子树结点个数足够多,我们要找的结点存在于右子树
k - rnum大于1的时候,表示右子树结点个数不够多,我们要找的结点存在于左子树,例如k=8,rnum=5的时候,明显此时右子树没有符合的结点,应该往左子树走

目前为止我们有了边界情况、一般情况下的三种状态,现在我们还需要完善另外两种状态的处理

  1. 当我们往右子树走的时候,k不需要改变,因为右子树此时必然存在目标结点,并且在递归过程中总会满足 k-rnum==1 这个状态,所以能找到目标结点
  2. 当我们往左子树走的时候,k需要改变大小,因为我们往左子树走的过程中,相当于构造一颗新树,这颗新树是当前树舍去根结点和右子树形成的,此时我们相当于递归找第k-rnum-1个结点

分析完了,我们发现解题并不需要用到lnum
下面是伪代码

void func(Tree T,int k){
	// 1 边界情况
	如果k==1并且T->right==null,返回T->val
	否则		// 2 一般情况
	获得右子树的结点数rnum
	val = k - rnum
	if val == 1:
		返回T->val	// 2.1
	else if val>1:
		return func(T->left,val-1);	// 2.2
	else:
		return func(T->right,k);	// 2.3
}

写出伪码后,具体实现就很简单了

class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:
        # 边界情况
        if k==1 and not root.right:
            return root.val
        # lnum = self.getNum(root.left)
        rnum = self.getNum(root.right)
        val = k-rnum
        if val == 1:
            return root.val
        elif val > 1:
            return self.kthLargest(root.left,val-1)
        else:
            return self.kthLargest(root.right,k) 
         
    def getNum(self,root):
        if not root:
            return 0 
        if not root.left and not root.right:
            return 1
        return self.getNum(root.left)+self.getNum(root.right)+1

在这里插入图片描述
以上就是这道题的一种做法,也是比较容易想到的解法

中序逆序遍历

接下来介绍官方题解的做法:
思想很巧妙,大意就是,二叉搜索树按照左-中-右的顺序遍历时,得到的结果是升序序列,按照右-中-左的顺序遍历时,得到的结果是降序序列,那么寻找第k大个数,相当于寻找降序序列中第k个结点
我们按照右-中-左的顺序遍历,遍历过程中,如果k==0,则表示当前节点就是答案,否则k-=1,继续递归

class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:
        def dfs(root):
            if not root: 
            	return
            dfs(root.right)
            if self.k == 0: 
            	return
            self.k -= 1
            if self.k == 0: 
            	self.res = root.val
            dfs(root.left)

        self.k = k
        dfs(root)
        return self.res

在这里插入图片描述

扩展

事实上这样右中左的变换,在迭代遍历二叉树的时候也有应用,例如我们可以用栈来实现先序遍历的迭代做法,使用栈来实现后序遍历的迭代做法,具体可以看之前的文章
https://blog.csdn.net/hhmy77/article/details/103841102

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值