【转】算法之堆排序

http://blog.csdn.net/SJF0115/article/details/8610201

前序:

(二叉)堆数据结构是一种数组对象,它可以被视为一棵完全二叉树。树中每个节点与数组中存放该节点值的那个元素对应。

树的每一层都是填满的,最后一层除外。

树的根为a[1] (在这里是从1开始的,也可以从0开始),给定了某个节点的下标i,其父节点为i/2,左二子为2*i,右儿子为2*i+1。

二叉堆满足二个特性:

1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。

2.每个结点的左子树和右子树都是一个二叉堆(最大堆或最小堆)。

当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。

当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆。

保持堆的性质:

MaxHeap是对最大堆进行操作的最重要的子程序。

以i为根的子树:

在算法每一步中,从a[i], a[Left(i)], a[Right(i)]找出最大值,并将其下标存在LargestIndex中。如果a[i]是最大的,则以i为根的子树已是最大堆,程序结束。

否则i的某个子结点中有最大元素则交换a[i],a[LargetIndex],从而使i及子女满足堆性质。下标为LargestIndex的结点在交换后的值为a[i],以该结点为根的子树又有可能违反最大堆的性质,因而又要对该子树递归调用MaxHeap,重新使子树平衡。

[cpp] view plaincopy

  1. //调整以index为根的子树
  2. //n:堆中元素个数
  3. int MaxHeap(int a[],int index,int n){
  4. int LargestIndex = index;
  5. //左子节点
  6. int LeftIndex = 2*index;
  7. //右子节点
  8. int RightIndex = 2*index+1;
  9. if(LeftIndex <= n && a[LeftIndex] > a[LargestIndex]){
  10. LargestIndex = LeftIndex;
  11. }
  12. if(RightIndex <= n && a[RightIndex] > a[LargestIndex]){
  13. LargestIndex = RightIndex;
  14. }
  15. //如果a[index]是最大的,则以index为根的子树已是最大堆否则index的子节点有最大元素
  16. //则交换a[index],a[LargetIndex],从而使index及子女满足堆性质
  17. int temp;
  18. if(LargestIndex != index){
  19. //交换a[index],a[LargetIndex]
  20. temp = a[index];
  21. a[index] = a[LargestIndex];
  22. a[LargestIndex] = temp;
  23. //重新调整以LargestIndex为根的子树
  24. MaxHeap(a,LargestIndex,n);
  25. }
  26. return 0;
  27. }

建堆:

我们可以自底向上的用MaxHeap来将一个数组a[1-n]变成一个最大堆,子数组a[n/2+1,........n]中的元素是树中的叶子,因此每个都可以看做只含一个元素的堆,满足最大堆的要求,不用调整。所以只需调整以a[n/2........1]中元素为根的子树使之成为最大堆。

[cpp] view plaincopy

  1. //建堆:将一个数组a[1-n]变成一个最大堆
  2. int BuildMaxHeap(int a[],int n){
  3. int i;
  4. //子数组a[(n/2+1,n/2+2......n)]中的元素都是树中的叶子
  5. for(i = n/2;i >= 1;i--){
  6. //调整以i为根节点的树使之成为最大堆
  7. MaxHeap(a,i,n);
  8. }
  9. return 0;
  10. }

a数组

16 7 3 20 17 8

初始堆:

自底向上从最后一个非叶节点开始调整:

(a)                                                    (b)                                                 (c)                                                   (d)

每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)。

堆排序:

开始时,堆排序先用BuildMaxHeap将输入数组a[1-n]构造成一个最大堆。又因为数组中最大元素在根a[1],则可以通过它与a[n]交换来达到最终的正确位置。

现在,如果从堆中”去掉“结点n(不是真的删除,而是通过修改堆的元素个数n),可以很容易的将a[1-(n-1)]建成最大堆。原来根的子女依旧是最大堆,二新交换的根元素很有可能违背最大堆的性质。这时调用MaxHeap重新调整一下。在a[1-(n-1)]中构造出最大堆。堆排序不断重复这一过程,堆的大小由n-1一直降到2.从而完成排序的功能

