堆之初步使用

  上一篇博客我们讲了一种叫并查集的数据结构,这里我们再讲另外一种数据结构——堆。

  堆是一种能凸显最重要数据的数据结构(最大值或最小值)。

  首先,堆是由完全二叉树勾成的。什么是完全二叉树呢?进化到完全体的树状数码宝贝。完全二叉树是指如二叉树的高度为h,除h层外,其他各层(1到h-1层)的结点个数都达到最大,第h层的结点都连续集中分布在最左边。如图:

堆之初步使用

 

  同时,通过观察我们还发现对于结点x,它的父亲结点为x/2(若存在)(舍去小数部分),它的左右儿子结点分别为2x,2x+1(若存在)

  知道了完全二叉树的这两个特性,我们会发现,咦,我完全可以用数组模拟一个完全二叉树。没错,数组确实可以模拟完全二叉树,接下来我们就聊聊堆。

 

  堆分为大根堆与小根堆。对大根堆来说,对每一个结点都有:它的父亲结点的值比它大,它的儿子结点的值比它小。那么第一个节点存储的值就是整个堆里最大的值了。小根堆的特性与之相反。

  哦。原来这就是堆。那大根堆里第二个结点就是第二大的元素啦,最后一个结点就最小的元素啦。然而并不是这样,大根堆只是保证对每个结点,它的值大于它的儿子结点的值,小于它的父亲结点得值。也就是说,堆里的元素并不是按升序或降序排列的,堆仅仅是凸显了最重要的元素,使得大根堆的根结点为最大的元素,小根堆的结点为最小的元素。

 

  下面我们以大根堆为例,看看如何创建一个堆。

  首先是堆的创建,我们用push函数将一个新元素加入到堆中。

1 #define maxsize 3000000
2 
3 int heap[maxsize];
4 
5 void push(int x) {
6     num++;    //num代表当前堆中的元素数量
7     heap[num] = x;
8     up(num);
9 }

   咦,为什么出现了一个奇怪的up函数。我们直接把x放在第num个节点中,但第num个结点不一定遵循堆的特性,为了维护堆的特性,我们必须让x上浮。怎样上浮呢?如果x的值大于它的父亲结点的值,那么就交换它和它的父亲结点,直到它的值小于它的父亲结点或已经是根结点为止。

 1 #define maxsize 3000000
 2 
 3 int heap[maxsize];
 4 
 5 void up(int x) {
 6     while (x/2 > 0) {    //如果x/2<=0,说明x已经是根结点了,不能再上浮了
 7         if (heap[x] > heap[x/2]) {
 8             swap(&heap[x], &heap[x/2]);
 9             x = x / 2;
10         }
11         else {
12             break;
13         }
14     }
15 }

  这里出现的swap函数是一个简单的交换函数,相信大家都能实现(如果不能实现,建议先学好基础,暂时不学习堆)。

  学会了怎么把数据读入堆的小茗又犯愁了,我该怎么把根结点从堆中弹出呢?这时候就需要一个pop函数了。

1 #define maxsize 3000000
2 
3 int heap[maxsize];
4 
5 void pop() {
6     heap[1] = heap[num];
7     num--;
8     down(1);
9 }

  这里我们将最后一个结点的值赋值给根结点,根结点就被覆盖了。同时num--,原来的第num个结点就在范围外了。但原来的num结点不一定是堆中最大的元素,为了维护堆,我们就让这个结点下沉,直到它的值大于它的儿子结点或它没有儿子结点。

 1 #define maxsize 3000000
 2 
 3 int heap[maxsize];
 4 
 5 void down(int x) {
 6     while (x*2 <= num) {
 7         int maxson = 2*x;
 8         if (maxson+1 <= num && heap[maxson+1] > heap[maxson]) {
 9             maxson++;
10         }
11 
12         if (heap[maxson] > heap[x]) {
13             swap(&heap[maxson], &heap[x]);
14             x = maxson;
15         }
16         else {
17             break;
18         }
19     }
20 }

  这样,我们就基本实现了堆的基本功能,我们就能随时查看堆中最重要的值了。

1 #define maxsize 3000000
2 
3 int heap[maxsize];
4 
5 int gettop() {
6     return heap[1];
7 }

  怎么样,堆这种数据结构你学废了学会了吗?

 

上一篇:Saving James Bond - Easy Version


下一篇:数据结构常用头文件之类型定义