【牛客 - 剑指offer】JZ54 二叉搜索树的第k个节点 递归版+非递归版 Java实现


剑指offer题解汇总 Java实现

https://blog.csdn.net/guliguliguliguli/article/details/126089434

本题链接

知识分类篇 - 树 - JZ54 二叉搜索树的第k个节点

题目

在这里插入图片描述

主要信息

  • 给定一棵节点数为n的二叉搜索树,需要其中的第k小的节点值
  • 返回第k小节点值即可
  • 不能查找的情况,如二叉树为空,则返回-1,或者k大于n等等,也返回-1
  • 保证n个节点的值不一样

方案一 中序遍历

二叉搜索树,采用中序遍历的方式可以将二叉树中的数字按照从小到大的顺序依次打印出来,用一个变量cnt记录当前遍历到了第几个节点,当遍历到第k个节点的时候,用变量res存储该节点对应的值

这个代码是可以通过的

不过,题目中所给的二叉树中节点的个数n的范围是[0,1000],也就是说最多有1000个节点

如果测试用例中就有1000个节点,k是1的话,也就是要找第一个最小的数,在该程序找到以后,并不是直接结束程序,而是要将整棵树遍历完以后,再结束程序,效率不是很高

import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 *   public TreeNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * @param proot TreeNode类
     * @param k     int整型
     * @return int整型
     */

    private int cnt = 0;

    private int res = -1;

    public int KthNode(TreeNode proot, int k) {
        //当树为空的时候返回-1
        if (proot == null) {
            return -1;
        }
        inOrder(proot, k);
        return res;
    }

    private void inOrder(TreeNode node, int k) {
        if (node == null) {
            return;
        }
        inOrder(node.left, k);
        cnt++;
        if (cnt == k) {
            res = node.val;
            return ;
        }
        inOrder(node.right, k);
    }
}

方案二 中序遍历(改进版)

递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能将原问题分解成更小的子问题,这是使用递归的关键。

而二叉树的递归,则是将某个节点的左子树、右子树看成一棵完整的树,那么对于子树的访问或者操作就是对于原树的访问或者操作的子问题,因此可以自我调用函数不断进入子树。

根据二叉搜索树的性质,左子树的元素都小于根节点,右子树的元素都大于根节点。因此,它的中序遍历(左中右)序列正好是由小到大的次序,因此我们可以尝试递归中序遍历,也就是从最小的一个节点开始,找到k个就是我们要找的目标。

方案二和方案一的区别在于在inOrder()方法中return的条件增加了一项,即cnt > k,就会return,不会继续往后遍历了

import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 *   public TreeNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * @param proot TreeNode类
     * @param k     int整型
     * @return int整型
     */

    private int cnt = 0;

    private int res = -1;

    public int KthNode(TreeNode proot, int k) {
        //当树为空的时候返回-1
        if (proot == null) {
            return -1;
        }
        inOrder(proot, k);
        return res;
    }

    private void inOrder(TreeNode node, int k) {
        if (node == null || cnt > k) {
            return;
        }
        inOrder(node.left, k);
        cnt++;
        if (cnt == k) {
            res = node.val;
            return ;
        }
        inOrder(node.right, k);
    }
}

方案三 非递归中序遍历(栈)

栈是一种仅支持在表尾进行插入和删除操作的线性表,这一端被称为栈顶,另一端被称为栈底。

  • 元素入栈指的是把新元素放到栈顶元素的上面,使之成为新的栈顶元素

  • 元素出栈指的是从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素

思路

递归实际上就是一种先进后出的栈结构,因此能用递归进行的中序遍历,非递归(栈)也可以实现,还需要记录遍历到第k位即截止。

具体做法

  1. 用栈记录当前节点,不断往左深入,直到左边子树为空

  2. 再弹出栈顶(即为当前子树的父节点),访问该节点,同时计数

  3. 然后再访问其右子树,其中每棵子树都遵循左中右的次序

  4. 直到第k个节点返回,如果遍历结果也没找到,则返回-1

import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 *   public TreeNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * @param proot TreeNode类
     * @param k     int整型
     * @return int整型
     */

    public int KthNode(TreeNode proot, int k) {
        if (proot == null) {
            return -1;
        }
        int cnt = 0;
        TreeNode p = null;
        Stack<TreeNode> stack = new Stack<>();
        while (!stack.isEmpty() || proot != null) {

            while (proot != null) {
                stack.push(proot);
                proot = proot.left;
            }

            p = stack.pop();
            cnt++;
            if (cnt == k) {
                return p.val;
            }
            proot = p.right;

        }
        return -1;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值