堆排序
2012-08-03 17:12 coodoing 阅读(880) 评论(1) 编辑 收藏 举报1、堆排序定义
n个关键字序列Kl,K2,…,Kn称为堆,当且仅当该序列满足如下性质(简称为堆性质):
(1) ki≤K2i且ki≤K2i+1
(2)Ki≥K2i且ki≥K2i+1(1≤i≤ )
若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。
大顶堆和小顶堆
根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆。
根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆。
注意:
①堆中任一子树亦是堆。
②以上讨论的堆实际上是二叉堆(Binary Heap),类似地可定义k叉堆。
2、堆排序动画演示
3、堆排序算法的基本思想
(1)用大顶堆排序的基本思想
① 先将初始文件R[1..n]建成一个大顶堆,此堆为初始的无序区 。
② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key 。
③ 由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
……
直到无序区只有一个元素为止。
(2)大顶堆排序算法的基本操作
① 建初始堆:将R[1..n]构造为初始堆;
② 堆调整:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆。
注意:
①只需做n-1趟排序,选出较大的n-1个关键字即可以使得文件递增有序。
②用小顶堆排序与利用大顶堆类似,只不过其排序结果是递减有序的。堆排序和直接选择排序相反:在任何时刻,堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止。
4、实现代码
//最大堆排序 public class MaxHeapProblem { public static void buildHeap(int a[]) { int heapSize = a.length; int filter = (int) Math.floor(heapSize / 2); // i从第一个非叶子结点开始 for (int i = filter - 1; i >= 0; i--) { heapAdjust(a, i, heapSize); } } // 已知H.r[i...heapSize]中记录的关键字除H.r[i]外,均满足最大堆结构 public static void heapAdjust(int arr[], int i, int heapSize) { // 当前待调整的元素 int tmp = arr[i]; // 该元素的左孩子 int index = 2 * i + 1; while (index < heapSize) { // 如果右孩子大于左孩子,则index+1,即交换右孩子和双亲节点 if (index + 1 < heapSize && arr[index] < arr[index + 1]) { index = index + 1; } if (arr[i] < arr[index]) { // 交换孩子和双亲节点 arr[i] = arr[index]; // 重新赋初值 i = index; index = 2 * i + 1; } // 已经是最大堆 else { break; } // 把双亲值赋给孩子节点 arr[i] = tmp; } } private void heapAdjust2(int[] arr, int i, int heapSize) { int maxIndex = i; if (2 * i + 1 <= heapSize - 1 && arr[2 * i + 1] > arr[i]) maxIndex = 2 * i + 1; if (2 * i + 2 <= heapSize - 1 && arr[i * 2 + 2] > arr[maxIndex]) maxIndex = 2 * i + 2; if (maxIndex != i) { int temp = arr[maxIndex]; arr[maxIndex] = arr[i]; arr[i] = temp; heapAdjust2(arr, maxIndex, heapSize); } } public static void heapSort(int a[]) { int heapSize = a.length; for (int i = heapSize - 1; i > 0; i--) { // 交换堆顶和最后一个元素 int tmp = a[0]; a[0] = a[i]; a[i] = tmp; // 在heapSize范围内根结点的左右子树都已经是最大堆,所以只需看新交换的堆顶元素是否满足最大堆结构即可。 // 将H.r[0...i]重新调整为最大堆 heapAdjust(a, 0, i); } } public static void main(String[] args) { int arr[] = new int[] { 6, 5, 3, 1, 8, 7, 2, 4 }; buildHeap(arr); System.out.println("初始建立的最大堆是:"); for (int data : arr) System.out.print(data + " "); System.out.println(); System.out.println("堆经过筛选调整后,排序结果为:"); heapSort(arr); for (int data : arr) System.out.print(data + " "); } }
5、复杂度分析
堆排序的平均时间复杂度和最坏时间复杂度均为:O(nlgn)
参考资料:
http://www.cnblogs.com/rollenholt/archive/2012/04/15/2450175.html