[cpp] view plaincopy

  1. //堆排序
  2. int HeapSort(int a[],int n){
  3. int temp;
  4. //BulidMaxHeap将输入数组构造一个最大堆
  5. BuildMaxHeap(a,n);
  6. //数组中最大元素在根a[1],则可以通过它与a[n]交换来达到最终的正确位置
  7. for(int i = n;i >= 2;i--){
  8. //交换
  9. temp = a[i];
  10. a[i] = a[1];
  11. a[1] = temp;
  12. //a[i]已达到正确位置,从堆中去掉
  13. n--;
  14. //重新调整,保持最大堆的性质
  15. MaxHeap(a,1,n);
  16. }
  17. return 0;
  18. }

(a)                                                   (b)                                               (c)                                                (d)

(e)                                                    (f)                                                    (g)

(h)                                              (i)                                                  (j)                                                (k)

红色为排序后的结果;

代码:

[cpp] view plaincopy

    1. #include<stdio.h>
    2. #include<stdlib.h>
    3. //调整堆
    4. int MaxHeap(int a[],int index,int n){
    5. int LargestIndex = index;
    6. //左子节点
    7. int LeftIndex = 2*index;
    8. //右子节点
    9. int RightIndex = 2*index+1;
    10. if(LeftIndex <= n && a[LeftIndex] > a[LargestIndex]){
    11. LargestIndex = LeftIndex;
    12. }
    13. if(RightIndex <= n && a[RightIndex] > a[LargestIndex]){
    14. LargestIndex = RightIndex;
    15. }
    16. //如果a[index]是最大的,则以index为根的子树已是最大堆否则index的子节点有最大元素
    17. //则交换a[index],a[LargetIndex],从而使index及子女满足堆性质
    18. int temp;
    19. if(LargestIndex != index){
    20. //交换a[index],a[LargetIndex]
    21. temp = a[index];
    22. a[index] = a[LargestIndex];
    23. a[LargestIndex] = temp;
    24. //重新调整以LargestIndex为根的子树
    25. MaxHeap(a,LargestIndex,n);
    26. }
    27. return 0;
    28. }
    29. //建堆:将一个数组a[1-n]变成一个最大堆
    30. int BuildMaxHeap(int a[],int n){
    31. int i;
    32. //子数组a[(n/2+1,n/2+2......n)]中的元素都是树中的叶子
    33. for(i = n/2;i >= 1;i--){
    34. //调整以i为根节点的树使之成为最大堆
    35. MaxHeap(a,i,n);
    36. }
    37. return 0;
    38. }
    39. //堆排序
    40. int HeapSort(int a[],int n){
    41. int temp;
    42. //BulidMaxHeap将输入数组构造一个最大堆
    43. BuildMaxHeap(a,n);
    44. //数组中最大元素在根a[1],则可以通过它与a[n]交换来达到最终的正确位置
    45. for(int i = n;i >= 2;i--){
    46. //交换
    47. temp = a[i];
    48. a[i] = a[1];
    49. a[1] = temp;
    50. //a[i]已达到正确位置,从堆中去掉
    51. n--;
    52. //重新调整,保持最大堆的性质
    53. MaxHeap(a,1,n);
    54. }
    55. return 0;
    56. }
    57. int main(){
    58. int n = 6;
    59. //a[0]不用,堆的根结点是从1开始的
    60. int a[] = {0,3,17,8,7,16,20};
    61. HeapSort(a,n);
    62. for(int i = 1;i <= n;i++){
    63. printf("%d ",a[i]);
    64. }
    65. return 0;
    66. }
时间: 2024-10-17 12:25:16

【转】算法之堆排序的相关文章

排序算法系列——堆排序

记录学习点滴,菜鸟成长记 堆排序引入了另一种算法设计技巧:使用一种我们称之为“堆”的数据结构来进行数据管理. 堆排序算是真正意义上的利用数据结构来求解数组排序的方法. “插入排序”和“归并排序”可以看做是一种“计算机体力活”,体现的思想更多的是去模拟最简单的人类思维,比如插入排序过程中的比较,归并中子问题合并时的比较. “堆排序”可以看做是“计算机脑力活”,他利用了一种结构化的语言来表达,这种结构化带来一些性质,比如左右孩子.比[堆大小的一半向下取整]大的下标都是叶节点不需要维护其最大堆性质等.

【数据结构与算法】堆排序

