排序算法总结(三)
2017-04-20 21:40
134 查看
堆排序
1.堆
堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2],满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,满足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆。大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。
2.堆排序的思想
利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单,堆排序的时间复杂度和归并排序,快速排序一样都是O(nlogn)。其基本思想为(小顶堆):
1)将初始待排序关键字序列(R0,R2....Rn-1)构建成小顶堆,此堆为初始的为数组,他是无序的;
2)将堆顶元素R[0]与最后一个元素R[n-1]交换,此时得到新的无序区(R0,R2,......Rn-2)和新的有序区(Rn),且满足R[1,2...n-1]>=R
;
3)由于交换后新的堆顶R[0]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[0]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
操作过程如下:
1)初始化堆:将R[0..n-1]构造为堆;
2)将当前无序区的堆顶元素R[0]同该区间的最后一个记录交换,同时将堆的长度做size--,然后将新的无序区调整为新的堆。
3)不断的重复2的操作直到size=0停止;
列子如下: 给定一个整形数组a[]={16,7,3,20,17,8},对其进行堆排序。
如下面图,画的不好看;
简而言之就是:数组写成二叉树形式----调整为小顶堆----首尾互换------调整为小顶堆------首尾互换------直到size=0;
代码如下:
//整理节点time:O(lgn) template<class T> void MinHeapify(T arry[], int size, int element) { int lchild = element * 2 + 1, rchild = lchild + 1;//左右孩子 while (rchild<size) //子孩子均在范围内 { if (arry[element] <= arry[lchild] && arry[element] <= arry[rchild])//如果比左右孩子都小,完成整理 { return; } //不论是左右边哪边最小,都将最小的那个交换为子父节点 if (arry[lchild] <= arry[rchild]) //如果左边最小 { swap(arry[element], arry[lchild]);//把左面的提到上面 element = lchild; //循环时整理子树 } else //否则右面最小 { swap(arry[element], arry[rchild]);//同理 element = rchild; } lchild = element * 2 + 1; rchild = lchild + 1;//重新计算子孩子位置 } if (lchild<size&&arry[lchild]<arry[element]) //子父左孩子判断 { swap(arry[lchild], arry[element]); } return; } //堆排序time:O(nlgn) template<class T> void HeapSort(T arry[], int size) { int i; //从子树开始整理树,最后变为小顶堆 for (i = size - 1; i >= 0; i--) { MinHeapify(arry, size, i); } while (size>0) { swap(arry[size - 1], arry[0]);//将根(最小)与数组最末交换 size--; //树长度减小 MinHeapify(arry, size, 0); //整理树重新变为小顶堆 } return; }