堆排序及数据结构中的堆

数据结构-堆

说起堆,程序员一般都会想到:堆排序/堆栈(其实就是栈)/堆内存~

它们之间有什么区别呢?堆这个数据结构又可以干啥呢?

堆内存VS数据结构中的堆

数据结构中的堆是一个完全二叉树,常用来实现堆排优先级队列等;

而堆内存是内存中的堆,我们在C++编程的时候,常说通过malloc()或者new()来申请内存,程序员对这块内存进行管理,意思就是对这块堆内存管理

它们是两个概念哦,基本可以认为没有交集(那么为啥会叫同一个名字呢?)

Why are two different concepts both called “heap”?

Donald Knuth says (The Art of Computer Programming, Third Ed., Vol. 1, p. 435):

Several authors began about 1975 to call the pool of available memory a “heap.”
He doesn’t say which authors and doesn’t give references to any specific papers, but does say that the use of the term “heap” in relation to priority queues is the traditional sense of the word.

意思就是使用数据结构heap或者priority queues找到了最小内存块
早期的Lisp就是使用数据结构堆来做内存池的~

Wikipedia claims that it’s because at an early stage Lisp used a heap (data structure) to implement its memory store. It doesn’t say how. Its reference is “Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest (1990): Introduction to algorithms. MIT Press / McGraw-Hill.”, which I don’t have. – Steve Jessop

[1] 参考知乎

数据结构堆的概念

(二叉)堆数据结构是一种数组对象,如下图所示,它可以被视为一颗完全二叉树。树中每个节点与数组中存放结点值的那个元素对应。
来源《算法导论》
最大堆: 除了根(Root)以外的每个结点i,都有:
A [ P A R E N T ( i ) ] > = A [ i ] A[PARENT(i)]>=A[i] A[PARENT(i)]>=A[i]
用人话说就是:从大到小顺着树排序

最大堆一般用于堆排序。

最小堆:除了根(Root)以外的每个结点i,都有:
A [ P A R E N T ( i ) ] < = A [ i ] A[PARENT(i)]<=A[i] A[PARENT(i)]<=A[i]
用人话说就是:从小到大顺着树排序

最小堆一般用于构造优先队列。

保持堆的性质

先来个时间复杂度总结,后续给分析:
来源《算法导论》
MAX-HEAPIFY是对最大堆进行操作的重要子程序。其输入为一个数组A和下标i。伪代码如下:

MAXHeapify(A,i)
	l<-LEFT(i)
	r<-RIGHT(i)
	if l<=heapsize[A] and A[l]>A[i]
		then largest<-l
		else largest<-i
	if r<=heapsize[A] and A[r]>A[largest]
		then largest<-r
	if largest != i
		then exchange A[i]<->A[largest]
		Maxheapify(A,largest)

来源《算法导论》

建堆

建堆的过程就是自底向上地用MAX-HEAPIFY来将一个数组A[1…n]变成一个最大堆。过程BUILD-MAX-HEAP对树中的每一个其他结点都调用一次MAX-HEAPIFY:

BUILD-MAX-HEAP(A)
	heapsize[A]<-length[A]
	for i<-[length[A]/2]downto 1
	do MAX-HEAPIFY(A,i)

注意,为什么下标i从[length(A)/2]递减到1呢?~
因为对于堆来说,A[length(A)/2+1]一直到A[length(A)]都是叶子结点,可以不用对它们进行遍历。

来源《算法导论》

堆排序

首先把原始数组搞成最大堆,然后对这个最大堆进行排序操作

伪代码如下:

HEAPSORT(A)
	BUILD-MAX-HEAP(A)
	for i<-length[A] downto 2
		do exchange A[1]<->A[i]
		heapsize[A]<-heapsize[A]-1
		MAX-HEAPIFY(A,1)

本博主行走江湖,遵从的就是一个“有图就不多BB”字,来啊小二,上图~
来源自画
自从做了码农,字越来越丑.jpg

优先级队列

虽然堆排序算法是一个漂亮的算法,但在实际中,快速排序的一个好的实现往往优于堆排序。堆的一个很常见的应用是优先级队列,这里我们拿最大堆实现的最大优先级队列举例:

最大优先级队列的一个应用是在一台分时计算机上进行作业调度。这种队列对要执行的各作业及它们之间的相对优先关系加以记录。当一个作业做完或被中断时,用EXTRACT-MAX操作从所有等待的作业中,选择出具有最高优先级的作业。在任何时候,一个新作业都可以用INSERT加入到队列中。
来源《算法导论》
来show一下EXTRACT-MAX(S)的伪代码:

HEAP-EXTRACT-MAX(A)
	if heapsize[A]<1
		then error"heap underflow"
	max<-A[1]
	A[1]<-A[heapsize[A]]
	heapsize[A]<-heapsize[A]-1
	MAX-HEAPIFY(A,1)
	return max

所以这个操作的时间复杂度是 O ( l g n )   O(lg n)~ O(lgn) 

再来show一下INCREASE-KEY(S,x,k)的伪代码:

HEAP-INCREASE-KEY(A,i,key)
	if key<A[i]
		then error"new key is smaller than current key"
	A[i]<-key
	while i>1 and A[PARENT(i)]<A[i]
		do exchange A[i]<->A[PARENT(i)]
		i<-PARENT(i)

有图就不多BB,来看看图吧:
来源《算法导论》
对堆进行MAX-HEAP-INSERT操作的伪代码如下,程序首先加入一个关键字为 − ∞ -\infty 的叶结点来扩展最大堆,然后调用HEAP-INCREASE-KEY来设置新结点的关键字的正确值,并保持最大堆性质

MAX-HEAP-INSERT(A,key)
	heapsize[A]<-heapsize[A]+1
	A[heapsize[A]]<--无穷
	HEAP-INCREASE-KEY(A,heapsize[A],key)

所以这个操作的时间复杂度是 O ( l g n )   O(lg n)~ O(lgn) 

实现堆排序算法C++代码看我的github

么么哒~

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 终极编程指南 设计师:CSDN官方博客 返回首页