堆排序的时间复杂度是O(nlogn),下面上代码 public class HeapSort { public void adjustHeap(int[] in, int index, int length) { int leftcIndex = index * 2 + 1; int rightcIndex = index * 2 + 2; int bigest = index; while (leftcIndex < length || rightcIndex < length) { if

常用算法之----堆排序

预备知识 堆排序 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序.首先简单了解下堆结构. 堆 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆:或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆.如下图: 同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子 该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是: 大顶

排序算法之堆排序(优先队列)

1.堆排序的堆,其实是一个 完全二叉树.既是一个结点要么是叶子结点,要么必定有左右两个子节点的树. 2.堆有序:每个结点的值,都必须大于两个子节点.但是两个子结点的大小不作要求. 3.一棵大小为N的完全二叉树,高度为lgN(层). 用数组实现堆,假设数组下标从0开始,下标为k的元素,它的左子树是2k+1,右子树是左子树+1,即2k+2 一:由上至下的有序化(下沉) 如果堆的有序状态,因为某个结点比它的两个子结点或者其中之一小而打破了,那么可以通过与两个子结点中的较大者来交换. 交换后可能会在子结

【算法设计-堆排序】大根堆排序

1.堆排序不仅拥有与归并排序一样的时间复杂度为o(nlgn),而且空间复杂度所占秩序额外的几个元素空间,这个又有插入排序的优势. 2.堆排序分为3部分,第一部分是保持堆的性质的函数MAX_HEAPIFY函数,用于保持堆的性质:父结点的值要大于子节点. 第二部分是创建大根堆的函数,BUILD_MAX_HEAP这个函数从第一个非叶节点开始进行MAX_HEAPIFY函数.最终创建一个大根堆. 第三部分是堆排序部分,输出一个最大值KEY[1],然后将最后一个值替换第一个值,重复进行. 代码如下: #in

必须知道的八大种排序算法【java实现】(三) 归并排序算法、堆排序算法详解

一.归并排序算法 基本思想: 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的.然后再把有序子序列合并为整体有序序列. 归并排序示例: 合并方法: 设r[i-n]由两个有序子表r[i-m]和r[m+1-n]组成,两个子表长度分别为n-i +1.n-m. j=m+1:k=i:i=i; //置两个子表的起始下标及辅助数组的起始下标 若i>m 或j>n,转⑷ //其中一个子表已合并完,比较选取结束 //选取r[i]和r[j]

排序算法之堆排序(Heapsort)解析

一.堆排序的优缺点(pros and cons) (还是简单的说说这个,毕竟没有必要浪费时间去理解一个糟糕的的算法) 优点: 堆排序的效率与快排.归并相同,都达到了基于比较的排序算法效率的峰值(时间复杂度为O(nlogn)) 除了高效之外,最大的亮点就是只需要O(1)的辅助空间了,既最高效率又最节省空间,只此一家了 堆排序效率相对稳定,不像快排在最坏情况下时间复杂度会变成O(n^2)),所以无论待排序序列是否有序,堆排序的效率都是O(nlogn)不变(注意这里的稳定特指平均时间复杂度=最坏时间复

排序算法之堆排序

1. 堆排序的思想 输入一个数组,利用一组二叉树的操作使其变成有序的数组,就是堆排序 堆排序利用的是二叉树的思想,操作对象是数组,所以数组需要在逻辑上映射到二叉树上,由于数组的下标是连续的,而二叉树中只有完全二叉树和满二叉树是连续的,所以将数组元素逐个映射到完全二叉树上,然后配备一系列的操作即可.例如数组data[]={9,6,5,4,3,2,1,7},映射到完全二叉树上如下图所示. 2.堆排序的过程 还是用上面的data数组作为输入数组,映射到完全二叉树如上图所示,怎么利用二叉树的性质,才能使

算法学习之排序算法:堆排序

要了解堆排序,首先要了解堆的概念,因为本文主要研究堆排序的算法,此处对数据结构堆只是给出概念:n个元素的序列{k1,k2,...kn},当且仅当满足如下关系时,称之为堆. k[i] <= k[2i]且k[i] <= k[2i+1] (或 k[i] >= k[2i]且k[i] >= k[2i+1]) 比如:序列96.83.27.38.11.09(或12.36.24.85.47.30.53.91)都是堆. 如果将堆对应的一维数组看成是一个二叉树,则堆的含义表明:完全二叉树中所有非终端结