C语言必学的12个排序算法:堆排序(第7篇)

题外话堆排序比之前的简单选择、冒泡算法、快速排序算法复杂一些,因为用到了树形数据结构,但是本文使用了数组实现完全二叉树,因此也比较简单。C语言初学者,可以简单了解其思想,具体的知识掌握可以参照数据结构等专业课程学习。

ps:昨天因为学习实在太晚,未及时更新,抱歉哈。学习C语言之余,觉得C语言编程,最重要的就是C语言语法+数据结构+算法,掌握这三方面基本上可以应付各种编程问题。

为什么学习排序算法,可以参见前面文章~[C语言必学的12个排序算法:基础知识 (第0篇)]

树形选择排序

  • 选择排序其实可以有简单选择排序、树形选择排序。

  • 其中堆排序是一种高效的树形选择排序。

  • 简单选择排序时间复杂度是O(n^2),每趟子排序需要比较O(n)次。

  • 树形选择排序通过减少每趟子排序比较次数,减少时间复杂度,基本思想是每趟子排序,对整个数据记录关键字重复两两比较(和锦标赛赛制一样),直至选出最小的关键字记录为止,这样每趟子排序只需要比较O(logn)次,重复n次,即可有序,时间复杂度为O(nlogn)。

  • 普通的树形选择排序需要的辅助空间较多、存在多余的比较的缺点,堆排序改进这些缺点,只需要1个辅助空间,堆排序的时间复杂度是O(nlogn),同样也是不稳定的内部排序。

  • 与快速排序比较,优势是最坏的情况,堆排序的时间复杂度同样是O(nlogn),快速排序平均性能比堆排序好,但是最坏情况时间复杂度是O(nlogn)。

堆排序

堆排序涉及的知识点较多,主要包括堆的定义、完全二叉树,对于C语言初学者来说,具体理论知识可以参见《数据结构》相关课本书籍。

1.完全二叉树

叶子结点只能出现在最后一层或倒数第二层的二叉树。通俗来说,树的父节点都有两个孩子结点,除非是最后一层或倒数第二层的父节点可以有1或0个孩子结点。

2.堆定义

堆首先是完全二叉树,根据父节点是否比孩子节点大,分为大顶堆和小顶堆。

大顶堆:完全二叉树中所有的父节点必须大于等于两个孩子结点,这样树的根结点就是整个树结点中最大值。

小顶堆:完全二叉树中所有的父节点必须小于等于两个孩子结点,这样树的根结点就是整个树结点中最小值。

3.排序过程

以从小到大排序为例,对整个数据记录序列初始建立“大顶堆”,然后根结点为最大关键字,和序列最后一个关键字记录交换,继续对剩下的前n-1个记录序列进行调整,重新变成新的“大顶堆”,如此反复,每次可以得到当前子序列最大的关键字,最后即可得到从小到大的序列。

实现要点:

    • 从小到大排序,为节约内存空间和方便实现,使用大顶堆;反之从大到小排序,则用小顶堆。
    • 重新调整建堆的过程称为筛选,完全二叉树父节点从上到下,沿着关键字较大的孩子节点向下调整,遇到比父节点大的孩子结点,则交换。
    • 每次筛选操作后,当前父节点代表的子完全二叉树符合堆的要求,因此对所有父节点筛选后,即可整个符合堆的定义。
    • 初始建堆的过程,相当于对所有父节点从“堆顶”筛选操作。
    • 初始建立堆时,注意完全二叉树最后一个非叶子结点肯定是[n/2]个数据元素,因为其左孩子的结点编号是n/2 * 2 = n,可以从该元素开始调整建立堆,叶子结点无需调整。

代码实现

*/
#include <stdio.h>
// 对范围[low,high]数据记录筛选调整建立堆
void adjust_heap(int a[],int low, int high)
{
   int t = a[low];
   int i;
   // 注意数组下标从0开始
   // 二叉树结点编号需要从1开始 
   // 父节点与孩子结点比较大小,沿着元素路径向下
   for(i=2*(low+1); i<=(high+1); i *= 2)
   {
      // 比较左右孩子结点,如果右孩子大,选择右孩子 
     if(i<=high && a[i-1]<a[i])  i++;

     // 如果父节点已经大于等于孩子结点,则调整结束
     if(t>=a[i-1]) 
       break;  
     // 否则调整交换位置
     // 此时不需要给a[i-1]赋值,节约操作
     a[low] = a[i-1];
     low = i-1; 
   } 
   a[low] = t;

} 
void heap_sort(int a[], int length)
{
  int i,t; 

  // 初始建成大顶堆 
  for(i=length/2-1; i>=0; i--)
  {
    adjust_heap(a, i, length-1);
  }
  // 每趟选出最大元素和当前最后一个元素交换,再调整成堆 
  for(i=length-1; i>=0; i--)
  {
    t = a[0];
    a[0] = a[i];
    a[i] = t;
    adjust_heap(a, 0, i-1);
  }
}
int main(void)
{
    int a[10] = {4,3,1,2,6,5,0,9,8,7};
   heap_sort(a, 10);
    int i;
    for(i=0; i<10; i++)
      printf("%d ", a[i]);
    return 0;
}

其实做为一个学习者,有一个学习的氛围跟一个交流圈子特别重要这里我推荐一个C/C++基础交流583650410,不管你是小白还是转行人士欢迎入驻,大家一起交流成长。



©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341