Java -数据结构,【优先级队列 / 堆】

本文介绍了二叉树的顺序存储方式,特别是完全二叉树在数组中的存储,以及堆的概念。堆是一种特殊的完全二叉树,分为大根堆和小根堆,常用于快速找到集合中的最大值或最小值。文章详细讲解了如何通过向下调整构建堆,以及建堆的过程。此外,堆还被应用于优先级队列,用于处理优先级高的任务。堆排序作为堆的一个应用,通过调整堆来实现排序。最后,文章提到了TopK问题,利用堆可以高效地找出数组中的最大k个数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、二叉树的顺序存储

在前面我们已经讲了二叉树的链式存储,就是一棵树的左孩子和右孩子
而现在讲的是:顺序存储一棵二叉树。

1.1、存储方式

使用数组保存二叉树结构,方式即将二叉树用层序遍历方式放入数组中。 一般只适合表示完全二叉树,因为非完全二叉树会有空间的浪费。
这种方式的主要用法就是堆的表示
在这里插入图片描述

下标关系

已知双亲(parent)的下标,则:
左孩子(left)下标 = 2 * parent + 1;
右孩子(right)下标 = 2 * parent + 2;

已知孩子(不区分左右)(child)下标,则:
双亲(parent)下标 = (child - 1) / 2;

也就是前面我们将的性质5:
对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的结点有:

(1)若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
(2)若2i+1<n,左孩子序号:2i+1,否则无左孩子
(3)若2i+2<n,右孩子序号:2i+2,否则无右孩子
在这里插入图片描述

二、堆

2.1、概念

  1. 堆逻辑上是一棵完全二叉树
  2. 堆物理上是保存在数组中
  3. 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆
  4. 反之,则是小堆,或者小根堆,或者最小堆
  5. 堆的基本作用是,快速找集合中的最值 :无论是 大根堆还是小根堆, 它们的 最值【最大值 和 最小值】都处于 二叉树的 根结点处。要想获得 最值,直接 peek 方法,就能获得 树 的 根结点值 / 最值。在这里插入图片描述
    在这里插入图片描述

2.2、操作-向下调整

前提:左右子树必须已经是一个 堆 / 逻辑上是一棵完全二叉树。
将一组 记录完全二叉树数据 的 数组 转换成 大根堆。
在这里插入图片描述

向下调整出现的问题:

这里是引用
得出结论:其实每棵树的调整结束位置都是一样的︰不能超过数组长度。

如何构造一个 向下调整的函数 - 重点
在这里插入图片描述

public class TestHeap {
   
   
    public int[] elem;//底层是一个数组
    public int usedSize;

    //
    public TestHeap(){
   
   
        this.elem = new int[10];
    }

    /**
     * 创建堆
     * @param array 堆里面存放的元素
     */
    public void creatHeap(int[] array){
   
   
        //将array数组的元素存入elme 数组
        for (int i = 0; i < array.length; i++) {
   
   
            elem[i] = array[i] ;
            usedSize++;
        }
        for (int praent = (usedSize-1-1)/2; praent >= 0; praent--) {
   
   
            shiftDown(praent,usedSize);
        }
    }

    /**
     * 向下调整
     * @param praent 每棵子树的父亲节点
     * @param len 调整的结束位置,不能大于数组的长度
     */
    public void shiftDown(int praent, int len){
   
   
        int child = 2+praent +1;
        while (child < len){
   
   
            if(child + 1 > len && this.elem[child] < this.elem[child+1]){
   
   
                child++;
            }

            if(elem[child] > elem[praent]){
   
   
                int tmp = elem[child];
                elem[child] = elem[praent];
                elem[praent] = tmp;

            }else {
   
   
                break;
            }
        }
    }
}

测试一下:
在这里插入图片描述

模拟实现 堆 的 时间复杂度
在这里插入图片描述
上图转载于:堆 / 优先队列

粗略估算,可以认为是在循环中执行向下调整,为 O(n * log(n))
(了解)实际上是 O(n)
堆排序中建堆过程时间复杂度O(n)怎么来的?

2.3、操作-建堆

下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆

图示(以大堆为例):

// 建堆前
int[] array =
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Später321

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值