结构定义
#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)
时间复杂度:
简单选择排序 | 最好情况 | 最坏情况 | 平均情况 |
---|---|---|---|
- | n2 | n2 | n2 |
直观看,简单选择排序性能最差,其实不然,万物有利有弊
如果记录的关键字本身比较大(比如数十位的数字),此时其占用存储空间很大,移动记录所用时间也就多,那么对于简单选择排序,它只比较,明确目标后,精确交换。因此,简单选择排序适用于数据量不大而关键字本身比较大的排序。
简单选择排序
/* 简单选择排序 */
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:堆的建立:将待排序的序列构成一个大顶堆,其实就是从上至下,从左至右,将每个非叶子结点当作根结点。如下图,依次将以 4 -> 3 -> 2 -> 1 的结点为根的子树调整为堆即可
问题解答2:
- 输出堆顶元素后,将它与堆数组的末尾元素交换
- 然后将根结点与左,右子树的根结点值进行比较,并与其中较小的值交换
- 反复执行,至到叶子结点,将得到一个新堆。称这个从堆顶到叶子的调整过程为“筛选”。
性能分析
不稳定排序
空间复杂度:O(1)
对原始记录的排序状态不敏感
时间复杂度:
堆插入 | 最好情况 | 最坏情况 | 平均情况 |
---|---|---|---|
- | nlogn | nlogn | nlogn |
堆排序
/* 堆排序 */
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; //插入到合适位置
}