本文将首先介绍什么是堆,然后介绍了堆的插入和删除操作,最后给出了堆的代码实现,并进行了测试。
什么是堆
堆是一颗完全二叉树,堆中某个节点的值总是不大于或不小于其父节点的值。根节点最大的堆叫做大根堆,根节点最小的堆叫做小根堆。 首先解释下什么是完全二叉树,设一颗二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。如下图所示,左侧的二叉树满足完全二叉树的定义,而右侧的不满足。

堆的表示
一般的二叉树表示时需要首先定义节点结构,节点中包含指向父节点的指针,如下所示:
class Node<E>{
E e;//节点储存的值
Node left,right;//左右子节点
public Node(E e){
this.e = e;
this.left = this.right = null;
}
}
但是堆并不是像树一样存储,其中没有使用父指针或者子指针,而是用数组来实现。怎么用数组来实现呢?先看一张图,如下:

int parentIndex = (i - 1) / 2;
然后,通过父节点的索引来找子节点的索引,设父节点的索引为p,则其孩子节点的索引为
int leftChildIndex = 2 * p + 1;//左子节点
int rightChildIndex = 2 * p + 2;//右子节点
这样,通过子节点与父节点之间的索引关系,便相当于建立了父节点和子节点之间的指针,实现了用数组来存储堆这种数据结构。
堆的插入
对于堆来说,只有插入和删除两种操作,先谈一下堆的插入操作,此处以小根堆为例。

堆的删除
对于堆来说,删除元素是指移除根节点。以小根堆为例,是指移除根中最小值的节点,也就是根节点。移除很简单,之后我们要通过操作来使得堆依然满足定义。

堆的实现
下面给出堆的代码实现,如下所示,实现了堆的插入和删除操作,并进行了测试。
package datastructures;
public class Heap {
private int[] data;//存储堆的数组
private int size;//堆中元素的数量
public Heap(int capacity){
data = new int[capacity];//初始化数组
size = 0;//初始化数量
}
/**
* 插入元素
*/
public void insert(int value) throws Exception{
if(size == data.length)
throw new Exception("堆已满");
else{
data[size] = value;//将新插入的元素放在堆的末尾
int i = size;
size ++;
while(i > 0){//对堆进行调整,直至满足条件
int p = (i - 1) / 2;
if(data[i] < data[p]){
int temp = data[i];
data[i] = data[p];
data[p] = temp;
i = p;
}
else
break;
}
}
}
/**
* 删除堆中的元素
* @return
* @throws Exception
*/
public int delMin() throws Exception{
int res;
if(size == 0)
throw new Exception("为空");
else{
res = data[0];//返回索引为0的元素
size -- ;
data[0] = data[size];//将堆中最后一个元素填充至索引为0的位置
int i = 0;
while(2 * i + 1 < size){//对堆进行调整
int left = 2 * i + 1;
int right = 2 * i + 2;
if(right < size && data[right] < data[left] && data[right] < data[i]){
int temp = data[i];
data[i] = data[right];
data[right] = temp;
i = right;
}
else if(data[left] < data[i] && (right >= size || data[right] >= data[left])){
int temp = data[i];
data[i] = data[left];
data[left] = temp;
i = left;
}
else
break;
}
}
return res;
}
//测试
public static void main(String[] args) throws Exception {
Heap heap = new Heap(10);
heap.insert(1);
heap.insert(5);
heap.insert(4);
heap.insert(3);
heap.insert(6);
heap.insert(2);
System.out.println(heap.delMin());
System.out.println(heap.delMin());
System.out.println(heap.delMin());
System.out.println(heap.delMin());
System.out.println(heap.delMin());
System.out.println(heap.delMin());
}
}
推荐阅读 为什么有红黑树?什么是红黑树?看完这篇你就明白了 《深入浅出话数据结构》系列之什么是B树、B+树?为什么二叉查找树不行? 都2020年了,听说你还不会归并排序?手把手教你手写归并排序算法 为什么会有多线程?什么是线程安全?如何保证线程安全?
觉得文章有用的话,点赞+关注呗,好让更多的人看到这篇文章,也激励博主写出更多的好文章。 更多关于算法、数据结构和计算机基础知识的内容,欢迎扫码关注我的原创公众号「超悦编程」。
