第二部分 排序和顺序统计量 第 6 章 堆排序

排序算法
  插入排序最坏情况下可以再 θ(n2) 时间内将 n 个数排好序。但是,由于其内层循环紧凑,对于小规模输入,是一张非常快的原址排序(如果输入数组中仅有常数个元素需要在排序过程中存储在数组之外,则称排序算法是原址的)。归并排序不是原址的。
  第 6 章介绍的堆排序,是一种 θ(nlgn) 时间的原址排序算法。
  第 7 章介绍快速排序,也是一种原址排序,但最坏情况时间为 θ(n2) ,然而期望运行时间为 θ(nlgn) ,而且在实际应用中,通常比堆排序快。与插入排序类似,快速排序的代码也很紧凑,因此运行时间中隐含的常数系数很小。快速排序是排序大数组的最常用算法。
这里写图片描述
  顺序统计量
  一个n个数的集合的第 i 个顺序统计量就是集合中第 i 个小的数。当然,我们可以通过将输入集合排序,取输出的第 i 个元素来选择第 i 个顺序统计量。当不知道输入数据的分布时,这种方法的运行时间为Ω(nlgn),即第 8 章所证明的比较排序算法的下界。
  在第 9 章,我们展示了即使输入数据是任意实数,也可以在O(n)时间内找到第 i 个小的元素。我们提出了一种随机算法,其伪代码非常紧凑,它的最欢情况运行时间为 θ(n2) ,但期望运行时间为O(n)。
  

第 6 章 堆排序

  堆排序的时间复杂度是 θ(nlgn) ,但不同于归并排序的是,堆排序同样具有空间原址性:任何时候都只需要常数个额外的元素空间存储临时数据。因此,堆排序是集合了归并排序和插入排序两种排序算法的优点。
  堆排序引入了另一种算法设计技巧:使用一种我们称为“堆”的数据结构来进行信息管理。堆不仅用在堆排序中,而且它也可以构造一种有效的优先队列。

6.1 堆

  (二叉)堆是一个数组,它可以被看成一个近似的完全二叉树。树上的每一个节点对应数组中的一个元素。除了最底层外,该数是完全充满的,而且是从左向右填充。表示堆的数组A包括两个属性:A.length(通常)给出数组元素的个数,A.heap-size表示有多少个堆元素存储在该数组中。这里:0 <= A.heap-size <= A.length。
  这里写图片描述
  图6-1 以二叉树和数组形式展现的一个最大堆。每个节点圆圈内部的数字是它所存储的数据。节点上方的数字是它在数组中相应的下标。数组上方和下方的连线显示的是父子关系:父节点总是在它的孩子节点的左边。该数的高度为 3,下标为4(值为8)的节点的高度为1.
  
数的根节点是A[1],给定一个节点的下标i,可以很容易得到它的父节点、左孩子和右孩子的小标:

PARENT(i)
    return i/2
LEFT(i)
    return 2i
RIGHT(i)
    return 2i+1

  二叉树可以分为两种形式:最大堆和最小堆。在这两个堆中,节点的值都要满足堆的性质,但一些细节定义有所差异。在最大堆中,最大堆性质是指除了满足根以外的所有节点 i 都要满足:
  A[PARENT(i)]>=A[i]
也就是说,某个节点的值至多与其父节点一样大。因此,堆中的最大元素存放在根节点中。最小堆的组织方式正好相反:最小堆性质是指除了根以外的所有节点 i 都有
  A[PARENT(i)]>=A[i]
最小堆的最小元素存放在根节点中。
  在堆排序算法中,我们使用的是最大堆。最小堆通常用于构造优先队列。
  我们定义一个堆中的节点的高度就为该节点到叶节点最长简单路径上边的数目;进而,我们可以把堆的高度定义为根节点的高度。既然一个包含 n 个元素的队可以看做一颗完全二叉树,那么该堆的高度是θ(lg n)。我们会发现,堆结构上的一些基本操作的运行时间至多与数的高度成正比,即时间复杂度为O(lgn)。在本章剩余部分将介绍一些基本过程:
  

  • MAX-HEAPIFY过程:其时间复杂度为O(lgn),它是维护最大堆性质的关键。
  • BUILD-MAX-HEAP过程:具有线性时间复杂度,功能是从无序的输入数据数组中构造一个最大堆。
  • HEAPSORT:其时间复杂度为O(nlgn),功能是一个数组进行原址排序。
  • MAX-HEAP-INSERT、HEAP-EXTRACT-MAX、HEAP-INCREASE-KEY和HEAP-MAXIMUM过程:时间复杂度为O(lgn),功能是利用堆实现一个优先队列。

6.2 维护堆的性质

MAX-HEAPIFY是用于维护最大堆性质的重要过程,它的输入为一个数组A和一个下标 i。在调用MAX-HEAPIFY时,我们假定根节点为LEFT(i)和RIGHT(i)的二叉树都是最大堆,但这是A[i]有可能小于其孩子。MAX-HEAPIFY通过让A[i]的值逐级下降,从而使得下标 i 为根节点的子树重新遵循最大堆性质
MAX-HEAPIFY(A, i)

