数据结构与算法--二叉树第k个大的节点

二叉树第k个大的节点
  • 二叉树文章列表:

数据结构与算法–面试必问AVL树原理及实现

数据结构与算法–二叉树的深度问题

数据结构与算法–二叉堆(最大堆,最小堆)实现及原理

数据结构与算法–二叉查找树转顺序排列双向链表

数据结构与算法-- 二叉树中和为某一值的路径

数据结构与算法-- 二叉树后续遍历序列校验

数据结构与算法-- 广度优先打印二叉树

数据结构与算法–解决问题的方法- 二叉树的的镜像

数据结构与算法–重建二叉树

数据结构与算法–二叉查找树实现原理

数据结构与算法–二叉树实现原理

数据结构与算法–B树原理及实现

数据结构与算法–数字在排序数组中出现次数

数据结构与算法–死磕二叉树

数据结构与算法–二叉树第k个大的节点

数据结构与算法–求1~n能组成的所有二叉搜索树的排列

  • 之前遇到过多个类型的题型但是都是针对数组这种数据结构,如果两篇:

数据结构与算法–最小的k个数
数据结构与算法–查找与排序另类用法-旋转数组中的最小数字

  • 其中最小k个数我们用二分法的思路,很快得出解,还有就是用二叉堆的特性求解
  • 在旋转数组中查找最小值,直接二分查找完成
  • 由上可见,二分法在数组中查找第k个大小的数还是很好用的。但是我们此处针对的是二叉排序树,二分法可能排不上用处
方法一统计节点法
  • 利用二叉排序数的特性,左节点 比 根小, 右节点比根大,那么需要找第 k 大的,直接统计左右节点个数

    • 如果右边节点个数 rightCount > k,那么第k 大的必然在右节点
    • 如果右节点个数 rightCount < k,那么有两种情况:
      • 当rightCount < k, 并且rightCount - k = 1 此时,最大的就是当前根节点
      • 如果rightCount < k,并且rigntCount- k > 1 此时,那么第 k 大的节点就是左节点的 k - rightCount - 1 个大的节点
  • 我们依次筛选左右节点,直到找到k 的具体节点,或者父节点,将k范围缩小到1或 0

  • 当k = 1 ,就是最右节点,当k = 0 就是最左节点。我们用如下图实例

  • 情况一:正好是root节点情况
    数据结构与算法--二叉树第k个大的节点_第1张图片

  • 情况二:在left节点中数据结构与算法--二叉树第k个大的节点_第2张图片
    数据结构与算法--二叉树第k个大的节点_第3张图片

  • 情况三,在right节点中
    数据结构与算法--二叉树第k个大的节点_第4张图片

  • 经如上分析有如下代码:

/**
 * 二叉搜索树中查找第 k 大的节点
 *
 * @author liaojiamin
 * @Date:Created in 10:18 2021/7/16
 */
public class FinMaxKNumberInBinary {
    public static void main(String[] args) {
        BinaryNode node = new BinaryNode(null, null, null);
        BinarySearchTree searchTree = new BinarySearchTree();
        Random random = new Random();
        for (int i = 0; i < 5; i++) {
            node = searchTree.insert(random.nextInt(100), node);
        }
        searchTree.printTree(node);
        System.out.println();
        System.out.println(getMaxKNumber(node, 4).getElement());

    }

