排序算法归纳(c语言) ⑤堆排序

堆排序

HeapSort,这是相当常用的一种排序算法。原因是它的时间复杂度相当稳定,且相当高效。无论是最好情况还是最坏情况,时间复杂度都能较稳定地保持在O(nlogn)
之所以这么高效,是因为它建立在一种特殊的数据结构上,即,堆。
什么是堆呢?其实堆就是一个完全二叉树
因为是一个完全二叉树,所以可以方便的用顺序存储的方式存下来。再根据完全二叉树的特殊性质,可以很完美的实现堆的adjust
关键的性质
对于一棵有 n 个结点的完全二叉树,其节点按层序编号,每层从左到右,则对于任一结点 i 有:

  • 如果 i 等于1,则节点 i 是二叉树的根,无双亲。如果 i 大于1,则该节点的双亲是结点 i / 2。
  • 如果 2 * i 大于n,则该节点无左孩子,否则其左孩子是结点 2 * i 。
  • 如果 2 * i + 1 大于n,则该节点无右孩子,否则其右孩子是结点 2 * i + 1 。

堆排序的关键在于adjust,即保持这颗完全二叉树是一个大顶堆(小顶堆)

  • 大顶堆:完全二叉树中,任一结点都小于其双亲结点。
  • 小顶堆:完全二叉树中,任一结点都大于其双亲结点。

我们每次取下一个堆顶元素,那么只要每次操作完后,通过一次adjust,都能保证这颗完全二叉树还是一个大顶堆(小顶堆),那么我们取出的也就自然是有序序列了。

待排序数据依然存放于顺序表中。
数据存放没有从0开始,而是选择从1开始。
代码参考于《大话数据结构》。

初始设定
#include<stdio.h>
#define MAXSIZE 20 //顺序表最大容量
#define N 10  //表中数据个数
顺序表结构体
typedef struct
{
 int data[MAXSIZE + 1];
 int len;  //已存储元素个数
}Sqlist;
输出顺序表
void Show(Sqlist L)
{
 int i;
 for (i = 1; i < L.len; ++i)
 {
  printf("%d,", L.data[i]);
 }
 printf("%d\n", L.data[i]);
}
输入函数
void Input(Sqlist* lp)
{
 int d[N] = { 9, 1, 5, 8, 3, 0, 7, 4, 6, 2 };
 for (int i = 0; i < N; i++)
  lp->data[i + 1] = d[i];
 lp->len = N;
}
swap函数
void Swap(Sqlist* lp,int a, int b)
{   //交换顺序表中两元素的数值
 int t = lp->data[a];
 lp->data[a] = lp->data[b];
 lp->data[b] = t;
}
堆排序
void HeapAdjust(Sqlist* lp, int s, int m)
{
 int j, temp;
 temp = lp->data[s];	//暂存
 for (j = 2 * s; j <= m; j *= 2)
 {				//沿较大的孩子结点向下筛选
  if (j < m && lp->data[j] < lp->data[j + 1])
   ++j;			//将j标在较大的孩子上
  if (temp >= lp->data[j])
   break;			//说明此时该子树双亲已经最大
  lp->data[s] = lp->data[j];	//较大孩子上移
  s = j;
 }
 lp->data[s] = temp;	//插入
}

void HeapSort(Sqlist* lp)
{
 int i;
 for (i = lp->len / 2; i > 0; --i)
 {		//不必考虑叶子节点,故一开始生成堆只需处理一半结点
  HeapAdjust(lp, i, lp->len);
 }
 for (i = lp->len; i > 1; --i)
 {		//每次将堆顶和最后一个元素交换,堆中元素个数减一
  Swap(lp, 1, i);
  HeapAdjust(lp, 1, i - 1);
 }
}
系列链接

上一个排序算法:
④希尔排序
下一个排序算法:
⑥归并排序

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值