本教程的内容基本来自于《Java数据结构与算法》
堆一种是一种特殊的二叉树结构,包含如下特点:
- 它是完全二叉树,即除了最后一层节点不是满的,其他层节点从左到右都完全是满的;
- 一般使用数组来存储堆数据结构;
- 堆中每个节点的关键字都大于等于(小于等于)这个节点的子节点的关键字,节点关键字大于等于子节点的关键字成为最大堆;反之,称为最小堆。
下图显示了完全二叉树与非完全二叉树的区别
下图显示了一个最大堆的数组存储方式
图中可以看到,根节点存储在数组的第一个位置,然后从上层到下层,从左到右,将数据存储到数组中。
很容易推得,父节点与子节点在数组中位置角标有如下关系:
设节点的角标为 i i ,则左子节点(如果有左孩子的话)在数组中的角标为,右子节点(如果有右孩子的话)在数组中的角标为 2∗i+2 2 ∗ i + 2 。
下面代码实现最大堆的过程。
1. 节点定义
// 定义节点
class Node{
private int idata;
public Node(int data){
idata = data;
}
public int getKey(){
return idata;
}
}
2. 定义堆
// 定义堆
class Heap{
private Node[] heapArray; // 存储节点的数组
private int maxSize; // 最大存储大小
private int currSize; // 当前大小
public Heap(int size){
maxSize = size;
currSize = 0;
heapArray = new Node[size];
}
}
3. 判断堆是否为空
//判断是否为空
public boolean isEmpty(){
return (currSize == 0);
}
4. 插入元素
插入元素时,总是先插入到数组最后,插入并调整结构的步骤:
- 将待插入元素放到数组最后;
- 令当前节点为插入元素所在节点;
- 当前节点与父节点比较大小,如果父节点的关键字小于当前节点,则与父节点交换;否则,表示调整结构结束,退出;
- 如果没有退出,对交换后的当前节点重复上一步过程。
下图为插入节点100的过程:
代码如下
// 插入元素
public boolean insert(int key){
// 存储已满
if (currSize == maxSize)
return false;
Node node = new Node(key);
// 将新节点放到数组最后
heapArray[currSize] = node;
// 调整堆结构
trickleUp(currSize++);
return true;
}
// 向上比较
private void trickleUp(int i) {
// 父节点角标
int parent = (i - 1) / 2;
Node temp = heapArray[i];
// 当前节点关键字大于父节点关键字
while (i > 0 && heapArray[i].getKey() > heapArray[parent].getKey()){
heapArray[i] = heapArray[parent];
i = parent;
parent = (i - 1) / 2;
}
heapArray[i] = temp;
}
5. 删除元素
堆中删除元素,总是删除堆顶元素,删除并调整结构的步骤:
- 删除堆顶元素,并将最后一个元素放到堆顶;
- 设当前节点为堆顶节点;
- 找到当前节点的子节点中关键字最大的那个节点,然后将此子节点与当前节点比较大小,如果此节点关键字大于当前节点,则交换两个节点;否则,表示调整结构结束,退出;
- 如果没有退出,对交换后的当前节点重复上一步过程。
下图为删除节点100的过程:
代码如下:
// 删除元素
public Node remove(){
Node root = heapArray[0];
// 最后一个元素放到堆顶
heapArray[0] = heapArray[--currSize];
// 向下比较
trickleDown(0);
return root;
}
// 向下比较
private void trickleDown(int i){
int largeChild;
Node top = heapArray[i];
while (i < currSize/2){
int leftChild = (i-1) / 2;
int rightChild = leftChild + 1;
if (rightChild < currSize &&
heapArray[rightChild].getKey() > heapArray[leftChild].getKey())
largeChild = rightChild;
else
largeChild = leftChild;
// 如果当前节点关键字大于等于最大子节点关键字,退出
if (top.getKey() >= heapArray[largeChild].getKey())
break;
heapArray[i] = heapArray[largeChild];
i = largeChild;
}
heapArray[i] = top;
}
github完整代码:
https://github.com/gamersover/data_structure_java/blob/master/Heap/HeapApp.java