排序- 选择排序-简单选择排序,堆排序(C语言)

结构定义

#include<stdio.h>
#define MAXSIZE 100  //排序数组个数最大值

typedef struct {
	int a[MAXSIZE + 1];   //a[0]用作哨兵或临时变量
	int length;           //顺序表长度
}SqList;

/* 交换数组L中下标为 i 和 j 的值 */
void swap(SqList* L, int i, int j)
{
	int t = L->a[i];
	L->a[i] = L->a[j];
	L->a[j] = t;

简单选择排序

在这里插入图片描述

思路

通过 n - 1 次关键字间的比较,从 n - i + 1 个记录中选出关键字最小的记录,并和第 i (1 <= i <= n) 个记录交换

性能分析

不稳定排序
最好情况:顺序有序
最坏情况:逆序有序
空间复杂度:O(1)
时间复杂度:

简单选择排序最好情况最坏情况平均情况
-n2n2n2

直观看,简单选择排序性能最差,其实不然,万物有利有弊
如果记录的关键字本身比较大(比如数十位的数字),此时其占用存储空间很大,移动记录所用时间也就多,那么对于简单选择排序,它只比较,明确目标后,精确交换。因此,简单选择排序适用于数据量不大而关键字本身比较大的排序。

简单选择排序

/* 简单选择排序 */
void SelectSort(SqList* L)
{
	int min;
	for (int i = 1; i < L->length; i++)
	{
		min = i;           //初始化最小值下标
		for (int j = i + 1; j <= L->length; j++)
		{
			if (L->a[min] > L->a[j]) //如有更小的记录
				min = j;             //更换min
		}
		if (i != min)    //如果有比初始值更小的记录,交换
			swap(L, i, min);
	}
}

堆排序

在这里插入图片描述

思路

堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值。称为小顶堆。

如果 n 个元素的序列{a1, a2,…an} 满足:
ai <= a2i 且 ai <= a2i+1
则称该序列{a1, a2,…an} 为小顶堆

如果 n 个元素的序列{a1, a2,…an} 满足:
ai >= a2i 且 ai >= a2i+1
则称该序列{a1, a2,…an} 为大顶堆

堆排序基本思想:将待排序序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根结点。将它移走(其实是将它与堆数组的末尾元素交换,此时末尾元素为最大值),然后将剩余的 n - 1 个序列重新构造成一个堆,这样就可以得到 n 个元素的次大值。反复执行,就可以得到一个有序序列了。

实现堆排序需是解决两个问题:

  1. 将一个无序序列构建成一个堆
  2. 输出堆顶元素后,调整剩余元素为一个新的堆

问题解答1:堆的建立:将待排序的序列构成一个大顶堆,其实就是从上至下,从左至右,将每个非叶子结点当作根结点。如下图,依次将以 4 -> 3 -> 2 -> 1 的结点为根的子树调整为堆即可
在这里插入图片描述
问题解答2:

  1. 输出堆顶元素后,将它与堆数组的末尾元素交换
  2. 然后将根结点与左,右子树的根结点值进行比较,并与其中较小的值交换
  3. 反复执行,至到叶子结点,将得到一个新堆。称这个从堆顶到叶子的调整过程为“筛选”。

性能分析

不稳定排序
空间复杂度:O(1)
对原始记录的排序状态不敏感
时间复杂度:

堆插入最好情况最坏情况平均情况
-nlognnlognnlogn

堆排序

/* 堆排序 */
void HeapSort(SqList* L)
{
     /* 构建大顶堆 */
	for (int i = L->length / 2; i > 0; i--) 
		HeapAdjust(L, i, L->length);
		
/*  输出堆顶元素后,调整剩余元素为一个新的堆,反复执行 */
	for (int i = L->length; i > 1; i--)
	{
		swap(L, 1, i);      //将堆顶记录与最后一记录交换
		HeapAdjust(L, 1, i - 1);  //将L->[1......i-1]重新调整为大顶堆
	}
}

/* 调整顶堆 */
/* 将L->a[s......m]重新调整为一个大顶堆 */
void HeapAdjust(SqList* L, int s, int m)
{
	int t;
	t = L->a[s];        //临时存储
	for (int j = 2 * s; j <= m; j *= 2) //沿着关键字较大的孩子结点向下筛选
	{
		if (j < m && L->a[j] < L->a[j + 1])
			j++;     
		//j现在为关键字孩子中较大的记录下标

		if (t >= L->a[j])     //如果 t > 它最大孩子记录值,则退出循环
			break;

		L->a[s] = L->a[j];  //做替换,大的记录上移
		s = j;              // s 下移
	}
	L->a[s] = t;       //插入到合适位置
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mirror_zz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值