经典算法系列三----堆排序

花了些时间好好看了堆排序的内容,代码也敲了,现在来总结一下。

为了说明白点,有些图片我就从网上截取了。

首先是堆的概念。

数据结构中的堆,又叫二叉堆

一般用数组来表示堆得结构,或者说是把堆数组化。举个列子来看:

这样就很清楚的看出了堆的储存结构。

接着就是堆得操作处理了。

首先堆的插入操作:

上代码:

 1 void Heap_insert_fix(int a[],int n)
 2 {
 3      int temp;
 4      int i,j;
 5      i = n;
 6      j = (i - 1) / 2;/*父节点*/
 7      temp = a[i];/*记录插入的数据*/
 8      while(i != 0 && j >= 0)
 9      {
10             if(a[j] <= temp)
11                     break;
12             a[i] = a[j];
13             i = j;
14             j = (i - 1) / 2;         
15      }
16      a[i] = temp;
17 }

上面代码解析:

先明白这点,

*父节点 i 子节点则为  j = i*2 +1
*子节点 i 父节点则为  j = (i-1) / 2

然后,插入的流程是,每次插入的数据都是在叶子节点,接着调整。直到满足堆得性质为止!

然后是堆的删除操作:

原理:每次删除根节点,就是用叶子节点覆盖根节点

上代码:

 1 /*
 2 *删除操作
 3 *删除根结点,然后调整
 4 *从i节点开始,总数有n个 
 5 */ 
 6 void Heap_del_fix(int a[],int i,int n)
 7 {
 8     int j;
 9     int temp;
10     temp = a[i];
11     j = i * 2 + 1;/*子节点*/
12     while(j < n)
13     {
14          if(a[j] > a[j+1] && j+1 < n)
15          j++;
16          if(a[j] >= temp)
17          break;
18          a[i] = a[j];
19          i = j;
20          j = i*2 + 1;    
21     }
22     a[i] = temp;
23 }

它的调用函数:

1 /*
2 *调用 
3 */
4 void Heap_sub(int a[],int n)
5 {
6      swap(&a[0],&a[n-1]);
7      Heap_del_fix(a,0,n-1);         
8 }


有了上面这两个操作的基础,下面我就来写排序操作!

思想:

我们有了删除操作,每次删除根节点,而且我们知道根节点是数组中数值最小的那个值(我这里是小堆模式,大堆相反),这样我们一次次的删除,就形成了一个有序的序列。获取这个序列就是我们想要的堆排序结果。

怎么获取,使用数组,看代码:

 1 /*
 2 *堆排序 
 3 */
 4 void Heap_sort(int a[],int n)
 5 {
 6      int i;
 7      for(i=n-1;i>=1;i--)
 8      {
 9           swap(&a[0],&a[i]);
10           Heap_del_fix(a,0,i);
11      }     
12 }

这样倒序便是我们的序列。

至于如何将堆数组化,代码如下:

 1 /*
 2 *堆化数组  
 3 */
 4 void create_heap(int a[], int n)
 5 {
 6      int i;
 7      for(i=n/2-1;i>=0;i--)/*将数组转成堆,分开调整*/
 8      {
 9           Heap_del_fix(a,i,n);                     
10      }      
11 }

这里的i=n/2-1;我们最好画图来看,这样理解会容易些。这部分我是在纸上实现的,读者可以自己尝试。

对于swap函数,一个比较好的写法如下:

1 void swap(int *i,int *j) 
2 {
3      *i = *i ^ *j;
4      *j = *i ^ *j;
5      *i = *i ^ *j; 
6 }

异或来交换两个数的写法,不需额外的变量。

整体代码如下:

  1 /*
  2 *堆排序
  3 *丁洋
  4 *说明:堆用数组来表示,那么
  5 *父节点 i 子节点则为  j = i*2 +1
  6 *子节点 i 父节点则为  j = (i-1) / 2 
  7 * 
  8 */
  9 #include<stdio.h>
 10 #include<stdlib.h>
 11 
 12 void swap(int *i,int *j) 
 13 {
 14      *i = *i ^ *j;
 15      *j = *i ^ *j;
 16      *i = *i ^ *j; 
 17 }
 18 /*
 19 *插入操作
 20 *堆尾插入,然后调整 
 21 */
 22 void Heap_insert_fix(int a[],int n)
 23 {
 24      int temp;
 25      int i,j;
 26      i = n;
 27      j = (i - 1) / 2;/*父节点*/
 28      temp = a[i];/*记录插入的数据*/
 29      while(i != 0 && j >= 0)
 30      {
 31             if(a[j] <= temp)
 32                     break;
 33             a[i] = a[j];
 34             i = j;
 35             j = (i - 1) / 2;         
 36      }
 37      a[i] = temp;
 38 }
 39 /*
 40 *调用
 41 */
 42 void Heap_add(int a[],int n,int Num)
 43 {
 44      a[n] = Num;
 45      Heap_insert_fix(a,n); 
 46 }
 47 
 48 /*
 49 *删除操作
 50 *删除根结点,然后调整
 51 *从i节点开始,总数有n个 
 52 */ 
 53 void Heap_del_fix(int a[],int i,int n)
 54 {
 55     int j;
 56     int temp;
 57     temp = a[i];
 58     j = i * 2 + 1;/*子节点*/
 59     while(j < n)
 60     {
 61          if(a[j] > a[j+1] && j+1 < n)
 62          j++;
 63          if(a[j] >= temp)
 64          break;
 65          a[i] = a[j];
 66          i = j;
 67          j = i*2 + 1;    
 68     }
 69     a[i] = temp;
 70 }
 71 /*
 72 *调用 
 73 */
 74 void Heap_sub(int a[],int n)
 75 {
 76      swap(&a[0],&a[n-1]);
 77      Heap_del_fix(a,0,n-1);         
 78 }
 79 
 80 /*
 81 *堆化数组  
 82 */
 83 void create_heap(int a[], int n)
 84 {
 85      int i;
 86      for(i=n/2-1;i>=0;i--)/*将数组转成堆,分开调整*/
 87      {
 88           Heap_del_fix(a,i,n);                     
 89      }      
 90 }
 91 
 92 /*
 93 *堆排序 
 94 */
 95 void Heap_sort(int a[],int n)
 96 {
 97      int i;
 98      for(i=n-1;i>=1;i--)
 99      {
100           swap(&a[0],&a[i]);
101           Heap_del_fix(a,0,i);
102      }     
103 }
104 int main()
105 {
106     int i;
107     int a[] = {2,4,8,1};
108     
109     create_heap(a,4);
110     Heap_sort(a,4);
111     for(i=0;i<4;i++)
112         printf("%d ",a[i]);
113     printf("\n");
114     system("pause");
115 }

时间复杂度:

由于每次重新恢复堆的时间复杂度为O(logN),共N - 1次重新恢复堆操作,再加上前面建立堆时N / 2次向下调整,每次调整时间复杂度也为O(logN)。二次操作时间相加还是O(N * logN)。故堆排序的时间复杂度为O(N * logN)。STL也实现了堆的相关函数,可以参阅《STL系列之四 heap 堆

 

posted @ 2013-12-24 22:44  丁洋  阅读(329)  评论(0编辑  收藏  举报