堆
堆的定义
堆分为大根堆和小根堆两种。
(1)若树根结点存在左右孩子,且根结点的值大于等于左右孩子结点的值,则为大根堆。
(2)若树根结点存在左右孩子,且根结点的值小于等于左右孩子结点的值,则为小根堆。
(3)以左、右孩子为根的子树又各是一个堆。
堆的性质
- 堆中某个节点的值总是不大于或不小于其父节点的值;
- 堆总是一棵完全二叉树
堆的实现
堆向下调整算法
给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整 成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。
int a[] = {27,15,19,18,28,34,65,49,25,37};
代码实现
void heapify(int a[],int,size,int index){
while(1){
int left=index*2+1;
int right=index*2+2;
if(left>size){
return;
}
int min=left;
if(right<size&&a[right]<a[left]){
min=right;
}
if(a[index]>a[min]){
int k=a[index];
a[index]=a[min];
a[min]=k;
}
index=min;
}
}
堆的创建
给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。如果根节点左右子树不是堆,从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。
int a[] = {1,5,3,8,7,6};
在第三次1、8交换后,其左子树的结构被破坏,因此每次调用元素的时候都有可能会破坏子堆结构,所以每次发生元素交换的时候都要递归调用重新构造函数的结构。第四次交换对左子树重新构建堆,最终建出一个大堆。
代码实现:
void createHeap(int a[],int size)
for(int i=(size-2)/2;i>=0;i--){
heapify(a,size,i);
}
}
堆的插入
先把数插入到数组的尾上,再进行向上调整算法,直到满足堆。
代码实现:
//向上调整
void adjustup(int a[],int index){
while(index>0){
int parent=(index-1)/2;
if(a[parent]<a[index]){
int k=a[parent];
a[parent]=a[index];
a[index]=k;
}
index=parent;
}
}
void HeapPop(Heap *heap,int val){
heap->array[hap->size++]=val;
adjustup(heap->array,heap->size-1);
}
堆的删除
删除堆是删除堆顶的数据(最大值或最小值),将堆顶的数据与最后一个数据交换,然后删除数组最后一个数据,再进行向下调整算法。
代码实现:
void HeapPop(Heap *heap){
assert(heap->size>0);
heap->array[0]=heap->array[size-1];
heap->size--;
heapify(heap->array, heap->size, 0);
}
堆排序**
堆排序是一种减治算法,将含有k个元素的数组a建成大堆后,把堆顶元素a[0]与最后一个元素a[k-1]交换,最后一个元素就是最大的数。通过向下调整后,再将堆顶元素a[0]与倒数第二个元素a[k-2]交换,此时a[k-2]就是所有元素中第二大的元素。以此类推,不断调整,最终有序。
无序区间:[0,size-i)
有序区间:[size-i,size)
void HeapSort(int a[],int size){
createHeap(int a[],int size);
for(int i=0;i<size;i++){
int k=a[0];
a[0]=a[size-i-1];
a[size-i-1]=k;
heapify(a,size-i-1,0)
}
}