`
talentluke
  • 浏览: 604727 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

堆排序及其分析

 
阅读更多

摘自http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html

前言

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

堆排序过程

堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即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。

image

所以建堆的过程就是

   1: for ( i = headLen/2; i >= 0; i++)
   2:  
   3:        do AdjustHeap(A, heapLen, i)

调堆:如果初始数组是非降序排序,那么就不需要调堆,直接就满足堆的定义,此为最好情况,运行时间为Θ(1);如果初始数组是如图1.5,只有 A[0] = 1不满足堆的定义,经过与子节点的比较调整到图1.6,但是图1.6仍然不满足堆的定义,所以要递归调整,一直到满足堆的定义或者到堆底为止。如果递归调 堆到堆底才结束,那么是最坏情况,运行时间为O(h) (h为需要调整的节点的高度,堆底高度为0,堆顶高度为floor(logn) )。

建堆完成之后,堆如图1.7是个大根堆。将A[0] = 8 与 A[heapLen-1]交换,然后heapLen减一,如图2.1,然后AdjustHeap(A, heapLen-1, 0),如图2.2。如此交换堆的第一个元

素和堆的最后一个元素,然后堆的大小heapLen减一,对堆的大小为heapLen的堆进行调堆,如此循环,直到heapLen == 1时停止,最后得出结果如图3。

image

image

   1: /*
   2:     输入:数组A,堆的长度hLen,以及需要调整的节点i
   3:     功能:调堆
   4: */
   5:  
   6: void AdjustHeap(int A[], int hLen, int i)
   7: {
   8:     int left = LeftChild(i);  //节点i的左孩子
   9:     int right = RightChild(i); //节点i的右孩子节点
  10:     int largest = i;
  11:     int temp;
  12:  
  13:     while(left < hLen || right < hLen)
  14:     {
  15:         if (left < hLen && A[largest] < A[left])
  16:         {
  17:             largest = left;
  18:         }
  19:         
  20:         if (right < hLen && A[largest] < A[right])
  21:         {
  22:             largest = right;
  23:         }
  24:  
  25:         if (i != largest)   //如果最大值不是父节点
  26:         {
  27:              temp = A[largest]; //交换父节点和和拥有最大值的子节点交换
  28:              A[largest] = A[i];
  29:              A[i] = temp;
  30:  
  31:             i = largest;         //新的父节点,以备迭代调堆
  32:             left = LeftChild(i);  //新的子节点
  33:             right = RightChild(i);
  34:         }
  35:         else
  36:         {
  37:             break;
  38:         }
  39:     }
  40: }
  41:  
  42: /*
  43:     输入:数组A,堆的大小hLen
  44:     功能:建堆
  45: */
  46: void BuildHeap(int A[], int hLen)
  47: {
  48:     int i;
  49:     int begin = hLen/2 - 1;  //最后一个非叶子节点
  50:     for (i = begin; i >= 0; i--)
  51:     {
  52:         AdjustHeap(A, hLen, i);  
  53:     }
  54: }
  55:  
  56: /*
  57:     输入:数组A,待排序数组的大小aLen
  58:     功能:堆排序
  59: */
  60: void HeapSort(int A[], int aLen)
  61: {
  62:     int hLen = aLen;
  63:     int temp;
  64:  
  65:     BuildHeap(A, hLen);      //建堆
  66:  
  67:     while (hLen > 1)
  68:     {
  69:         temp = A[hLen-1];    //交换堆的第一个元素和堆的最后一个元素
  70:         A[hLen-1] = A[0];
  71:         A[0] = temp;
  72:         hLen--;        //堆的大小减一
  73:         AdjustHeap(A, hLen, 0);  //调堆
  74:     }
  75: }

性能分析

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

image

因此,建堆的运行时间是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)

 

 

从上述过程可知,堆排序其实也是一种选择排序,是一种树形选择排序。只不过直接选择排序中,为了从R[1...n]中选择最大记录,需比较n-1次,然后 从R[1...n-2]中选择最大记录需比较n-2次。事实上这n-2次比较中有很多已经在前面的n-1次比较中已经做过,而树形选择排序恰好利用树形的 特点保存了部分前面的比较结果,因此可以减少比较次数。对于n个关键字序列,最坏情况下每个节点需比较log2(n)次,因此其最坏情况下时间复杂度为 nlogn。堆排序为不稳定排序,不适合记录较少的排序。

 

 

参考http://www.cnblogs.com/dolphin0520/archive/2011/10/06/2199741.html

分享到:
评论

相关推荐

    堆排序算法分析及源代码

    通过阅读和分析这段源代码,你可以更深入地了解堆排序算法,并可能学习到如何在实际编程项目中应用这种排序方法。 总的来说,堆排序是一种重要的排序算法,它利用了二叉堆的特性实现了高效的排序。通过建堆、调整和...

    改进的堆排序算法及其复杂度分析改进的堆排序算法及其复杂度分析改进的堆排序算法及其复杂度分析

    改进的堆排序算法及其复杂度分析改进的堆排序算法及其复杂度分析改进的堆排序算法及其复杂度分析改进的堆排序算法及其复杂度分析

    ACM准备模板——堆排序模板

    在ACM(国际大学生程序设计竞赛)中,堆...总之,堆排序是ACM竞赛中不可或缺的工具之一,理解和掌握堆排序及其模板,能够提升你在竞赛中的竞争力。通过不断练习和应用,你将能够灵活运用堆排序解决各种复杂的排序问题。

    堆排序 c语言 演示

    这对于学习和理解堆排序原理及其具体实现非常有帮助。同时,这种可视化的方法也有助于初学者更直观地理解堆排序的工作机制。 通过以上分析可以看出,堆排序算法在C语言中的实现并不复杂,但其效率较高,在实际应用...

    堆排序算法原理

    ### 堆排序算法原理详解 #### 一、引言 在计算机科学中,排序算法是一种重要的数据处理技术,广泛应用于...通过本文的学习,我们了解了堆排序的基本原理及其具体实现过程,有助于在实际编程中更好地应用这一算法。

    堆排序算法实现

    ### 堆排序算法实现详解 #### 一、引言 堆排序是一种高效的排序方法,其核心在于构建...本文通过分析《算法导论》中的堆排序算法,并结合具体的C语言代码实现,详细介绍了堆排序的基本概念、实现步骤及具体实现细节。

    堆排序源代码

    根据题目提供的部分源代码,我们可以深入分析堆排序的具体实现细节。 ##### 3.1 变量声明 ```c #include #include int *a; int size; ``` - `#include&lt;stdio.h&gt;`:引入标准输入输出库。 - `#include&lt;stdlib.h&gt;`:...

    堆排序 数据结构 C语言

    根据给定文件的信息,我们可以提炼出以下几个核心知识点: ### 1. 堆排序的基本概念 堆排序是一种基于比较的排序算法,它...通过对上述代码的理解和学习,可以帮助我们更好地掌握堆排序算法的实现原理及其应用场景。

    堆排序简介及应用实例及实例分析.txt

    ### 堆排序复杂度分析 - **时间复杂度**: - 最好情况:O(nlogn) - 平均情况:O(nlogn) - 最坏情况:O(nlogn) - **空间复杂度**:O(1),因为堆排序是原地排序算法。 - **稳定性**:堆排序不是稳定的排序算法。 #...

    堆排序C++语言实现

    接下来,我们将详细讨论堆排序的原理及其C++实现。 ### 堆的概念 堆是一个近似完全二叉树的结构,分为大顶堆和小顶堆。在大顶堆中,每个节点的值都大于或等于其子节点的值;在小顶堆中,每个节点的值都小于或等于...

    堆排序代码

    ### 堆排序知识点解析 #### 一、堆排序简介 堆排序是一种基于比较的排序算法,它利用了堆这种特殊的树...通过上述分析我们可以了解到堆排序的基本原理、实现步骤及其应用领域,对于理解和掌握堆排序具有重要的意义。

    C语言实现堆排序的算法

    堆排序是一种高效的排序算法,基于比较的内部排序方法,它利用了数据结构中的“堆”这一概念。在C语言中实现堆排序,首先需要理解堆的性质:堆是一个完全二叉树,且满足堆的性质——父节点的值总是大于或等于(在最...

    堆排序及优先队列源代码_上机c++ 添加元素 删除最大节点

    ### 堆排序及优先队列的实现与应用 #### 一、堆排序的基本概念 ...通过以上分析,我们可以看到堆排序及优先队列在实际编程中的应用是非常广泛的,掌握其基本原理及实现方法对于提高编程能力非常有帮助。

    堆排序及算法分析PDF

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

    堆排序算法详解及其实现过程

    内容概要:本文详细介绍了堆排序算法的基本概念、建堆和排序过程,以及其...阅读建议:建议读者在理解二叉堆的基础上,跟随文章逐步实践建堆和排序的具体操作,同时注意分析时间和空间复杂度,以深入掌握堆排序的精髓。

    java堆排序

    #### 堆排序代码分析 提供的代码中定义了一个名为`HeapSort`的类,该类包含了主要的堆排序逻辑。`Sort`方法实现了堆排序的整体流程,`Adjust`方法负责维护最大堆的性质,`Display`方法用于输出数组状态。 #### 其他...

    六种内部排序算法比较:直接插入排序、希尔排序、冒泡排序、快速排序、选择排序、堆排序。

    本话题主要探讨六种内部排序算法:直接插入排序、希尔排序、冒泡排序、快速排序、选择排序以及堆排序。这六种排序算法各有优劣,适用于不同的场景,接下来我们将逐一进行详细阐述。 1. **直接插入排序**: 直接...

    数据结构堆排序的源代码

    #### 二、堆排序原理及实现 本节将根据提供的源代码详细解析堆排序的具体实现方法。 ##### 2.1 基础定义 堆是一种特殊的完全二叉树结构,其中每个父节点的键值都大于等于(对于大顶堆)或小于等于(对于小顶堆)...

Global site tag (gtag.js) - Google Analytics