一.堆的定义及本质
要知道在Java中,堆是以优先级队列来表示的。因此学会了堆,我们也就学会优先级队列
1.堆的定义
当一颗二叉树的每个节点都大于等于它的两个子字节时,它被称为堆有序。而堆是一组能够用堆有序的完全二叉树排序的元素,并在数组中按照层级储存
总而言之堆具有一下性质:
①堆逻辑上是一颗完全二叉树,但本质上确是数组
②堆中的某个节点总是不大于或不小于其父节点的值
2.堆的分类
堆分为大顶堆与小顶堆
①大顶堆:每个结点的值都大于或等于其左右孩子结点的值。
②小顶堆:每个结点的值都小于或等于其左右孩子结点的值。
定义是不是看上去难懂丫,那我们来根据图来了解一下吧:
3.完全二叉树
完全二叉树就是除了叶子节点之外,其余节点都有左右孩子的二叉树
下图就是一个完全二叉树(也是一个大顶堆)
堆本质上是一个数组,图中粉色序列就是堆在数组中索引,蓝色序列就是堆在数组中储存的值
二.Java API中的PriorityQueue类
我们学习堆,首先就要来了解堆是如何创建并如何使用的
下面我们来看一下Java中的PriorityQueue类,并据此创建堆,完成堆的一些基本操作
1.构造方法
在这里我们要知道最主要的两点:
①创建小顶堆,构造方法中不用传入任何值
②创建大顶堆,要在构造方法中填入相应的规则
在下面我们会一一创建
2.方法摘要
其中最主要且需要我们掌握的就是
增( offer ) 删( poll )查( peek )等方法
3.堆的使用
在大顶堆中传入的相应规则中,其中是一个匿名内部类,类中的compare方法是我们要重写的接口
三.堆的实现(大顶堆)
了解了堆是如何使用的,那么下面我们就来实现一下吧
堆的本质是数组,因此成员变量中要用数组arr,有效长度size,以及数组容量capacity
当然数组是有限的,若数组溢出,我们就要扩容数组
下面我们来写出最基本的成员变量及方法
1.建堆heapify
如果我们要将一个数组传入到堆中,一个一个的向其中添加效率太慢了,因此我们可以实现一个建堆的构造方法,可以直接将数组传入,从而直接为我们创建好堆
思路:找到最后一个非叶子节点,从后向前依次下潜(与其左右两个孩子的最大者进行交换,重复此操作,直到到叶子节点)
我们来根据下面的堆来了解一下
首先我们找到最后一个非叶子节点7,但它比它的左右两个孩子5和3大,因此进行下一个节点2的操作
我们进行2节点的操作,我们找到其最大的右孩子5,与其交换
我们再进行下一个节点1的操作,我们找到其最大的右孩子7与其交换
再继续,它最大的是左孩子6,交换
这样我们就建成了一个大顶堆了
以上思路也叫做弗洛伊德建堆算法
2. poll移除堆顶元素
思路:数组的最后一个元素是最容易删除的,因此我们要删除堆顶元素,就将堆顶元素与最后一个叶子节点交换,再删除最后一个节点,再将交换的堆顶元素下潜
3. peek获取堆顶元素
4. offer添加元素
思路:将要添加的元素上浮(其与父节点比较,大于父节点交换,重复此操作,直到堆顶)
5.结果显示