堆是一个完全二叉树,树中每个结点对应于原始数据的一个记录,并且每个结点应满足以下条件:非叶结点的数据大于或等于其左、右孩子结点的数据(若是按从大到小的顺序排序,则要求非叶结点的数据小于或等于其左、右孩子结点的数据)。
由堆的定义可看出,其根结点为最大值,堆排序就是利用这一特点进行的。堆排序过程包括两个阶段:
(1)将无序的数据构成堆(即用无序数据生成满足堆定义的完全二叉树)。
(2)利用堆排序(即用上一步生成的堆输出有序的数据)。
加入有一组要排序的数据:69,65,90,37,92,6,28,54
1.首先,根据堆的定义,将这组数据生成堆
a. 从最后一个非叶节点37开始,比较其子节点,若发现左右节点中有一个比该节点大,则将两个节点进行交换
b. 按照上面的步骤,一遍下来可以将树中最大的节点92筛选到最上面的根节点
2.利用堆进行排序
a.
因为上面排序一遍过后,最大的节点在根节点,因此将根节点与最后一个节点交换,使最大的节点排在最后
b.
由于根节点和最后一个节点交换,破坏了原来的堆结构,所以需要对除了最后一个节点外的其他n-1个节点重新生成堆
c.
重复上面两个步骤,最后剩下的一个节点即为最小节点,排在整个二叉树的根节点,至此则生成从小到大的有序堆
具体图示如下:
(图中输出,即表示为调整到数组最后)
下面贴上具体代码(C语言版):
1 #include <stdio.h>
2 #include "CreateData.c" //生成随机数文件
3
4 #define MAXSIZE 10
5
6 //对指定的节点构成堆
7 //int m 指定的节点
8 //int n 数组的元素个数
9 void HeapAdjust(int a[],int m,int n){
10 int i,j,t;
11
12 while(2*m+1 < n){ //指定的节点有左子树
13 j = 2*m+1; //m的左子树
14
15 if(j+1 < n){
16 //有右子树,且右子树大于左子树
17 if(a[j] < a[j+1]) j++;
18 }
19
20
21 if(a[m] < a[j]){
22 //如果有个子树大于根节点,则交换
23 t = a[m];
24 a[m] = a[j];
25 a[j] = t;
26
27 m = j; //交换过以后,以j为根的节点再重新生成堆
28 }else
29 break;
30
31 }
32 }
33
34 //堆排序算法
35 void HeapSort(int a[],int n){
36 int i,t;
37
38 i = n/2-1;
39
40 for(;i>=0;i--)
41 HeapAdjust(a,i,n);
42
43 for(i = n-1;i>=0;i--){
44 //将树根节点(最大)放到最后一个元素
45 t = a[i];
46 a[i] = a[0];
47 a[0] = t;
48
49 //破坏了根节点,再对根节点重新生成堆
50 HeapAdjust(a,0,i);
51 }
52 }
53
54 int main(){
55 int a[MAXSIZE];
56 int i;
57
58
59 if(!CreateData(a,100,10,MAXSIZE)){
60 printf("生成随机数失败.\n");
61 return 0;
62 }
63
64 printf("排序前:");
65 for(i = 0 ; i < MAXSIZE ;i++)
66 printf("%d ",a[i]);
67
68 HeapSort(a,MAXSIZE);
69
70 printf("\n排序后:");
71 for(i = 0 ; i < MAXSIZE ;i++)
72 printf("%d ",a[i]);
73 printf("\n");
74
75 return 1;
76 }
此段代码在调试的过程中比较坎坷,在Linux中有时候会发现比较古怪的问题,比如陷入死循环但是很难追踪,不过都是个人粗心,有几个变量赋值和循环条件刚开始的时候写错了。不过现在经测试已经运行稳定,并实现从小到大进行数据排序功能。
时间: 2024-10-17 16:15:48