排序
排序:(记录对应的)关键字递增或递减 有序序列
n个记录的序列:{r1,r2………,rn},
对应的关键字:{k1,k2,……kn},
需确定 1,2,……,n 的一种排列 p1,p2,………,pn,使其相应的关键字满足 kp1<=kp2<=……<=kpn(非递减或非递增)关系,
即得一个按关键字有序的序列{rp1,rp2,……,rpn},这样的操作就称为排序。
排序:线性表的一种操作
输入:记录集合,输出:记录集合
依据:关键字之间的大小关系
同一个记录集合,针对不同的关键字(主关键字、次关键字、若干数据项的组合)排序,可以得到不同序列
稳定性
排序方法稳定:两个相等的关键字不会因为排序逆序
排序方法不稳定:两个相等的关键字因为排序逆序
结构和函数
//排序用的顺序表结构
#define MAXSIZE 10 /*用于要排序数组个数最大值,可根据需要修改*/
typedef struct
{
int r[MAXSIZE + 1]; /*用于存储要排序数组,r[0]用作哨兵或临时变量*/
int length; /*用于记录顺序表的长度*/
} SqList;
//排序中常用的交换数组2元素的函数
/*交换L中数组r的下标为i和j的值*/
void swap(SqList *L, int i, int j)
{
int temp = L->r[i];
L->r[i] = L->r[j];
L->r[j] = temp;
}
内排序和外排序
外排序:排序的记录个数太多(不能同时放在内存),需要在内外存之间多次交换数据
内排序:待排序的所有记录放在内存
性能影响:
1、时间性能,比较和移动
2、辅助空间,算法需要的,除待排序元素以外的其他存储空间
3、算法复杂度
分为:插入排序,交换排序,选择排序,归并排序
简单算法
从小往大排序
冒泡(交换)
初级版:每一个关键字都和后面的每一个关键字比较,大则交换
冒泡排序:循环(数组长度)次,每次从数组末尾开始,更小元素往前冒泡(交换),最小值会冒泡到最前端
优化:增加一个标记,一次循环中没有发生冒泡说明序列已有序,判断标记结束循环
优化版本 时间复杂度 O(n2):
最好情况:n-1次比较,没有数据交换,O(n)
最坏情况:逆序, (n-1)+(n-2)+…+2+1=n*(n-1)/2 次比较,相等数量数据交换;
简单选择
每次选出 i 到数组末尾最小的元素,和 i 交换
总时间复杂度 O(n2) :
比较次数 (n-1)+(n-2)+…+2+1=n*(n-1)/2
交换数据少, 次数 n-1,略优于冒泡
直接插入
把记录插入到有序表中,得到记录+1的有序表
总时间复杂度 O(n2):
最好情况:比较(n-1)次
最坏情况:
比较次数:n+(n-1)+…+2=(n+2)* (n-1)/2
移动次数 (n+1)+n+…+3=(n+4)*(n-1)/2
平均:(好坏概率相同)比较和移动次数n2/4,略优于冒泡和简单选择
改进算法
希尔(插入排序升级版)
基本有序:小的关键字基本在前面,大的基本在后面,不大不小的基本在中间
希尔排序:(相距某个“增量”的)记录组成一个子序列,对子序列直接插入排序
跳跃分割 保证结果基本有序,不是局部有序
时间复杂度O(n3/2):
增量选取非常关键,研究提供的增量序列 dlta[k]=2t-k+1-1,(0<=k<=t<=logz(n+1)向下取整)
最后一个增量值必须=1
跳跃移动,不稳定
堆(选择排序升级版)
堆:完全二叉树,大/小顶堆
大顶堆:每个结点都>=左右孩子结点
小顶堆:每个结点都<=左右孩子结点
根结点一定是堆中所有结点的最大(小)值
一棵完全二叉树,
i=1:结点i是二叉树的根,无双亲;
i>1,则其双亲是结点i/2向下取整。
有 n个结点的二叉树,i<=n/2向下取整
层序遍历编号i,n个结点,1<=i<=n/2向下取整
大顶堆:ki>=k2*i(左) ,ki>=k2*i+1(右)
小顶堆:ki<=k2*i ,ki<=k2*i+1
存入数组
堆排序:待排序列,构造大顶堆,得到最大值(根),移除(与堆层序遍历末尾元素交换),重复构造大顶堆和移除操作
堆排 时间复杂度 O(nlogn):
构件堆时间复杂度O(n)
第i次取堆顶,重建堆需要O(logi),取n-1次,共O(nlogn)
空间复杂度:只有一个暂存单元用来交换
跳跃比较交换:不稳定排序
初始构建堆需要比较多次,待排序列个数少用比较浪费
归并
归并排序:n个记录,看作n个有序子序列,序列长度为1,两两归并,得到(n/2向上取整)个长度为2或1的有序子序列;重复两两归并,直到得到长度为n的有序序列
Merge
递归归并排序
非递归归并排序
时间复杂度:合并消耗O(n),log2n次合并,共O(nlogn)
非递归省略递归的时间
空间复杂度:
递归:辅助空间n,递归时深度为log2n的栈空间
非递归:只有辅助空间O(n)
快排(交换排序升级版)
枢轴:关键字,左边的值比它小,右边的值比它大
partition函数:用第一个关键做枢轴,将数组调整为枢轴成立
时间复杂度:
最优情况,O(nlogn),递归次数,对应n个结点平衡二叉树 深度 log2n向下取整+1;
最坏情况,O(n2),n-1次递归,每次需要比较n-i次
平均 O(nlogn)
空间复杂度:
最优情况,递归次数,O(logn);
最坏情况,n-1次递归,O(n)
平均 O(nlogn)
跳跃进行,不稳定
优化
1、优化选取枢轴
枢轴太大或太小,都会影响性能,(完全二叉树和斜二叉树的深度区别)
- 随机枢轴
- 三数取中:取3个关键字排序,取中间数,适合小数组
- 九数取中:3次取样,每次取3个,3个中数再取中数,适合大数组
2、优化不必要的交换,替换代替交换
3、优化小数组的排序:数组小(长度小于某一个值)时直接插入更好
4、优化递归:while代替if,减少递归