1 冒泡排序(优化)
2 选择排序
3 直接插入排序
4 希尔排序
5 堆排序
6 归并排序(优化)
7 快速排序(优化)
#define MAXSIZE 10000 /* 用于要排序数组个数最大值,可根据需要修改 */
typedef struct
{
int r[MAXSIZE+1]; /* 用于存储要排序数组,r[0]用作哨兵或临时变量 */
int length; /* 用于记录顺序表的长度 */
}SqList;
1 冒泡排序
两两比较相邻的数据,如果反序则交换,直到没有反序的记录
//改进冒泡排序
//在性能上有一些提升,避免因已经有序的情况下的无意义循环判断
void maoPao(SqList *L){
Status flag = TRUE;
//若flag为true则退出循环
for (int i = 1; i < L->length && flag; i++)
{
flag = FALSE;
for (int j = L->length - 1; j >= 1; j--)
{
if (L->r[j] > L->r[j+1])
{
int temp = L->r[i];
L->r[i] = L->r[j];
L->r[j] = temp;
flag = TRUE;
}
}
}
}
2 选择排序
//通过n-i次关键字间的比较,从n-i+1个数据中选出关键字最小的数据,并和第i(1<=i<=n)个数据交换
void xuanZe(SqList *L){
int min;
for (int i = 1; i < L->length; i++)
{
//将当前下标定义为最小值下标
min = i;
//循环当前下标之后的所有数据
for (int j = i+1; j <= L->length; j++)
{
//j=2 L[1]>L[2] 9>1
//min=2
//j=3 L[2]>L[3] 1>5
//min不变,还是2
if (L->r[min] > L->r[j])
{
min = j;
}
}
//全比较过后,交换值
if (i != min)
{
int temp=L->r[i];
L->r[i]=L->r[min];
L->r[min]=temp;
}
}
}
3 直接插入排序
//将一个数据插入到已经排好序的有序表中,得到一个新的有序表
//r[6] = {0,5,3,4,6,2}
void chaRu(SqList *L){
int i,j;
for (i = 2; i < L->length; i++)
{
//i=2,053462,3<5
//i=3,035462,4<5接着执行
if (L->r[i] < L->r[i-1])
{
//设置哨兵
//r0 = 3
L->r[0] = L->r[i];
//j=1 5>3成立,走循环体
//j=0 3>3不成立,出循环,此时j为0
for (j=i-1; L->r[j] > L->r[0]; j--)
{
//r2=5
L->r[j+1] = L->r[j];
}
//r1=3
L->r[j+1] = L->r[0];
}
}
}
4 希尔排序
//实际上是优化的直接插入排序,采取跳跃分割的策略,将相距某个"增量"的记录组成一个子序列,保证子序列内分别进行直接插入排序结果基本有序(大的基本在后面)
//时间复杂度为O(n³/2),比O(n²)要好点
void xiEr(SqList *L){
int i,j;
int zeng = L->length;
do{
//增量序列
zeng = zeng/3 + 1;
//分段排序,与直接插入排序相同
for (i = zeng+1; i <= L->length; i++)
{
//判断是否交换
if (L->r[i] < L->r[i-zeng])
{
L->r[0] = L->r[i];
for (j = i-zeng; j>0 && L->r[j] > L->r[0]; j-=zeng)
{
L->r[j+zeng] = L->r[j];
}
L->r[j+zeng] = L->r[0];
}
}
//增量为1时,停止循环
} while (zeng > 1);
}
5 堆排序
//对简单选择排序进行的一种改造,时间复杂度为O(nlogn),比冒泡,选择,插入的O(n²)好很多
//堆是一种近似完全二叉树:利用完全二叉树的深度是(log2n)+1的特性
//每个结点的值都大于或等于左右孩子结点的值,为大顶堆
//每个结点的值都小于或等于左右孩子结点的值,为小顶堆
//将待排序的序列构造成一个大顶堆,整个序列的最大值就是堆顶的根结点,将它移走,然后将剩余的n-1个序列重新构造成一个大顶堆,反复执行
//就是将堆数组的根结点与末尾元素交换,末尾元素就变成最大值了
//将数组调整为大顶堆
void daDingDui(SqList *L,int s,int m);
void dui(SqList *L){
int i;
//把有子节点的结点调整成大顶堆
for (i = L->length/2 ; i>0 ; i--)
{
daDingDui(L, i, L->length);
}
for (i = L->length; i>1 ; i--)
{
//将堆顶数据和当前未经排序子序列的最后一个数据交换
//把最大值换到尾巴处
int temp=L->r[i];
L->r[i]=L->r[1];
L->r[1]=temp;
//排除最后一个最大数据,再重新调整成大顶堆
daDingDui(L, 1, i-1);
}
}
void daDingDui(SqList *L,int s,int m){
int temp,j;
temp= L->r[s];
//沿关键字较大的孩子结点向下筛选
//根据完全二叉树的按层序递增排列特点
//从j=2s开始,j=m结束
for (j=2*s ; j<=m ; j*=2)
{
//j为左孩子下标,j+1为右孩子下标,找出最大的
if (jr[j] < L->r[j+1])
{
++j;
}
//比较根结点大小
if (temp >= L->r[j])
{
break;
}
//把最大值给根结点
L->r[s] = L->r[j];
s = j;
}
//把刚开始保存的较小值给子结点
L->r[s] = temp;
}
6 归并排序
//归并--将两个或两个以上的有序表组合成一个新的有序表
//2路归并排序:n个数据,两两归并,直到得到一个长度为n的有序序列
//总的时间复杂度为O(nlogn),空间复杂度为O(n+logn)
//比较占用内存,但是稳定,效率高
//归并函数
void realGuiBing(int SR[],int TR1[],int s,int t);
//排序函数
void Merge(int SR[],int TR[],int s,int m,int t);
//调用归并函数
void guiBing(SqList *L){
realGuiBing(L->r,L->r,1,L->length);
}
//递归函数,将 SR[s...t] 归并排序为 TR1[s...t]
void realGuiBing(int SR[],int TR1[],int s,int t){
int m;
int TR2[MAXSIZE +1];
//相等就不用排序了
if (s == t)
{
TR1[s] = SR[s];
}
else
{
//将SR[s...t]平分为SR[s...m]和SR[m+1...t]
m = (s+t)/2;
//递归将SR[s...m]归并为有序的TR2[s...m]
realGuiBing(SR,TR2,s,m);
//递归将SR[m+1...t]归并为有序的TR2[m+1...t]
realGuiBing(SR,TR2,m+1,t);
//将TR2[s...m]和TR2[m+1...t]归并到TR1[s...t]
Merge(TR2,TR1,s,m,t);
}
}
//关键函数,排序并合并
//将有序的SR[i...m] 和 SR[m+1...n]归并为有序的TR[i...n]
void Merge(int SR[],int TR[],int s,int m,int t){
int j,k,l;
//将SR中数据由小到大归并入TR
for (j = m+1, k = s; s <= m && j <= t; k++)
{
if (SR[s] < SR[j])
{
TR[k] = SR[s++];
}
else
{
TR[k] = SR[j++];
}
}
//将剩余的SR[s...m]复制到TR
if (s <= m)
{
for (l = 0; l <= m-s; l++)
{
TR[k+1] = SR[s+1];
}
}
//将剩余的SR[j...t]复制到TR
if (j <= t)
{
for (l = 0; l < t-j; l++)
{
TR[k+1] = SR[j+1];
}
}
}
//优化归并排序,非递归实现
//非递归增加的函数
void MergeAdd(int SR[], int TR[],int s,int n);
//归并排序
void guiBing2(SqList *L){
//申请内存空间
int *TR = (int *)malloc(L->length *sizeof(int));
int k = 1;
while (k < L->length)
{
MergeAdd(L->r, TR, k, L->length);
//子序列长度加倍
k = 2*k;
MergeAdd(TR, L->r, k, L->length);
//加倍
k = 2*k;
}
}
void MergeAdd(int SR[], int TR[],int s,int n){
int i = 1;
int j;
while (i <= n - 2 * s + 1)
{
//两两归并
Merge(SR, TR, i, i + s - 1, i + 2 *s -1);
i = i +2 *s;
}
//归并最后两个序列
if (i < n-s+1)
{
Merge(SR, TR, i, i+s-1, n);
}
//剩下的单数直接并入
else
{
for (j = i; j <= n; j++)
{
TR[j] = SR[j];
}
}
}
//希尔排序为直接插入排序的升级,属于插入排序类
//堆排序为简单选择排序的升级,属于选择排序类
//快速排序为冒泡排序的升级,属于交换排序类
7 快速排序
//通过一趟排序将待排数据分割成独立的两部分,其中一部分key均比另一部分key小,分别对这两部分记录继续排序,最终完成排序
//时间复杂度平均为O(nlogn)
//快速排序
void realKuaiSu(SqList *L,int low,int high);
//找到枢轴值
int fenKai(SqList *L ,int low,int high);
//调用快速排序
void kuaiSu(SqList *L){
realKuaiSu(L,1,L->length);
}
//快速排序
void realKuaiSu(SqList *L,int low,int high){
int pivot;
if (low < high)
{
//将L->r[low...high]一分为二,算出枢轴值pivot
pivot = fenKai(L,low,high);
//对低子表递归排序
realKuaiSu(L,low,pivot-1);
//对高子表递归排序
realKuaiSu(L,pivot+1,high);
}
}
//找到枢轴值,交换顺序表L中子表的数据
//将选取的枢轴值不断变换,将比它小的换到它的左边,比它大的换到它的右边,它也在交换中不断更改自己的位置,直到完全满足这个要求为止
int fenKai(SqList *L ,int low,int high){
int pivotkey;
//用子表的第一个数据做枢轴数据
pivotkey = L -> r[low];
//从表的两端交替向中间扫描
while (low < high)
{
//比枢轴数据小的数据交换到低端
while (low < high && L->r[high] >= pivotkey)
{
high--;
int temp=L->r[low];
L->r[low]=L->r[high];
L->r[high]=temp;
}
//将比枢轴大的数据交换到高端
while (low < high && L->r[low] <= pivotkey)
{
low++;
int temp=L->r[low];
L->r[low]=L->r[high];
L->r[high]=temp;
}
}
return low;
}
//快速排序优化
//1.选取枢轴pivotkey的优化
//三数取中法:取左,中,右三个数据先排序,将中间数作为枢轴
//九数取中法
int getPivotkey(SqList *L,int high,int low){
int pivotkey;
//计算数组中间元素的下标
int m = low + (high - low)/2;
//交换左右端数据,保证左端较小
if (L->r[low] > L->r[high])
{
int temp=L->r[low];
L->r[low]=L->r[high];
L->r[high]=temp;
}
//交换中间与右端数据,保证中间较小
if (L->r[m] > L->r[high])
{
int temp=L->r[m];
L->r[m]=L->r[high];
L->r[high]=temp;
}
//交换中间与左端数据,保证左端较小
if (L->r[m] > L->r[low])
{
int temp=L->r[m];
L->r[m]=L->r[low];
L->r[low]=temp;
}
//L.r[row]为三个关键字中间值
pivotkey = L->r[low];
return pivotkey;
}
//2.优化不必要的交换
int youHuaKuaiSu(SqList *L,int low,int high){
//取到枢轴
int pivotkey = getPivotkey(L, high, low);
//将枢轴备份
L->r[0] = pivotkey;
//从表的两端交替向中间扫描
while (low < high)
{
//比枢轴数据小的数据交换到低端
while (low < high && L->r[high] >= pivotkey)
{
high--;
//采用替换不是交换
L->r[low] = L->r[high];
}
//将比枢轴大的数据交换到高端
while (low < high && L->r[low] <= pivotkey)
{
low++;
//采用替换不是交换
L->r[high] = L->r[low];
}
}
//将枢轴数值替换回L.r[low]
L->r[low] = L->r[0];
//返回枢轴所在位置
return low;
}
//因为递归的使用,非常大的数组用快速排序,直接插入排序是简单排序中性能最好的
经过优化的快速排序是性能最好的排序算法
从最好情况看,选择冒泡和直接插入排序
从最坏情况看,选择堆排序与归并排序
从稳定性来看,选择归并排序
如果执行算法的软件所处的环境非常在乎内存使用量的多少,选择堆排序等