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

  • 剑指Offer54.二叉搜索树的第k大节点
  • 题目:
    输入一个二叉搜索树,返回它的第k大节点值;
    1 <= k <= 二叉搜索树的节点数(即不用担心树为空,也不用担心不存在第k大的情况);
  • 思路:
    看到二叉搜索树,首先想到中序遍历是从小到大的;
    本题找第k大,即中序遍历序列的倒数第k个,有两个思路:①正常按中序遍历去找,找到第n-k+1个就是倒数第k个,但需要提前扫一遍,确定树的节点数n,扫第二次找到正数第n-k+1个 ②按照中序的逆序“右中左”遍历,这样得到的序列是从大到小的,遍历到第k个就是目标节点;这样只需要扫一遍即可;
    “右中左”的大体执行思路是:先往右走,然后在递归回退的时候执行“中”if (–k == 0) ans = root->val,每处理一个节点就–k;

1.逆序中序遍历:时间O(n):当二叉搜索树退化为只有右孩子的单链表时,无论k为几,递归深度都为n层(因为右中左的遍历顺序需要先走到右下角才回退,即使找到第k大节点,依然要逐层回退到根节点),空间O(n):同理,最坏情况下需要n层系统栈;
每处理一个节点,就–k,直到k==0,说明当前节点是第k大的,此时用全局变量保存下来;
找到第k大节点后,需要尽快返回,不要继续执行递归;

//具体实现技巧:
//写法1.一开始把k设置为全局变量,这样在“右中左”的处理中节点时,每处理一个就--k,当--k==0时就是目标节点;
class Solution {
    
    
public:
    int ans, k;
    void travel(TreeNode* root) {
    
    
        if (!root) return;

        travel(root->right);
        if (--k == 0) ans = root->val;//在找到第k大节点时,k=0;之后每次--k会使k越来越小,为了尽快返回就要避免执行travel(root->left)
        if (k > 0) travel(root->left);
    }
    int kthLargest(TreeNode* root, int k) {
    
    
        this->k = k;
        travel(root);

        return ans;
    }
};

//写法2:k作为参数传递,但传值的话,本层递归函数--k,并不会影响上一层的k,因此,在处理第k大节点时,由于之前处理的影响没有施加到当前层,此时--k不会==0,而k传引用的话,相当于所有递归函数的k是共用一个,按照处理“中”的顺序,一旦执行了--k,所有递归函数中的k都减1,因此一定会在第k次--k==0
//可以以一个只有右孩子的单链表思考这个过程,这样相当于不用执行travel(root->left),一开始我们在地面,每递归一次就下一层,逐渐递归到底,此时在链表尾,之后随着逐层回退执行“中”--k,若在负i层--k后,k=2,那么回退到负i-1层时,此时的k仍然=3,也就是负i层--k并不会影响负i-1层的k
class Solution {
    
    
public:
    int ans;
    void travel(TreeNode* root, int& k) {
    
    
        if (!root) return;

        travel(root->right, k);
        if (--k == 0) ans = root->val;
        if (k > 0) travel(root->left, k);
    }
    int kthLargest(TreeNode* root, int k) {
    
    
        travel(root, k);

        return ans;
    }
};

猜你喜欢

转载自blog.csdn.net/jiuri1005/article/details/114363910