l = LEFT(i)
r = RIGHT(i)
if l <= A.heap-size and A[l] > A[i]
    largest = l
else largest = i
if r <= A.heap-size and A[r] > A[largest]
    largest = r
if largest != i
    exchangeA[i] with A[largest]
    MAX-HEAPY(A, largest)

我们用下面的递归式刻画MAX-HEAPIFY的运行时间:
T(n)<= T(2n/3) + θ(1)
根据主定理,上述的解为T(n)= O(lgn)。也就是说,对于一个树高为h的节点来说,MAX-HEAPIFY的时间复杂度为O(h)。

6.3 建堆

  我们可以用自底向上的方法利用MAX-HEAPIFY把一个大小为n=A.length的数组A[1,…,n]转换为最大堆。过程BUILD-MAX-HEAP对数中的其他节点都调用一次MAX-HEAPIFY。
BUILD-MAX-HEAP(A)

A.heap-size = A.length
for i = A.length/2 downto 1
    MAX-HEAPIFY(A,i)

BUILD-MAX-HEAP的时间复杂度为O(n)。因此,我们可以在线性时间内,把一个无序数组构造称为一个最大堆。
  类似地,我们也可以通过调用一个BUILD-MIN-HEAP构造一个最小堆。只需修改第 3 行的调用替换为MIN-HEAPIFY。

6.4 堆排序算法

  初始时候,堆排序算法利用BUILD-MAX-HEAP将输入数组A[1..n]建成最大堆,其中n = A.length。因为数组中的最大元素总在根节点A[1]中,通过把它与A[n]进行交换,可以让该元素放到正确的位置。这是,去掉节点n,剩余的节点中,原来的根的节点仍然是最大堆,而新的跟节点可能会违背最大堆的性质。为了维护最大堆的性质,调用MAX-HEAPIFY(A, 1),从而在A[1..n-1]上构造一个新的最大堆。堆排序算法会不断重复这一过程,知道堆的大小从n-1降到2.
HEAPSORT(A)

BUILD-MAX-HEAP(A)
for i = A.length downto 2
    exchange A[1] with A[i]
    A.heap-size = A.heap-size - 1
    MAX-HEAPIFY(A, 1)

HEAPSORT过程的时间复杂度是O(nlgn)。

6.5 优先队列

  和堆一样,优先队列也有两种形式:最大优先队列和最小优先队列。
  优先队列是一种用来维护由一组元素构成的结合S的数据结构,其中的每一个元素都有一个相关的值,称为关键字。一个最大优先队列支持以下操作:
  INSERT(S, x):把元素x插入集合S中。这一操作等价于S=SU{x}
  MAXIMUM(S):返回S中具有最大键字的元素。
  EXTRACT-MAX(S):去掉并返回S中的具有最大键字的的元素。
  INCREASE-KEY(S,x,k):将元素x的关键字值增加到k,这里假设k的值不小于x的原关键字值。
  最大优先队列的应用有很多,其中一个就是在共享计算机系统的作业调度。
  现在我们来讨论如何实现最大优先队列的操作。过程HEAP-MAXIMUM可以在θ(1)时间内实现MAXIMUM操作。
HEAP-MAXIMUM(A)  

return A[1]

过程HEAP-EXTRACT-MAX实现EXTRACT-MAX操作。
HEAP-EXTRACT-MAX(A)

if A.heap-size < 1
    errror "heap underflow"
max = A[1]
A[1] = A[A.heap-size]
A.heap-size = A.heap-size - 1
MAX-HEAPIFY(A, 1)
return max

HEPA-INCREASE-KEY能够实现INCREASE-KEY操作。
HEPA-INCRESE-KEY(A,i,key)

if key < A[i]
    error"new key is smaller than current key"
A[i] = key;
while i > 1 and A[PARENT(i)] < A[i]
    exchange A[i] with A[PARENT(i)]
    i = PARENT(i)

MAX-HEAP-INSERT能够实现INSERT操作。
MAX-HEAP-INSERT(A,key)

A.heap-size = A.heap-size + 1
A[A.heap-size] = -
HEAP-INCREASE-KEY(A, A.heap-size, key)

