堆(Heap)是由完全二叉树实现的,具体关于完全二叉树的性质等,下面会简单介绍。堆分为最大值堆和最小值堆,最大值堆中所有元素都大于它的孩子的值,最小值堆中所有元素的值都小于它孩子的值。
一:完全二叉树
所谓完全二叉树最基本的还是一棵二叉树,即一个节点最多有两个子节点,最少有0个子节点(即是叶子节点),而二叉树里面又有一种比较特殊的二叉树叫做满二叉树。满二叉树的特点就是除了叶子节点之外,所有的节点都有两个子节点,所以他也有一些特殊的性质,比如,叶子节点的个数会比内部节点的个数多一个等,而满二叉树中又有一种更加特殊的树就是完全二叉树。完全二叉树也具有满二叉树的性质,但是它还有一个更加严格的规定就是它的节点除了最后一层可以不满之外而且必须全部在左边,所有的层都必须是满的。下面给出一些具体的图片帮助大家理解。
上面就是满二叉树,和它在数组中的存储形式(这点很重要)!!!!!
二:最大值堆
首先说明一下,在你知道一个节点在数组中的储存下标的时候,你是可以计算出它的父节点和两个子节点的下标值的,具体的计算依据就是完全二叉树的性质。
比如你知道了当前节点的下标值是pos,那我就可以计算出父节点的下标值是(pos - 1 )/ 2, 左孩子的下标是2 * pos
#include<iostream>
#include<iomanip>
using namespace std;
template<typename Elem>
class MaxHeap{
private:
Elem * heap;
int maxSize;
int currentSize;
//下滑,确定元素在堆中的正确位置
void shiftDown(int pos){
while(!isLeaf(pos)){
int max = leftChild(pos);
int rc = rightChild(pos);
if(rc < currentSize && heap[max] < heap[rc])
max = rc;
if(heap[pos] >= heap[max])
return;
else{
swap(heap, pos, max);
pos = max;
}
}
}
//构建堆栈
void buildHeap(){
for(int i = currentSize /2 -1; i >= 0;i--)
shiftDown(i);
}
//交换堆中两个元素的位置
void swap(Elem * h, int p1, int p2){
Elem temp = h[p1];
h[p1] = h[p2];
h[p2] = temp;
}
public:
MaxHeap(Elem * h, int num, int max){
maxSize = max;
currentSize = num;
heap = new Elem[maxSize];
for(int i = 0; i < currentSize; i++)
heap[i] = h[i];
buildHeap();
}
~MaxHeap(){
delete [] heap;
}
//返回堆的大小
int heapSize(){
return currentSize;
}
//判断是否为叶子节点
bool isLeaf(int pos) const{
return (pos >= currentSize / 2 && pos < currentSize);
}
//左子节点对应的下标
int leftChild(int pos) const{
return 2 * pos + 1;
}
//右子节点对应的下标
int rightChild(int pos) const{
return 2 * pos + 2;
}
//父节点的下标
int parent(int pos) const{
return (pos - 1) / 2;
}
//在堆栈中插入元素
bool insert(const Elem & value){
if(currentSize >= maxSize)
return false;
else{
heap[currentSize++] = value;
int curr = currentSize - 1;
while(curr != 0 && heap[curr] > heap[parent(curr)]){
swap(heap, curr, parent(curr));
curr = parent(curr);
}
return true;
}
}
//在堆栈中删除最大元素,然后保存在value中
bool removeMax(Elem & value){
if(currentSize == 0)
return false;
else{
swap(heap, 0 , --currentSize);
value = heap[currentSize];
if(currentSize != 0)
shiftDown(0);
return true;
}
}
//移除pos处的元素,然后保存在value中
bool remove(int pos, Elem & value){
if(pos < 0 || pos >= currentSize)
return false;
else{
swap(heap, pos, --currentSize);
value = heap[currentSize];
while(pos != 0 && heap[pos] > heap[parent(pos)]){
swap(heap, pos, parent(pos));
pos = parent(pos);
}
shiftDown(pos);
return true;
}
}
//打印堆
void show() const{
for(int i = 0; i < currentSize; i++){
cout << setw(7) << heap[i];
if(i + 1 % 10 == 0)
cout << endl;
}
cout << endl;
}
};
三,总结
堆在软件工程中用的也是比较频繁的一种数据结构,后面还会介绍基于堆的堆排序算法,所以说掌握堆的基本原理还是非常必要的。