堆(英语:Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。在队列中,调度程序反复提取队列中第一个作业并运行,因为实际情况中某些时间较短的任务将等待很长时间才能结束,或者某些不短小,但具有重要性的作业,同样应当具有优先权。堆即为解决此类问题设计的一种数据结构。
逻辑定义
n个元素序列{k1,k2…ki…kn},当且仅当满足下列关系时称之为堆:
(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4…n/2)
堆的实现通过构造二叉堆(binary heap),实为二叉树的一种;由于其应用的普遍性,当不加限定时,均指该数据结构的这种实现。这种数据结构具有以下性质。
任意节点小于(或大于)它的所有后裔,最小元(或最大元)在堆的根上(堆序性)。
堆总是一棵完全树。即除了最底层,其他层的节点都被元素填满,且最底层尽可能地从左到右填入。
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。
二叉堆
二叉堆故名思议是一种特殊的堆,二叉堆具有堆的性质(父节点的键值总是大于或等于(小于或等于)任何一个子节点的键值),二叉堆又具有二叉树的性质(二叉堆是完全二叉树或者是近似完全二叉树)。当父节点的键值大于或等于(小于或等于)它的每一个子节点的键值时我们称它为最大堆(最小堆)。
完全二叉树是增加了限定条件的二叉树。假设一个二叉树的深度为n。为了满足完全二叉树的要求,该二叉树的前n-1层必须填满,第n层也必须按照从左到右的顺序被填满。
插入
新插入的节点new放在完全二叉树最后的位置,再和父节点比较。如果new节点比父节点小,那么交换两者。交换之后,继续和新的父节点比较…… 直到new节点不比父节点小,或者new节点成为根节点。
删除
让最后一个节点last成为新的节点,从而构成一个新的二叉树。再将last节点不断的和子节点比较。如果last节点比两个子节点中小的那一个大,则和该子节点交换。直到last节点不大于任一子节点都小,或者last节点成为叶节点。
以下C代码可以直接运行,有不妥当的地方望指正~
/*
堆
使用数组arr实现堆,arr[0]来存储堆的大小,从arr[1]开始存储元素
构建的是小顶堆
*/
#include <stdio.h>
void buildHeap(int *arr,int n);
void insert(int *arr,int target);
void bottom_up(int *arr);
void top_down(int *arr);
int delete_root(int *arr);
void heapSort(int *arr);
/*
arr:堆数组
n: 要建的堆的长度
*/
void buildHeap(int *arr,int n)
{
int i=1;
for(;i<=n;i++)
insert(arr,arr[i]);
}
/*
插入操作,把新插入的节点放在完全二叉树最后面的位置,然后开始自下而上的比较,
如果新节点比其父节点小(小顶堆),那就进行交换;新的父节点在与其父节点进行比较...
直到儿子节点大于父节点或者到达顶节点结束
*/
/*
arr: 堆数组
target:要插入的元素
*/
void insert(int *arr,int target)
{
arr[0]++;
arr[arr[0]]=target;
bottom_up(arr);
}
void bottom_up(int *arr)//自下而上处理 从最后一个元素往上处理
{
int nChild=arr[0];
int parent=nChild/2;
while(parent>0 && arr[nChild]<arr[parent])
{
arr[nChild]=arr[nChild]^arr[parent];
arr[parent]=arr[nChild]^arr[parent];
arr[nChild]=arr[nChild]^arr[parent];
nChild=parent;
parent=nChild/2;
}
}
/*
删除操作:只能删除根节点,根节点删除后,会得到两个子树,需要进行重构堆,进行的操作为,
让最后一个节点last成为根节点,然后进行向下比较,如果根节点大于其中一个儿子节点,则交换位置,
直到父节点不大于儿子节点或到叶子为止。
*/
int delete_root(int *arr)
{
if(arr[0]<1)
return -1;
int res=arr[1];
int nChild=arr[0];
arr[0]--;
arr[nChild]=arr[nChild]^arr[1];
arr[1]=arr[nChild]^arr[1];
arr[nChild]=arr[nChild]^arr[1];
top_down(arr);
return res;
}
void top_down(int *arr)//从第一个元素往下处理
{
int nChild;
int parent=1;
nChild=parent<<1;
while(nChild<=arr[0])
{
if(nChild+1<=arr[0] && arr[nChild]>arr[nChild+1])
nChild++;
if(arr[parent]<=arr[nChild])
break;
arr[nChild]=arr[nChild]^arr[parent];
arr[parent]=arr[nChild]^arr[parent];
arr[nChild]=arr[nChild]^arr[parent];
parent=nChild;
nChild=parent<<1;
}
}
/*
1.首先将待排序的元素构建二叉堆
2.执行删除操作,并不真正删除,把头结点与尾节点交换,并形成新的堆
3.重复步骤2
*/
void heapSort(int *arr)//堆排序
{
int len=arr[0],i=arr[0];
while(--i)
{
delete_root(arr);
printf("arr[0]:%d\n",arr[0]);
}
arr[0]=len;
}
int main(int argc,char **args)
{
int arr[30]={0,9,88,23,45,9,100,200,230,234,453,12};
int i=0;
buildHeap(arr,10);
insert(arr,2);
for(i=0;i<=arr[0];i++)
{
printf("%d ",arr[i]);
}
//printf("%d\n",delete_root(arr));
printf("\n");
heapSort(arr);
for(i=0;i<=arr[0];i++)
{
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}