《算法导论》学习总结 — 5.第六章(2) 优先级队列

建议先看看前言:http://www.wutianqi.com/?p=2298

上一章总结是的堆排序算法,这一章同样是利用了堆这种数据结构,实现在是优先级队列

根据堆分为最大堆,最小堆,所以优先级队列也可以分为最大优先级队列最小优先级队列

优先级队列的概念和用途书上已经写的很清楚了,我就不再打一遍了。直接写出具体实现。

在实现前先说几点:

1.上一章说过,堆的length和heapsize要区分清楚,这一章的优先级队列里就用到了。

2.优先级队列用到了上一章的一些函数比如MaxHeapify(),不记得的可以复习下上一章。

以下是代码及讲解(此处实现的是最大优先级队列):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// 堆应用之优先级队列
// Tanky Woo
// Blog: www.WuTianQi.com
#include <iostream>
using namespace std;
const int INF = 999999;
 
/////////////////////////////////////////////////////////
////////////// 以下代码在堆排序中已讲解过 ///////////////
void MaxHeapify(int *a, int i, int len)
{
	int lt = 2*i, rt = 2*i+1;
	int largest;
	if(lt <= len && a[lt] > a[i])
		largest = lt;
	else
		largest = i;
	if(rt <= len && a[rt] > a[largest])
		largest = rt;
	if(largest != i)
	{
		int temp = a[i];
		a[i] = a[largest];
		a[largest] = temp;
		MaxHeapify(a, largest, len);
	}
}
 
void BuildMaxHeap(int *a, int size)
{
	for(int i=size/2; i>=1; --i)
		MaxHeapify(a, i, size);
}
 
void PrintArray(int data[], int size)
{
    for (int i=1; i<=size; ++i)
        cout <<data[i]<<"  ";
    cout<< endl << endl;
}
////////////////////////////////////////////////////////////
 
// 返回具有最大关键字的元素
int HeapMaximum(int *a)
{
	return a[1];
}
 
// 去掉并返回具有最大关键字的元素
// 注意:这里每次MaxHeapify的是heapsize
int HeapExtractMax(int *a, int &heapsize)
{
	if(heapsize < 1)
		cout << "Heap Underflow" << endl;
	int max = a[1];
	a[1] = a[heapsize];
	--heapsize;
	MaxHeapify(a, 1, heapsize);
	return max;
}
 
// 将元素a[i]的值增加到key
void HeapIncreaseKey(int *a, int i, int key)
{
	if(key < a[i])
		cout << "New key is smaller than current key" << endl;
	a[i] = key;
	while(i > 1 &&a[i/2] < a[i])
	{
		int temp = a[i];
		a[i] = a[i/2];
		a[i/2] = temp;
		i /= 2;
	}
}
 
// 插入关键字为key的元素
void MaxHeapInsert(int *a, int key, int &heapsize)
{
	++heapsize;
	a[heapsize] = -INF;
	HeapIncreaseKey(a, heapsize, key);
}
 
 
 
int main()
{
	int len, heapsize;
	int arr[100] = {0, 15, 13, 9, 5, 12, 8, 7, 4, 0, 6, 2, 1};
	// 区别len 和 heapsize
	// heapsize是堆的大小,而len是初始数组的总大小。
	len = heapsize = 12;
 
	// 首先建堆
	BuildMaxHeap(arr, len);
	cout << "建堆后: " << endl;
	PrintArray(arr, len);
 
	// 使用HeapMaximum
	cout << "当前最大的元素是: " << endl;
	cout << HeapMaximum(arr) << endl << endl;
 
	// 使用HeapExtractMax
	cout << "使用HeapExtractMax后: " << endl;
	HeapExtractMax(arr,heapsize);
	PrintArray(arr, heapsize);
 
	// 再次使用HeapExtractMax
	cout << "再次使用HeapExtractMax后: " << endl;
	HeapExtractMax(arr,heapsize);
	PrintArray(arr, heapsize);
 
	// 使用HeapIncreaseKey
	cout << "使用HeapIncreaseKey后: " << endl;
	HeapIncreaseKey(arr, 2, 15);
	PrintArray(arr, heapsize);
 
	// 使用MaxHeapInsert
	cout << "使用MaxHeapInsert后: " << endl;
	MaxHeapInsert(arr, 28, heapsize);
	PrintArray(arr, heapsize);
}

以下是运行结果:

youxianji

看上图的结果:

在第二次使用HeapExtractMax后,把第二个数字即6设为15,更新后,结果就是HeapIncreaseKey的输出。

Tanky Woo 标签:

《算法导论》学习总结 — 4.第六章(1) 堆排序

建议先看看前言:http://www.wutianqi.com/?p=2298

首先介绍几个概念:

卫星数据:一个带排序的的数通常是有一个称为记录的数据集组成的,每一个记录有一个关键字key,记录的其他数据称为卫星数据。

原地排序:在排序输入数组时,只有常数个元素被存放到数组以外的空间中去。

在第二章介绍了两种排序:插入排序和合并排序,接下来两章要介绍的是推排序和快速排序,这四个排序都属于比较排序(comparison sort)。

我以前总结过堆排序,并具体实现了堆排序,代码中给出了详细的注释,所以在这里就不重复发了,大家可以去看看,个人觉得总结的还是比较给力的:

http://www.wutianqi.com/?p=1820

这里再补充几点:

1.区别length[A]和heap-sort[A]。(P73)(这个在下一篇的优先级队列中将会具体区别)

2.总体上看堆排序由三个函数组成:①.MAX-HEAPIFY ②.BUILD-MAX-HEAP ③.HEAP-SORT

