堆排序和优先级队列

堆排序和优先级队列

  堆排序:和合并排序一样,时间复杂度为O(nlgn);同时和插入排序一样,在原序列中进行;这样堆排序集合了合并排序和插入排序的优点。

  堆排序的另一个特点是利用了"堆"这种数据结构. 堆数据结构还不止在堆排序中有用,还可以构成一个有效的优先队列.

  堆: 是一种数据结构,也是一种数组对象,如图 1-1所示: 

图 1-1 最大堆(图片来源《算法导论》)

  如上图1-1 所示, 可以被看成一棵完全二叉树,二叉树的的每个节点和数组中存放该节点的那个元素对应.在使用"堆"数据机构的时候,其实并不需求真正构建一棵完全二叉树, 数据的存储依然是在一个数组中,操作也在数组中,只是数组中数据的存放,位置关系如完全二叉树所示,我们在构造堆的时候其实是在构建数组中数据的关系. 上图所示一个最大堆(父节点大于两个子节点), 最小堆同理.

  由于我们将数组中的数据关系对应成了一块完全二叉树,所以对数组的各种操作时间复杂度也应该和完全二叉树操作向对应,即O(nlgn). 下文所以的堆即代指"最大堆"

  堆排序

  根据堆的性质,父节点大于子节点,那么根节点一定是最大的,继而数组中第一个数据也应该是最大的. 根据冒泡排序和选择排序的原理, 如果将每次数组中的第一个元素和最后一个元素进行交换,再让第一个到倒数第二个形成的新数组重新形成最大堆,再次将新数组第一个和最后一个交换;需要重新建堆的数组长度不断减小.如此循环下去,最后原数组成了一个有小到大的有序序列.

  根据上面的论述,如何让无序的元素,构建成一个符合(最大/最小)堆性质的堆才是关键.

  保持堆(最大堆)性质原理: 将父节点和两个子节点进行比较,找到那个最大;如果父节点最大,该节点符合最大堆性质;如何是两个子节点中某个最大,将最大子节点和父节点位置进行交换; 在查看新的子节点作为父节点是否符合最大堆的性质,以此循环.

  代码如下:

 1 //子节点和父节点在数组中位置的对应关系(数组从0开始)
 2 int parent(int i)
 3 {
 4     return (int)((i-1)/2); 
 5 }
 6 //父节点和左子节点在数组中的位置对应关系
 7 int Left(int i)
 8 {
 9     return 2*i+1;
10 }
11 //父节点和右子节点在数组中的位置对应关系
12 int Right(int i)
13 {
14     return 2*i+2;
15 }
16 
17 //保持最大堆性质,循环控制的形式
18 void MaxHeapify(int A[], int  i,int length)
19 {
20     int left,right,largest;
21     left=Left(i);
22     right=Right(i);
23 
24     largest=length;
25 
26     while(largest!=i){
27 
28     if(left<length && A[left]>A[i]) 
29         largest=left;
30     else
31         largest=i;
32     if(right<length && A[right]>A[largest])
33         largest=right;
34         int temp;
35         temp=A[largest];
36         A[largest]=A[i];
37         A[i]=temp;        
38     }
39 
40 }
41 
42 //保持最小堆性质,递归的形式
43 void MinHeapify(int A[], int  i,int length)
44 {
45     int left,right,minmun;
46     left=Left(i);
47     right=Right(i);
48 
49     if(left<length && A[left]<A[i]) 
50         minmun=left;
51     else
52         minmun=i;
53     if(right<length && A[right]<A[minmun])
54         minmun=right;
55 
56     if(minmun!=i)
57     {
58         int temp;
59         temp=A[minmun];
60         A[minmun]=A[i];
61         A[i]=temp;
62 
63         MaxHeapify(A,minmun,length);
64     }
65 
66 }

 

  建堆以及堆排序算法: 

  堆排序的基本原理就是对一个无序数组建立堆关系,再利用堆的性质不断重数组中查找最值元素.

  建堆的代码如下:

1 void BuildMaxHeapify(int A[], int length)
2 {
3     int i;
4     for(i=(int)((length-1)/2);i>=0;i--)
5         MaxHeapify(A,i,length);
6 }

 

  堆排序的算法代码如下: 

 1 void Heapsort(int A[],int Length)
 2 {
 3     int i;
 4     int temp;
 5     BuildMaxHeapify(A,Length);
 6 
 7     for(i=Length;i>=1;i--)
 8     {
 9         MaxHeapify(A,0,i);
10 
11         temp=A[i-1];
12         A[i-1]=A[0];
13         A[0]=temp;
14 
15     }
16 
17 }

 

  优先级队列

  优先级队列分为最大优先级队列和最小优先级队列.(应用举例摘于《算法导论》)

  最大优先级队列应用:其一,一台分时计算机上进行作业调度,如何从具有优先级标示的队列中选择优先级最高的任务.

  最小优先级队列应用: 其一, 被用在基于事件驱动的模拟器中. 在这种应用中,队列中的各项是要模拟的事件,每一个都有一个发生时间作为关键字。事件模拟按照各事件的发生时间的顺序进行,因为模拟某一事件可能导致稍后对其他事件的模拟。模拟程序都使用EXTRACT-MIN来选择下一个事件,而使用INSERT将一个新事件插入队列中。

  最大优先级队列提取操作代码如下:

 1 int HeapExtractMax(int A[], int length)
 2 {
 3     int max;
 4 
 5     if(length<1)
 6         printf("the heap underflow!\n");
 7     else
 8     {
 9         max=A[0];
10         A[0]=A[length-1];
11 
12         MaxHeapify(A,0,length);
13     }
14 
15     return max;
16 }

 

  最大优先级队列提高或减小优先级操作代码如下:

 1 void HeapIncreaseKey(int A[],int i,int key)
 2 {
 3     int temp;
 4     
 5     if(key<A[i])
 6     {
 7         printf("The new key can not be smaller than current key!\n");
 8     }
 9     else
10     {
11         while(i>=0 && A[parent(i)]<key)
12         {
13             temp=A[parent(i)];
14             A[parent(i)]=key;
15             key=temp;
16 
17             i=parent(i);
18         }
19     }
20 }
21 
22 //减小某一元素的关键字
23 void  HeapDecreaseKey(int A[],int i,int key,int length)
24 {
25     int temp;
26 
27     if(key>A[i])
28     {
29         printf("The new key can not be bigger than current key!\n");
30     }
31     else
32     {
33         while(i<length && (key<A[Left(i)] || key<(Right(i))))
34         {
35             if(key<A[Left(i)])
36             {
37                 temp=A[Left(i)];
38                 A[Left(i)]=key;
39                     key=temp;
40 
41                     i=Left(i);
42             }
43             else
44             {
45                 temp=A[Right(i)];
46                 A[Right(i)]=key;
47                     key=temp;
48 
49                     i=Right(i);
50             }
51 
52         }
53     }
54     
55 }

  最大优先级队列插入新元素操作代码如下:

1 void MaxHeapInsert(int A[],int key,int length)
2 {
3     length=length+1;
4     A[length-1]=-1;//假设数组内的整数全部大于0
5     
6     HeapIncreaseKey(A,length-1,key);
7 }

 

 

 

 

 

 

 

 

posted @ 2014-03-05 14:19  游-游  阅读(593)  评论(0编辑  收藏  举报