剑指Offer:二叉搜索树的第k个节点

描述
给定一棵结点数为n 二叉搜索树,请找出其中的第 k 小的TreeNode结点值。
1.返回第k小的节点值即可
2.不能查找的情况,如二叉树为空,则返回-1,或者k大于n等等,也返回-1
3.保证n个节点的值不一样

数据范围: 0 \le n \le10000≤n≤1000,0 \le k \le10000≤k≤1000,树上每个结点的值满足0 \le val \le 10000≤val≤1000
进阶:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)

如输入{5,3,7,2,4,6,8},3时,二叉树{5,3,7,2,4,6,8}如下图所示:
在这里插入图片描述
该二叉树所有节点按结点值升序排列后可得[2,3,4,5,6,7,8],所以第3个结点的结点值为4,故返回对应结点值为4的结点即可。

我的思路

返回第k小的,那可不就是从最左节点中序遍历到的第k个数吗?这个简单,我可有思路。
很好,也错了,我就说,怎么一个中等题这么简单

int ans;
    public int KthNode (TreeNode proot, int k) {
        // write code here
        //不能查找的情况,如二叉树为空,则返回-1,或者k大于n等等,也返回-1
        int i = 0;
        if (proot == null)
            return -1;
        travel(proot,i,k);
        if (ans == 0)
            return -1;
        return ans;
    }

    private void travel(TreeNode root,int i,int k) {
        if (root == null)
            return;
        //左
        travel(root.left,i + 1,k);
        //中
        if (i == k)
            ans = root.val;
        //右
        travel(root.right,i + 1,k);
    }

在这里插入图片描述
这个错我就懒得看,反正整个逻辑就是错的,这个i操作应该是从遍历到最左节点开始计数的。而不是从头结点遍历下去这样计数的。所以做了第二次修改:

int ans;
    public int KthNode (TreeNode proot, int k) {
        // write code here
        //不能查找的情况,如二叉树为空,则返回-1,或者k大于n等等,也返回-1
        int i = 1;//因为方法传过去就是最左节点了,所以初始化的值直接为1
        if (proot == null)
            return -1;
        LeftMost(proot);
        travel(leftNode,i,k);
        if (ans == 0)
            return -1;
        return ans;
    }

    private void travel(TreeNode root,int k,int i) {
        if (root == null)
            return;
        //左
        travel(root.left,k,i + 1);
        //这个是不能这样计数的,得遍历到最左节点才是第一个数
        //中
        /*if (root.left == null)//希望在第一次遍历到最左端的时候执行这行代码
            i = 1;*/
        //这样肯定是不行的,得换一种思路
        if (i == k)
            ans = root.val;
        //右
        travel(root.right,k,i + 1);
    }

    //如何返回最左节点呢
    TreeNode leftNode;
    private void LeftMost(TreeNode root){
        if (root.left == null) {
            leftNode = root;
            return;
        }
        LeftMost(root.left);
    }

但这次代码的问题在于取到的最左节点是空的,左右指针都没有指向,再放入递归遍历的循环没办法回溯和向后取值了。所以这种写法也是错的。写的有点烦,用了一个比较暴力的方法先解出来了,等等看看别人解决的思路。

int ans;
    List<Integer> list = new LinkedList<>();
    public int KthNode (TreeNode proot, int k) {
        if (k <= 0)
            return -1;
        // write code here
        //不能查找的情况,如二叉树为空,则返回-1,或者k大于n等等,也返回-1
        int i = 1;//因为方法传过去就是最左节点了,所以初始化的值直接为1
        if (proot == null)
            return -1;
//        LeftMost(proot);
        travel(proot);
        for (int c: list){
            if (i == k)
                ans = c;
            i++;
        }
        if (k >= i)
            return -1;
        return ans;
    }

    private void travel(TreeNode root) {
        if (root == null)
            return;
        //左
        travel(root.left);
        //这个是不能这样计数的,得遍历到最左节点才是第一个数
        //中
        /*if (root.left == null)//希望在第一次遍历到最左端的时候执行这行代码
            i = 1;*/
        //这样肯定是不行的,得换一种思路
        list.add(root.val);
        //右
        travel(root.right);
    }

看了一下别人的题解,都快忘记了优先队列了,不过要注意优先队列的默认大根堆,如果我们要使用小根堆,就得重写比较器。具体的步骤和思想在注释里了。

public int KthNode (TreeNode proot, int k) {
        if (k <= 0)
            return -1;
        if (proot == null)
            return -1;
       //这里用另一种方法,我居然都忘记了
        PriorityQueue<Integer> pq = new PriorityQueue<>((a,b)->b - a);
        //PriorityQueue<>((a, b) -> b - a)表示优先级安装最大优先,默认是最小优先(小根堆)
        //在这里设置一个大根堆,如果有数传进来了就会按照大小排列,到时候要取第k个数直接弹出这个数就好,
        // 第k小也是第k大(前k个元素中最大的),到时候直接返回堆顶就好
        //看了别人的题解,发现写法会更好一些,学习了
        /*
        * 他的思想是使用K个元素的,用另外一个队列来承接节点,正常遍历,如果这个数值是前k个就放入
        * 如果这个数小于大根堆堆顶就放进去,把栈顶弹出。以此类推,最后弹出堆顶就是我们要取得数了
        * */
        int i = 1;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(proot);//先把第一个节点放进去
        while (!queue.isEmpty()){
            TreeNode root = queue.poll();
            if (pq.size() < k)
                pq.add(root.val);
            else if (root.val < pq.peek()){
                pq.poll();
                pq.add(root.val);
            }
            if (root.left != null) {
                queue.add(root.left);
                i++;
            }
            if (root.right != null) {
                queue.add(root.right);
                i++;
            }
        }
        if (k > i)
            return -1;
        return pq.peek();
    }

还有最后一种方法,就是我最开始想用但是没能成功的方法:利用搜索二叉树的性质,用中序遍历拿出这个数。没能实现,主要是有个边界条件很烦,不想看了,我好笨,有空再看看吧。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值