一:什么是堆?
1.堆: n个元素的序列{k1,k2,k3,.....kn}当且仅当满足以下关系是,称为堆. {ki <= k2i 且 ki <= k2i+1} 或
{k2i <= ki 且 k2i+1 <= ki} (i = 1,2,...[n/2] ).这也是堆的一个性质.
2.堆结构是一种数组对象堆,它可以被视为一棵完全二叉树如下图, 书中每个节点与数组中存放该节点中值的那个元素
对应,除了最后一层,其余的每个节点都是满的.因为堆可看成是一棵完全二叉树,那它就满足一些树的性质,
对于有n个结点的完全二叉树,对任一结点i 有:
1)如果 i = 1,则结点i是二叉树的根,无双亲;
2)如果i > 1,则其双亲Parent(i)是[ i / 2];
3)如果2i > n,则结点i 无左孩子(结点i为叶子结点);否则,其左孩子Lift_child(i) 是结点2i.
4)如果2i + 1 > n,则结点i无右孩子;否则,其右孩子right_child(i)是结点2i + 1.
5)其非终端结点是第[ n / 2]个元素,(非终端结点是指:完全二叉树中的最后一个带有叶子的结点).
二:堆的分类
堆包含: 1.大根堆. 2.小根堆.
1. 大根堆必须满足: {ki <= k2i , ki <= k2i+1} (i = 1,2,...[n/2] ), 大根堆排序后元素是由小到大的顺序.
2.小根堆必须满足: {k2i <= ki , k2i+1 <= ki} (i = 1,2,...[n/2] ). 小根堆排序后元素是由大到小的顺序.
三:如何实现堆排序?
1.堆排序需要解决的两个问题就是:(1)如何由一个无序序列建成一个堆? (2)如何在输出堆顶元素之后,
调整剩余元素成为一个新的堆?
2. 让一个无序序列建成一个堆的最重要的步骤就是堆调整(牢记堆性质).
堆调整的思想:
1).把待调整的记录当成根,然后 比较此根和其左,右孩子这三个数的大小,如果左,右孩子中还中有一个最大的,
则把此最大值与根值交换(这是大根堆调整法,小根堆调整只需找到最小的值并与根交换就行),
2).接着从最大值下标开始,并又以此结点为根重复1)中的步骤直到最大值下标循环的叶子结点.
3)如果1)中比较后得到的最大值(/最小值)仍为根,则表示以此根结点的子序列已满足堆性质,就已经是堆了,
3.建堆。
可以自底向上地用heap_adjust来将一个数组array[1..n] (n = length[array])变成一个堆,因为,
子数组array[(n/2+1) ....n ]中的 元素都是树上中的叶子,每个叶子都可以看成是一个元素的堆,根据堆的性质知,根是一个最值,而自底向上调整数组,就是子根与子根 或是子根与 叶 再和父根比较 进而可保证越往上值越大(越小)。
假设有数组A = {1, 3, 4, 5, 7, 2, 6, 8, 0}。那么调堆的过程如下图,数组下标从0开始,A[3] = 5开始。分别与左孩子和右孩子比较大小,如果A[3]最大,则不用调整,否则和孩子中的值最大的一个交换位置,在图1中是A[7] > A[3] > A[8],所以A[3]与A[7]对换,从图1.1转到图1.2。
4.堆排序.
堆排序,首先应该是一个堆,即先让一个无序序列调整成一个堆,然后再进行排序.
排序的思想:
1)首先要清楚堆的一个性质,那就是根为最值. 因为数组中最大(或最小)元素是在根array[1]上,因此通过访问根就可以到达排序的效果.
2)可通过把它与叶子结点array[n]进行交换来达到最终的正确位置.因为叶子都在array[n/2+1 ..n]上,让第一最值根和最后一个叶子交换,然后,再进行堆调整.
3)第2)中的交换产生两种效果:
其一,将第一最值保存到array的最后一个元素空间中(此时已进开始进行数组就地排序了!!)
其二,叶子结点到根后就会破坏堆性质,那就会引起堆调整反应(但,此时的调整有点变化的就是待调整堆的长度,还是原来的数组长度吗? 那肯定不是,如果还是数组长度的话,就会把叶子上的最值又调到了根上,那就会重复访问根了,所以每次交换后,待排序的长度就要减一(待排序的堆大小),使得堆调整到达交换的叶子结点.),进而将第二最值调到了根array[1]的位置.
然后就是将次最值和倒数第二个叶子交换(将此最值保存到第一最值的前一位置),就这样重复1)步骤,直到最后一个元素.
c++代码:
#include <iostream> using namespace std; void exchange(int &a,int &b); //交换 void Max_Heapify(int *,int,int); //保持堆性质 void Build_Max_Heapify(int *,int); //建堆 void Sort_Heap(int*,int); // 堆排序 void exchange(int &a,int &b) { int t; t = a ;a = b ; b =t ; } void Max_Heapify(int *heap, int i,int heapSize) //heapSize :堆大小 { int l = 2*i + 1; //左子树下标 int r = 2*i + 2; //右子树下标 int largest; //最大节点下标 if(l < heapSize && heap[l] > heap[i]) largest = l; else largest = i; if(r < heapSize && heap[r] > heap[largest] ) largest = r; if(largest != i) { exchange (heap[i],heap[largest]); Max_Heapify(heap,largest,heapSize); //保持以largest为根节点的子树满足堆性质 } } void Build_Max_Heapify(int *heap,int arraySize) { int i = (arraySize/2)-1; //含有子树的根节点的下标,(下标最大的根) for(; i>=0; i--) Max_Heapify(heap,i,arraySize); } void Sort_Heap(int *heap,int arraySize) { int i = arraySize-1; Build_Max_Heapify(heap,i); for(; i>=1 ; i--) { exchange(heap[i],heap[0]); Max_Heapify(heap,0,i-1); } } int main() { int a[N]={1,24,35,6,8,75,89,45,44,5,345,23}; int i; Sort_Heap(a,N); for(i=0; i<N; i++) cout << a[i]<<" "; cout <<endl; return 0; }