在包含n个元素的堆上,MAX-HEAP-INSERT的运行时为O(lgn)。
总之,在一个包含n个元素的堆中,所有优先队列的操作都可以在O(lgn)时间内完成。
练习:
1. 当用数组表示存储n个元素的堆时,叶节点的下标分别为⌊n/2⌋+1,⌊n/2⌋+2, … ,⌊n/2⌋+n.
2. 对于任一包含n个元素的堆中,至多有 n/2h+1 个高度为h的节点。
3.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、本书的内容 目前,市面上有关计算机算法的书很多,有些叙述严谨但不全面,另外一些则是容很大但不够严谨。本书将叙述的严谨性以及内容的深度和广度有机地结合了起来。第1版推出后,即在世界范围内受到了广泛的欢迎,被各高等院校用作多种课程的教材和业界的标准参考资料。它深入浅出地介绍了大的算法及相关的数据结构,以及用于解决一些复杂计算问题的高级策略(如动态规划、贪心算法、平摊分析等),重点在于算法的分析和设计。对于每一个专题,作者都试图提供目前最新的研究成果及样例解答,并通过清晰的图示来说明算法的执行过程。. 本书是原书的第2版,在第1版的基础之上增加了一些新的内容,涉及算法的作用、概率分析和随机化算法、线性规划,以及对第1版详尽的、几乎涉及到每一小节的修订。这些修订看似细微,实际上非常重要。书引入了“循环不变式”,并贯穿始终地用来证明算法的正确性。在不改动数学和分析重点的前提下,作者将第1版的许多数学基础知识从第一部分移到了附录。 二、本书的特点 本书在进行算法分析的过程,保持了很好的数学严谨性。书的分析和设计可以被具有各种水平的读者所理解。相对来说,每一都可以作为一个相对独立的单元来教授或学习。书的算法以英语加伪代码的形式给出,只要有一点程序设计经验的人都能读懂,并可以用任何计算机语言(如C/C++和Java等)方便地实现。在书,作者将算法的讨论集在一些比较现代的例子上,它们来自分子生物学(如人类基因项目)、商业和工程等领域。每一小节通常以对相关历史素材的讨论结束,讨论了在每一算法领域的原创研究。 本书的特点可以概括为以下几个方面: 1.概念清晰,广度、深度兼顾。 本书收集了现代计算机常用的数据结构和算法,并作了系统而深入的介绍。对涉及的概念和背景知识都作了清晰的阐述,有关的定理给出了完整的证明。 2.“五个一”的描述方法。 本书以相当的深度介绍了许多常用的数据结构和有效的算法。编写上采用了“五个一”,即一介绍一个算法、一种设计技术、一个应用领域和一个相关话题。.. 3.图文并茂,可读性强。 书的算法均以通俗易懂的语言进行说明,并采用了大插图来说明算法是如何工作的,易于理解。 4.算法的“伪代码”形式简明实用。 书的算法均以非常简明的“伪代码”形式来设计,可以很容易地把它转化为计算机程序,直接应用。 注重算法设计的效率,对所有的算法进行了仔细、精确的运行时间分析,有利于进一步改进算法。 三、本书的用法 本书对内容进行了精心的设计和安排,尽可能考虑到所有水平的读者。即使是初学计算机算法的人,也可以在本书找到所需的材料。 每一都是独立的,读者只需将注意力集到最感兴趣的节阅读。 1.适合作为教材或教学参考书。 本书兼顾通用性与系统性,覆盖了许多方面的内容。本书不但阐述通俗、严谨,而且提供了大练习和思考题。针对每一节的内容,都给出了数和难度不等的练习题。练习题用于考察对基本内容的掌握程度,思考题有一定的难度,需进行精心的研究,有时还通过思考题介绍一些新的知识。 前言回到顶部↑本书提供了对当代计算机算法研究的一个全面、综合性的介绍。书给出了多个算法,并对它们进行了较为深入的分析,使得这些算法的设计和分析易于被各个层次的读者所理解。力求在不牺牲分析的深度和数学严密性的前提下,给出深入浅出的说明。. 书每一都给出了一个算法、一种算法设计技术、一个应用领域或一个相关的主题。算法是用英语和一种“伪代码”来描述的,任何有一点程序设计经验的人都能看得懂。书给出了230多幅图,说明各个算法的工作过程。我们强调将算法的效率作为一种设计标准,对书的所有算法,都给出了关于其运行时间的详细分析。 本书主要供本科生和研究生的算法或数据结构课程使用。因为书讨论了算法设计的工程问题及其数学性质,因此,本书也可以供专业技术人员自学之用。 本书是第2版。在这个版本里,我们对全书进行了更新。所做的改动从新增了若干,到个别语句的改写。 致使用本书的教师 本书的设计目标是全面、适用于多种用途。它可用于若干课程,从本科生的数据结构课程到研究生的算法课程。由于书给出的内容比较多,只讲一学期一般讲不完,因此,教师们应该将本书看成是一种“缓存区”或“瑞典式自助餐”,从挑选出能最好地支持自己希望教授的课程的内容。 教师们会发现,要围绕自己所需的各个节来组织课程是比较容易的。书的各都是相对独立的,因此,你不必担心意想不到的或不必要的各之间的依赖关系。每一都是以节为单位,内容由易到难。如果将本书用于本科生的课程,可以选用每一的前面几节内容;在研究生课程,则可以完整地讲授每一。 全书包含920多个练习题和140多个思考题。每一节结束时给出练习题,每一结束时给出一些
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值