一、排序的基本概念
1) 排序: 将一组杂乱无章的数据按一定规律顺次排列起来
// 将无序序列排成一个有序序列(由小到大或由大到小)的运算
2) 如果参加排序的数据结点包含多个数据域,那么排序往往是针对其中某个域而言
3) 排序方法的分类:
* 按数据存储介质: 内部排序和外部排序
// 内部排序: 数据量不大、数据在内存,无需内外存交换数据
// 外部排序: 数据量较大、数据在外存(文件排序)
* 按比较器个数: 串行排序和并行排序
// 串行排序: 单处理机(同一时刻比较一对元素)
// 并行排序: 多处理机(同一时刻比较多对元素)
* 按主要操作: 比较排序和基数排序
// 比较排序: 用比较的方法(插入排序、交换排序、选择排序、归并排序)
// 基数排序: 不比较元素的大小,仅仅根据元素本身的取值确定其有序位置
* 按辅助空间: 原地排序和非原地排序
// 原地排序: 辅助空间用量为O(1)的排序方法(所占的辅助存储空间与参加排序的数据量大小无关)
// 非原地排序: 辅助空间用量超过O(1)的排序方法
* 按稳定性: 稳定排序和非稳定排序
// 稳定排序: 能够使任何数值相等的元素,排序以后相对次序不变(只对结构类型数据排序有意义)
// 非稳定排序: 不是稳定排序的方法
* 按自然性: 自然排序和非自然排序
// 自然排序: 输入数据越有序,排序的速度越快的排序方法
// 非自然排序: 不是自然排序的方法
4) 记录序列以顺序表存储
#define MAXSIZE 20 //设记录不超过20个
typedef int KeyType; //设关键字为int型
typedef char InfoType; //设其他数据项为char型
typedef struct //定义每个数据元素的结构
{
KeyType key; //关键字
InfoType otherinfo; //其他数据项
}RedType;
typedef struct //定义顺序表的结构
{
RedType r[MAXSIZE + 1]; //存储顺序表的向量,r[0]一般作哨兵或缓冲区
int length; //顺序表的长度
}SqList;
二、插入排序(类似打扑克摸牌)
1) 基本思想: 每步将一个待排序的对象,按其关键码大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止
// 边插入边排序,保证子序列中随时都是排好序的
2) 基本操作: 有序插入
* 在有序序列中插入一个元素,保持序列有序,有序长度不断增加
* 起初,a[0]是长度为1的子序列,然后逐一将a[1]至a[n-1]插入到有序子序列中
* 在插入a[i]前,数组a的前半段(a[0]~a[i-1])是有序段,后半段(a[i]~a[n-1])是停留于输入次序的"无序段"
* 插入a[i]使a[0]~a[i-1]有序,也就是要为a[i]找到有序位置j(0<=j<=i),将a[i]插入在a[j]的位置上
3) 插入排序的分类:
* 直接插入排序: 顺序法定位插入位置
* 二分插入排序: 二分法定位插入位置
* 希尔排序: 缩小增量 多遍插入排序
1.直接插入排序
* 顺序表下标为0存哨兵,将每次要比较的元素存入进去(作用是少一次判断,不用判断j是否满足条件,相等直接退出循环)
* 从顺序表下标为2的元素开始比较(即i的值,i小于等于顺序表元素个数),如果下标为i的元素大于下标为i-1的元素,则i++,否则
// 将j设为i-1(即最后一个有序值),如果哨兵(即要比较的值)<最后一个有序值,则将下标为j的元素向后移
// 直到下标j的元素<哨兵(即要比较的值),则将哨兵的值赋给下标为j+1的位置
// 退出内层循环,i++,继续比较下一个将要插进来的值
void InsertSort(SqList *L) //直接插入排序
{
int i, j;
for (i = 2; i <= L->length; i++)
{
if (L->r[i].key < L->r[i - 1].key) //若"<",需将L->r[i]插入有序子表
{
L->r[0] = L->r[i]; //复制为哨兵
for (j = i - 1; L->r[0].key < L->r[j].key; j--)
{
L->r[j + 1] = L->r[j]; //记录后移
}
L->r[j + 1] = L->r[0]; //插入正确位置
}
}
}
算法分析:
主要是比较两个关键字的大小及数据元素的移动情况
1) 最好情况(顺序有序): 时间复杂度: O(n)
比较次数: n-1(从第二个元素开始比较)
移动次数: 0(不需要移动)
2) 最坏情况(逆序有序): 时间复杂度: O(n*n)