堆排序

 #返回上一级

@Author: 张海拔

@Update: 2014-01-12

@Link: http://www.cnblogs.com/zhanghaiba/p/3515570.html

 

 1 /*
 2  *Author: ZhangHaiba
 3  *Date: 2014-1-12
 4  *File: heap_sort.c
 5  *
 6  *a demo of heap data struct and heap sort
 7  */
 8 #include <stdio.h>
 9 #define N 512
10 
11 //public
12 void repair_heap(int*, int, int);
13 void create_heap(int*, int);
14 void heap_sort(int*, int);
15 //private
16 void swap(int*, int*);
17 
18 int heap[N];
19 
20 int main(void)
21 {
22     int n;
23 
24     scanf("%d", &n);
25     set_array(heap, n);
26     create_heap(heap, n);
27     heap_sort(heap, n);
28     return 0;
29 }
30 
31 void repair_heap(int *h, int n, int par)
32 {
33     int pmin, save, left, right;
34 
35     while (par <= n/2) {
36         save = h[par];
37         pmin = par; //now point to par
38         left = par*2;
39         right = left+1;
40         if (h[left] < h[par])
41             h[par] = h[pmin = left]; //now point to left
42         if (right <= n && h[right] < h[par])
43             h[par] = h[pmin = right]; //now point to right
44         if (pmin == par) //if ture means par point to root of a heap
45             break;
46         else
47             h[pmin] = save; //swap fininsed
48         par = pmin;
49     }
50 }
51 
52 void create_heap(int *h, int n)
53 {
54     int i;
55     
56     for (i = n/2; i != 0; --i)
57         repair_heap(h, n, i);
58 }
59 
60 //after heap sort, original heap becomes a descending array
61 //in this procession, we output a ascending order of heap data;
62 void heap_sort(int *h, int n)
63 {
64     while (n != 0) {
65         printf(n == 0 ? "%d\n" : "%d ", h[1]);
66         swap(&h[1], &h[n--]);
67         repair_heap(h, n, 1);
68     }
69 }
70 
71 //set array range [1, n]
72 int set_array(int *a, int n)
73 {
74     int i;
75     
76     for (i = 1; i <= n; ++i)
77         scanf("%d", a+i);
78 }
79 
80 void swap(int *a, int *b)
81 {
82     int tmp = *a;
83     *a = *b;
84     *b = tmp;
85 }

 

堆的定义:
1)首先,逻辑关系上它是完全二叉树,物理存储是静态线性表(数组)
2)其次,父母结点的值一定大于(小于)孩子结点的值

为了形象,下面把数组的下标说成指针,即广义的指针

指针从1开始,有如下性质:
1)父母结点的指针范围:[1, n/2]
2)叶子结点的指针范围:[n/2+1, n]
3)左孩子指针可由其父母指针计算:left = par*2;
4)右孩子存在的条件:right <= n;

怎么把一个数组(完全二叉树的存储)变成堆?
1)自顶向下调整(可能需要回溯往上调整)
2)自底向上调整(一定顺序往下调整)
因此采用自底向上调整

注意:

自底向上调整堆,是对堆整体来说的。
而对于单个结点的调整,是从上往下的。

 

这里用自底向上调整的方法实现了小根堆。

排序过程是堆顶元素与“堆尾”元素(完全二叉树最右下方的节点)交换,交换前输出,交换后调整规模减1的堆顶。

堆排序的核心是repair_heap(),这个函数的while循环中对par所指的节点进行堆调整——

pmin指针的意义是指向当前的树根、左子树根和右子树根三者中的值最小的节点。

pmin初始指向par,经过判断,pmin可能指向left,也可能指向right,也可能仍指向par。

若仍指向par,说明par比两个孩子的值都大,即par所指的已经是堆了,因此不需要再调整。(因为堆是自底向上创建的)

否则pmin指向了最小节点,完成交换后,当前par应该变为pmin。

理解了上面的过程,用递归来实现也是很简单的。

 

 #返回上一级

posted @ 2014-01-12 01:04  张海拔  阅读(347)  评论(0编辑  收藏  举报