当前位置:  编程技术>c/c++/嵌入式

深入理解堆排序及其分析

    来源: 互联网  发布时间:2014-10-15

    本文导语:  记得在学习数据结构的时候一味的想用代码实现算法,重视的是写出来的代码有一个正确的输入,然后有一个正确的输出,那么就很满足了。从网上看了许多的代码,看了之后貌似懂了,自己写完之后也正确了,但是不久之后...

记得在学习数据结构的时候一味的想用代码实现算法,重视的是写出来的代码有一个正确的输入,然后有一个正确的输出,那么就很满足了。从网上看了许多的代码,看了之后貌似懂了,自己写完之后也正确了,但是不久之后就忘了,因为大脑在回忆的时候,只依稀记得代码中的部分,那么的模糊,根本不能再次写出正确的代码,也许在第一次写的时候是因为参考了别人的代码,看过之后大脑可以进行短暂的高清晰记忆,于是欺骗了我,以为自己写出来的,满足了成就感。可是代码是计算机识别的,而我们更喜欢文字,图像。所以我们在学习算法的时候要注重算法的原理以及算法的分析,用文字,图像表达出来,然后当需要用的时候再将文字转换为代码。记忆分为三个步骤:编码,存储和检索,就以学习为例,先理解知识,再归纳知识,最后巩固知识,为了以后的应用而方便检索知识。

堆排序过程
堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。

既然是堆排序,自然需要先建立一个堆,而建堆的核心内容是调整堆,使二叉树满足堆的定义(每个节点的值都不大于其父节点的值)。调堆的过程应该从最后一个非叶子节点开始,假设有数组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。

所以建堆的过程就是

代码如下:

for ( i = headLen/2; i >= 0; i++)

        do AdjustHeap(A, heapLen, i)


建堆完成之后,堆如图1.7是个大根堆。将A[0] = 8 与 A[heapLen-1]交换,然后heapLen减一,如图2.1,然后AdjustHeap(A, heapLen-1, 0),如图2.2。如此交换堆的第一个元
素和堆的最后一个元素,然后堆的大小heapLen减一,对堆的大小为heapLen的堆进行调堆,如此循环,直到heapLen == 1时停止,最后得出结果如图3。

代码如下:

/*
     输入:数组A,堆的长度hLen,以及需要调整的节点i
     功能:调堆
 */

 void AdjustHeap(int A[], int hLen, int i)
 {
     int left = LeftChild(i);  //节点i的左孩子
     int right = RightChild(i); //节点i的右孩子节点
     int largest = i;
     int temp;

     while(left < hLen || right < hLen)
     {
         if (left < hLen && A[largest] < A[left])
         {
             largest = left;
         }

         if (right < hLen && A[largest] < A[right])
         {
             largest = right;
         }

         if (i != largest)   //如果最大值不是父节点
         {
              temp = A[largest]; //交换父节点和和拥有最大值的子节点交换
              A[largest] = A[i];
              A[i] = temp;

             i = largest;         //新的父节点,以备迭代调堆
             left = LeftChild(i);  //新的子节点
             right = RightChild(i);
         }
         else
         {
             break;
         }
     }
 }

 /*
     输入:数组A,堆的大小hLen
     功能:建堆
 */
 void BuildHeap(int A[], int hLen)
 {
     int i;
     int begin = hLen/2 - 1;  //最后一个非叶子节点
     for (i = begin; i >= 0; i--)
     {
         AdjustHeap(A, hLen, i); 
     }
 }

 /*
     输入:数组A,待排序数组的大小aLen
     功能:堆排序
 */
 void HeapSort(int A[], int aLen)
 {
     int hLen = aLen;
     int temp;

     BuildHeap(A, hLen);      //建堆

     while (hLen > 1)
     {
         temp = A[hLen-1];    //交换堆的第一个元素和堆的最后一个元素
         A[hLen-1] = A[0];
         A[0] = temp;
         hLen--;        //堆的大小减一
         AdjustHeap(A, hLen, 0);  //调堆
     }
 }

性能分析
•调堆:上面已经分析了,调堆的运行时间为O(h)。
•建堆:每一层最多的节点个数为n1 = ceil(n/(2^(h+1))),

因此,建堆的运行时间是O(n)。
•循环调堆(代码67-74),因为需要调堆的是堆顶元素,所以运行时间是O(h) = O(floor(logn))。所以循环调堆的运行时间为O(nlogn)。
总运行时间T(n) = O(nlogn) + O(n) = O(nlogn)。对于堆排序的最好情况与最坏情况的运行时间,因为最坏与最好的输入都只是影响建堆的运行时间O(1)或者O(n),而在总体时间中占重要比例的是循环调堆的过程,即O(nlogn) + O(1) =O(nlogn) + O(n) = O(nlogn)。因此最好或者最坏情况下,堆排序的运行时间都是O(nlogn)。而且堆排序还是原地算法(in-place algorithm)。


    
 
 

您可能感兴趣的文章:

  • 深入oracle特定信息排序的分析
  • 深入Java冒泡排序与选择排序的区别详解
  • 经典的委托排序(深入分析)
  • 深入IComparable与IComparer的排序实例详解
  • 快速排序的深入详解以及java实现
  • 深入单链表的快速排序详解
  • 深入C中常用的三种排序方法总结以及探讨分析
  • 深入理解linux内核
  • 问一个《深入理解计算机系统》中的问题
  • IP地址数字互转 iis7站长之家
  • 100分求:哪儿有《深入理解linux内核》可供下哉!
  • 哪儿有下载《深入理解Linux内核》这本书?(中文)
  • 有人读完《深入理解linux内核》吗?
  • 求一起看《深入理解linux内核》
  • 深入理解Java对象实例生成的例子
  • 深入理解计算机系统一书的一个问题
  • java父类和子类初始化顺序的深入理解
  • 深入Ref,Out的理解及其使用
  • 深入理解Oracle数据库的启动和关闭
  • 《现代操作系统》和《深入理解计算机系统》
  • CS:APP深入理解计算机系统练习题-【ELF文件的符号表相关】
  • 深入理解结构体中占位符的用法
  • 求支持,深入理解LINUX内核
  • 深入理解Activity之间的数据传递
  • 深入理解linux内核第三版中文版 不够可以再加分
  • C# 多态性的深入理解
  • 基于Java Tomcat和激活MyEclips的深入理解
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • Docker支持更深入的容器日志分析
  • 关于《深入浅出MFC》
  • Linux有没有什么好的高级的书,我要深入,
  • [100分]有没有关于binutils的深入的资料?或者深入底层的资料?
  • 想深入学习Java应该学习哪些东西
  • 哪位有《JSP深入编程》电子版?
  • 想要深入学习LINUX该学什么?
  • 如何深入Linux的内核学习?
  • U-BOOT得掌握到什么程序,用不用深入去学
  • 想深入了解操作系统该怎么做
  • 前一阵子学习了shell脚本,如果想深入点了解linux可以看什么书呢
  • 深入多线程之:深入分析Interlocked
  • ##想买书深入学习linux下的编程,请指教
  • 深入JDBC sqlserver连接写法的详解
  • 深入分析C中不安全的sprintf与strcpy
  • 请问JFC是否有必要深入研究?
  • 如何深入了解某个平台系统的底层
  • 各位,帮忙推荐几本深入学习Linux的书!!!
  • 想深入学习linux,请推荐一个版本的LINUX系统
  • 如何深入了解线程
  • 深入C++浮点数无效值定义与判定的解决办法


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3