另外,在这里给大家补充一点个人经验,有时理论难以理解,代码难以理解,这个时候,就要靠秘诀了:拿起手中的笔和纸,自己给出一组输入,按照书上的代码,自己去模拟这组输入的执行过程。(这个过程人人都知道,但并不是人人都去做了!学算法,就要自己去模拟,去画图,去推!怎么样容易理解就怎么去做!)

所以这也是我喜欢《算法导论》的原因,接下来,就要强烈推荐大家看《算法导论》上非常非常给力的堆排序实现图了—图6-4。

总结:本章最基础也是最重要的就是理解堆这种结构!

堆是什么?来看看《算法导论》上的图6-1:

dui

图(a)是一个最大堆,图(b)是最大堆的数组表示。可以看到堆的数组并不是已排序好的。

让我们来回忆下最大堆的定义(P74):

在最大堆中,最大堆特性是指除了根以外的每个结点i,有A[PARENT(i)] >= A[i]。这样,堆的最大元素就存放在根结点中。

对,堆排序就是利用的这个特性—“堆的最大元素就存放在根结点中”

每次堆化,这样就找到了当前堆的最大元素。

所以说,理解了其本质特征,堆排序其实很简单的。

至于堆排序的具体应用,在后面的最短路算法—Dijkstra中,会用到由堆来优化普通的Dijkstra算法。

下一篇将实现最大优先级队列。

Tanky Woo 标签:

堆排序基础讲解(代码+注释)

首先,推荐一下《算法导论》的第六章—堆排序,在网上找了很多资料,发现还是这本圣经最给力。大家学堆排序一定要去看看,不然是一种浪费。如果大家没有,可以去网上下载英文版(chm版)的,既清晰又适合阅读。

其实堆排序的讲解网上很多,而且基本都一样,不过我还是把一些基本概念写出来:

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

二叉堆有两种:最大堆和最小堆(小根堆)。

最大堆:所有节点的子节点比其自身小的堆。
最小堆:所有节点的子节点比其自身大的堆。

堆排序:堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单

在堆排序算法中,使用的是最大堆,最小堆通常在构造优先级队列时使用。

再次提醒大家,去看看《算法导论》里的第六章-堆排序。因为书上讲的太详细了,所以我也就不再多说。

这里我把《算法导论》上的伪代码用C/C++实现了,每个函数我都用自己的理解写出来了,如果大家还不懂,可以留言。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/*
* Author: Tanky Woo
* Blog:   www.WuTianQi.com
* Data:   2010.12.20
* Note:   堆排序(Heap Sort)
*/
#include <iostream>
using namespace std;
 
// 输出当前堆的排序状况
void PrintArray(int data[], int size)
{
    for (int i=1; i<=size; ++i)
        cout <<data[i]<<"  ";
    cout<<endl;
}
 
// 堆化,保持堆的性质
// MaxHeapify让a[i]在最大堆中"下降",
// 使以i为根的子树成为最大堆
void MaxHeapify(int *a, int i, int size)
{
	int lt = 2*i, rt = 2*i+1;
	int largest;
	if(lt <= size && a[lt] > a[i])
		largest = lt;
	else
		largest = i;
	if(rt <= size && a[rt] > a[largest])
		largest = rt;
	if(largest != i)
	{
		int temp = a[i];
		a[i] = a[largest];
		a[largest] = temp;
		MaxHeapify(a, largest, size);
	}
}
 
// 建堆
// 自底而上地调用MaxHeapify来将一个数组a[1..size]变成一个最大堆
//
void BuildMaxHeap(int *a, int size)
{
	for(int i=size/2; i>=1; --i)
		MaxHeapify(a, i, size);
}
 
// 堆排序
// 初始调用BuildMaxHeap将a[1..size]变成最大堆
// 因为数组最大元素在a[1],则可以通过将a[1]与a[size]互换达到正确位置
// 现在新的根元素破坏了最大堆的性质,所以调用MaxHeapify调整,
// 使a[1..size-1]成为最大堆,a[1]又是a[1..size-1]中的最大元素,
// 将a[1]与a[size-1]互换达到正确位置。
// 反复调用Heapify,使整个数组成从小到大排序。
// 注意: 交换只是破坏了以a[1]为根的二叉树最大堆性质,它的左右子二叉树还是具备最大堆性质。
//        这也是为何在BuildMaxHeap时需要遍历size/2到1的结点才能构成最大堆,而这里只需要堆化a[1]即可。
void HeapSort(int *a, int size)
{
	BuildMaxHeap(a, size);
	PrintArray(a, size);
 
	int len = size;
	for(int i=size; i>=2; --i)
	{
		int temp = a[1];
		a[1] = a[i];
		a[i] = temp;
		len--;
		MaxHeapify(a, 1, len);
		cout << "中间过程:";
		PrintArray(a, size);
	}
 
}
 
int main()
{
	int size;
	int arr[100];
	cout << "Input the num of elements:\n";
	cin >> size;
	cout << "Input the elements:\n";
	for(int i=1; i<=size; ++i)
		cin >> arr[i];
	cout << endl;
    HeapSort(arr, size);
	cout << "最后结果:";
    PrintArray(arr, size);
}

最后,给大家推荐我在网上看到的写的不错的几篇堆排序文章:

1.http://blog.csdn.net/super_chris/archive/2009/09/22/4581900.aspx

  这一篇讲得很细致。

2.http://blog.csdn.net/made_in_chn/archive/2010/04/12/5473871.aspx

  大家可以看看文章里的图,注意那是最小堆实现的图。

3.http://blog.csdn.net/midgard/archive/2009/04/14/4070074.aspx

  同样讲的很细致的一篇。

4.http://student.zjzk.cn/course_ware/data_structure/web/paixu/paixu8.4.2.2.htm

   这是网站很给力,有flash模拟堆排序,大家可以实际去看看。