    /**
     * 遍历统计,在比较
     */
    public static BinaryNode getMaxKNumber(BinaryNode binaryNode, Integer k) {
        if (binaryNode == null || k < 0) {
            return null;
        }

        if (k == 1) {
            BinaryNode right = binaryNode;
            while (right.getRight() != null) {
                right = right.getRight();
            }
            return right;
        }
        if (k == 0) {
            BinaryNode left = binaryNode;
            while (left.getLeft() != null) {
                left = left.getLeft();
            }
            return left;
        }
        BinaryNode baseCount = new BinaryNode(null, null, null);
        baseCount.setCount(0);
        int rightCount = countNode(binaryNode.getRight(), baseCount).getCount();
        //第k大的在right
        if (rightCount >= k) {
            return getMaxKNumber(binaryNode.getRight(), k);
        }
        //此时root节点是当前第 k大的数据
        if (k - rightCount == 1) {
            return binaryNode;
        }
        //第k大的在left
        if (k - rightCount > 1) {
            return getMaxKNumber(binaryNode.getLeft(), k - rightCount - 1);
        }
        return null;
    }

    public static BinaryNode countNode(BinaryNode binaryNode, BinaryNode baseCount) {
        if (binaryNode == null) {
            return baseCount;
        }
        baseCount.setCount(baseCount.getCount() + 1);
        countNode(binaryNode.getLeft(), baseCount);
        countNode(binaryNode.getRight(), baseCount);
        return baseCount;

    }
}

  • 以上实现方案中通过递归不断将 第 k大的节点范围缩小,在最后的2个节点中找出我们的值,问题在于,存在太多重复的遍历,如上情况三种:
  • 当遍历右子树 C的时候其实已经遍历过 G,F
  • 但是在之后的步骤中还依然需要遍历G, F继续缩小范围,因此时间效率很低
方法二逆中序遍历
  • 还是利用二叉搜索树的特性,我们需要找最大的第 k位置,但是在二叉树三种遍历方式中,前序,中序,后续遍历,只有中序遍历是按顺序排列二叉搜索树的所有节点,但是是小到大的顺序
  • 由此我们得到启发:
    • 我们利用中序遍历,求第k个大的,也就是从小到大排列的第 s - k+ 1 个数据,但是此时我们并不知道二叉树的总节点,无法得出这个值
    • 如果我们反过来遍历,中序遍历是 左,中,右, 我们换成 右,根,左,那么直接求第k个位置的遍历到的节点就得到我们的解
  • 因此最简单的遍历查找方式如下
/**
 * 二叉搜索树中查找第 k 大的节点
 *
 * @author liaojiamin
 * @Date:Created in 10:18 2021/7/16
 */
public class FinMaxKNumberInBinary {
    public static void main(String[] args) {
        BinaryNode node = new BinaryNode(null, null, null);
        BinarySearchTree searchTree = new BinarySearchTree();
        Random random = new Random();
        for (int i = 0; i < 5; i++) {
            node = searchTree.insert(random.nextInt(100), node);
        }
        searchTree.printTree(node);
        System.out.println();
        System.out.println(getMaxKNumber(node, 4).getElement());

        System.out.println(getMaxKNumberOver(node, 4).getElement());

    }

    /**
     * 直接从最大的遍历,同时统计遍历节点数,当统计到k个,则是第k个大
     */
    public static BinaryNode getMaxKNumberOver(BinaryNode binaryNode, Integer k) {
        if (binaryNode == null || k < 0) {
            return null;
        }
        BinaryNode baseCount = new BinaryNode(null, null, null);
        baseCount.setCount(0);
        return printOver(binaryNode, baseCount, k);
    }

    /**
     * 右, 中,  左,方式遍历树,与之前树遍历三种都不同
     */
    public static BinaryNode printOver(BinaryNode node, BinaryNode baseCount, Integer k) {
        if(node == null){
            return baseCount;
        }
        baseCount = printOver(node.getRight(), baseCount, k);
        baseCount.setCount(baseCount.getCount() + 1);
        if(baseCount.getCount() == k){
            baseCount = node;
            baseCount.setCount(k);
            return  baseCount;
        }
        return printOver(node.getLeft(), baseCount, k);
    }
}

上一篇:数据结构与算法–再来聊聊数组
下一篇:数据结构与算法–求1~n能组成的所有二叉搜索树的排列

你可能感兴趣的:(数据结构,算法,数据结构,算法)