码迷,mamicode.com
首页 > 编程语言 > 详细

堆排序

时间:2015-03-07 06:13:09      阅读:161      评论:0      收藏:0      [点我收藏+]

标签:

堆排序算法

  一、基本思想:是一种属性排序选择方法,在排序过程中,将 R[1...n] 看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区域中选择关键字最大(或最小)的记录。

  

   技术分享 

 

  二、C 语言代码:

  

 1 /**
 2  *  堆排序的关键是构造初始堆,这里采用筛选的算法建堆: 假若完全二叉树的某一个结点 i, 它的左子树、右子树已是堆, 接下来需要将 R[2i].key
 3  *和 R[2i+i].key 之中的最大者与 R[i].key 作比较, 若 R[i].key 较小则将其与最大孩子的关键字交换, 这有可能破坏了下一级的堆, 于是继续采用
 4  *上述方法构造下一级的堆, 直到完全二叉树中结点 i 构成堆为止. 对于任意一棵完全二叉树, 从 i = [n/2] ~ 1, 反复利用上述调整堆的方法构建堆,
 5  *大者"上浮"小者被"筛选"下去, 筛选算法如下:
 6 */
 7 
 8 //对 R[low...high] 进行筛选 
 9 void sift(RecType R[], int low, int high)
10 {
11     //R[j] 是 R[i] 的左孩子 
12     int i = low;
13     int j = 2 * i;
14     RecType tmp = R[i];
15     
16     //若 j 在有效范围内 
17     while (j <= high) {
18         //若右孩子较大, 则将 j 指向右孩子 
19         if (j < high && R[j].key < R[j+1].key) {
20             j++; 
21             //若 R[i].key 小于孩子中最大关键字 
22             if (tmp.key < R[j].key) {
23                 R[i] = R[j]; //将 R[j] 调整到双亲结点位置上 
24                 i = j; //修改 i 的值以便于继续向下筛选 
25                 j = 2 * i; //再次设置 R[j] 为 R[i] 的左孩子 
26             } else {
27                 break; 
28             } 
29         }
30     }
31     R[i] = tmp; //被筛选结点的值放入最终位置 
32 }
33 
34 /**
35  *  在初始堆(大根堆)构造完后, 根结点一定是最大关键字结点, 将其放到数序的最后, 也就是将堆中的根与最后一个叶子交换. 由于最大元素已归位, 
36  *整个待排序的元素个数减少一个, 但由于根结点的改变, 这 n-1 个结点不一定为堆, 而其左子树和右子树均为堆, 调用一次 sift() 算法将这 
37  *n-1 个结点调整成堆, 其根结点次大的元素, 将它放到数列的倒数第二个位置, 即将堆中根与最后一个叶子交换, 待排序的元素个数变为 n-2 个,
38  *再调整, 再将根结点归位, 如此这样直至完全二叉树只剩下一个根为止, 堆排序算法如下:
39 */
40 
41 void heapSort(RecType R[], int n)
42 {
43     int i; 
44     RecType tmp; 
45     
46     //循环建立初始堆 
47     for (i = n/2; i >= 1; i--) {
48         sift(R, i, n); 
49     } 
50     
51     //进行 n-1 趟完成堆排序, 每一趟堆排序的元素个数减 1, 将最后一个元素同当前区间内的 R[1] 对换 
52     for (i = n; i > 2; i--) {
53         tmp = R[1]; 
54         R[1] = R[i]; 
55         R[i] = tmp; 
56         sift(R, 1, i-1); //筛选 R[i] 结点, 得到 i-1 个结点的堆 
57     }
58 }

 

  三、算法分析

    时间复杂度:由算法代码可知,堆排序是由两个函数组成,第一个是 sift() 函数构建大根堆,第二个是 heapSort() 排序函数,对于 n 个排序记录而言,完全二叉树的高度为 log2(n+1),即 sift() 函数对于每个函数结点调整的时间复杂度是 O(log2n),而在 heapSort() 函数中两次循环的叠加为 (n/2 + n-1),故对于堆排序而言:

      关键字比较和记录移动的最少次数是 (n/2 + n-1)log2n,算法的时间复杂度为 O(nlog2n)。

      关键字比较和记录移动的最多次数为 (n/2 + n-1)log2n,算法的时间复杂度为 O(nlog2n)。

      关键字平均比较和记录移动次数为 (n/2 + n-1)log2n,算法的时间复杂度为 O(nlog2n)。

      

    空间复杂度:由算法代码可知,所需的额外空间只有一个 tmp 变量,故堆排序算法空间复杂度为 O(1)。

 

  四、思考

    堆排序的重点在于构造初始堆,那么,为什么一般要构造大根堆而不是小根堆?同时,当记录数较少时,为什么堆排序不合适?

 

 

 

 

 

  

堆排序

标签:

原文地址:http://www.cnblogs.com/lishiyun19/p/4319622.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!