文章目录
一.排序算法
首先明白几个概念
(1)排序稳定性
简单来讲就是对于两个相同的元素,比如a1,a2,开始排序时a1在a2前面,那么排序结束后位置仍然如此,则为稳定算法,否则就是不稳定算法。在下面介绍的几种排序算法中,稳定排序算法有,不稳定排序算法有
(2)内排序和外排序
根据待排序记录是不是全放在内存中,排序分为外排序和内排序,内排序当然是所有记录都放在内存中,外排序是记录太多,内存放不下,所以内外存都有数据,排序时要内外交换数据。主要的内排序算法有:插入排序,交换排序,选择排序,归并排序。
(3)算法衡量标准
无非就是两个,一是耗费的空间越小越好,二是所用的时间越短越好,也就是时间复杂度和空间复杂度越低越好
接下来进入正题
1.简单选择排序
思想就是正常人的思维,从头开始扫描到尾,首先找出最小的记录放在第一位,把第一位的数和最小记录的数交换一下位置,然后再找出次小的放在第二位,继续交换第二位原本的数和此次找出次小数的位置······最后整个表都有序了。
数字 | 3 | 8 | 9 | 1 | 0 | 2 | |
---|---|---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
起始状态
数字 | 0 | 8 | 9 | 1 | 3 | 2 | |
---|---|---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
第一次选择排序,最小的在下标为5的位置,交换下标为1和下标为5的数字,下标为1的位置有序了
数字 | 0 | 1 | 9 | 8 | 3 | 2 | |
---|---|---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
第二次选择排序,次小的在下标为4的位置,交换下标为2和下标为4的数字,下标为2的位置有序了Ok,后面步骤类似,直到整个列表有序。下表为0位置可以用来标记整个表是否排完或者存放中间临时变量。
不墨迹,上代码
#include<stdio.h>
#define Maxsize 10
typedef struct
{
int r[Maxsize];//储存要排序数组,r[0]用作哨兵
int length;//顺序表长度
}Sqlist;
void swap(Sqlist *L,int i,int j)//交换顺序表中i和j位置上的值
{
int temp=L->r[i];
L->r[i]=L->r[j];
L->r[j]=temp;
}
int SelectSort(Sqlist *L)
{
int i,j,min;
for(i=1;i<=L->length;i++)
{
min=i;
for(j=i+1;j<=L->length;j++)
{
if(L->r[min]>L->r[j])
min=j;
}
swap(L,i,min);
}
}
int main()
{
Sqlist L;
int i;
for(i=1;i<6;i++)
scanf("%d",&L.r[i]);
L.length=5;
SelectSort(&L);
for(i=1;i<6;i++)
printf("%d",L.r[i]);
}
算法复杂度O(n^2)
2.冒泡排序
每一次都进行两两相邻元素比较,小一点的移到前面,直到所有位置都有序,有n个元素的表要经过n-1轮冒泡,每一轮又要经过n-i次相邻比较换位
第一趟冒泡,从后向前比较2移到了最末的位置
下标 | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
初始 | 7 | 2 | 1 | 9 | |
7 | 2 | 1 | 9 | ||
7 | 1 | 2 | 9 | ||
完成1位置 | 1 | 7 | 2 | 9 |
#include<stdio.h>
#define Maxsize 10
typedef struct
{
int r[Maxsize];//储存要排序数组,r[0]用作哨兵
int length;//顺序表长度
}Sqlist;
void swap(Sqlist *L,int i,int j)//交换顺序表中i和j位置上的值
{
int temp=L->r[i];
L->r[i]=L->r[j];
L->r[j]=temp;
}
int BubbleSort1(Sqlist *L)
{
int i,j;
for(i=1;i<=L->length;i++)
{
for(j=L->length;j>i;j--)
{
if(L->r[j]<L->r[j-1])
swap(L,j,j-1);
}
}
return 1;
}
int main()
{
Sqlist L;
int i;
for(i=1;i<6;i++)
scanf("%d",&L.r[i]);
L.length=5;
BubbleSort1(&L);
for(i=1;i<6;i++)
printf("%d",L.r[i]);
}
还可以优化一下
//相邻两数不断交换位置,通过标志旗帜来解决后续已经有序但仍需继续比较的问题
#include<stdio.h>
#define Maxsize 10
typedef struct
{
int r[Maxsize];//储存要排序数组,r[0]用作哨兵
int length;//顺序表长度
}Sqlist;
void swap(Sqlist *L,int i,int j)//交换顺序表中i和j位置上的值
{
int temp=L->r[i];
L->r[i]=L->r[j];
L->r[j]=temp;
}
int BubbleSort2(Sqlist *L)
{
int i,j;
int flag=1;//flag用来做标记
for(i=1;i<=L->length&&flag;i++)//flag为0退出循环
{
flag=0;
for(j=L->length;j>i;j--)
{
if(L->r[j]<L->r[j-1])
{
swap(L,j,j-1);
flag=1;//有数据交换,flag就为1
}
}
printf("%d ",flag);
}
return 1;
}
int main()
{
Sqlist L;
int i;
for(i=1;i<6;i++)
scanf("%d",&L.r[i]);
L.length=5;
BubbleSort2(&L);
for(i=1;i<6;i++)
printf("%d",L.r[i]);
}
算法复杂度O(n^2
3.插入排序
从头开始,默认第一个位置已经排序好,其他的插入,形成一个有序的表中
//默认第一个是有序的,其他的以其为基准不断往里调整顺序
#include<stdio.h>
#define Maxsize 10
typedef struct
{
int r[Maxsize];//储存要排序数组,r[0]用作哨兵
int length;//顺序表长度
}Sqlist;
void swap(Sqlist *L,int i,int j)//交换顺序表中i和j位置上的值
{
int temp=L->r[i];
L->r[i]=L->r[j];
L->r[j]=temp;
}
int InsertSort(Sqlist *L)
{
int i,j;
for(i=2;i<=L->length;i++)
{
if(L->r[i]<L->r[i-1])
{
L->r[0]=L->r[i];//设置哨兵
for(j=i-1;L->r[j]>L->r[0];j--)
L->r[j+1]=L->r[j];//记录后移
L->r[j+1]=L->r[0];//插入到正确位置
}
}
}
int main()
{
Sqlist L;
int i;
for(i=1;i<6;i++)
scanf("%d",&L.r[i]);
L.length=5;
InsertSort(&L);
for(i=1;i<6;i++)
printf("%d",L.r[i]);
}
算法复杂度O(n^2)
4.希尔排序
相隔某个增量来组成一个子序列,局部有序,最后达到整体有序这个增量的选择dlta[k]=2^(t-k+1)-1(0<=k<=t<=[log 2底(n+1)])(向下取整),可以取得好一点效果
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
初始 | 4 | 7 | 1 | 9 | 3 |
计算increment=6/3+1=3,i=increment+1=4,r[4]=9>r[i-increment]=r[1]=4,不交换, i+1=5,r[5]=3<r[i-increment]=r[2]=7,交换位置
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
第一次 | 4 | 3 | 1 | 9 | 7 |
计算increment=4/3+1=2,i=increment+1=3,r[3]=1<r[i-increment]=r[1]=4,交换, i+1=4,r[4]=9>r[i-increment]=r[2]=3,不交换
i+1=5,r[5]=7>r[i-increment]=r[3]=4,不交换
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
第二次 | 1 | 3 | 4 | 9 | 7 |
计算increment=2/3+1=1,最后一次循环i=increment+1=2,r[2]=3<r[i-increment]=r[1]=1,不交换,
i+1=3,r[3]=4>r[i-increment]=r[2]=3,不交换
i+1=4,r[4]=9>r[i-increment]=r[3]=4,不交换
i+1=5,r[5]=7<r[i-increment]=r[4]=9,交换位置
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
第二次 | 1 | 3 | 4 | 7 | 9 |
#include<stdio.h>
#define Maxsize 10
typedef struct
{
int r[Maxsize];//储存要排序数组,r[0]用作哨兵
int length;//顺序表长度
}Sqlist;
void swap(Sqlist *L,int i,int j)//交换顺序表中i和j位置上的值
{
int temp=L->r[i];
L->r[i]=L->r[j];
L->r[j]=temp;
}
int ShellSort(Sqlist *L)
{
int i,j;
int increment=L->length;
do{
increment=increment/3+1;
for(i=increment+1;i<L->length;i++)
{
if(L->r[i]<L->r[i-increment])
{
swap(L,i,i-increment);
}
}
}while(increment>1);
}
int main()
{
Sqlist L;
int i;
for(i=1;i<6;i++)
scanf("%d",&L.r[i]);
L.length=6;
ShellSort(&L);
for(i=1;i<6;i++)
printf("%d ",L.r[i]);
}
时间复杂度O(n^(3/2))
5.堆排序
堆:大根堆,堆顶元素比其他元素都要大的堆。小根堆,堆顶元素比其他元素都要小的堆。堆是一种完全二叉树。
按照层序遍历方式从1开始编号,结点满足如下关系**{k(i)>=k(2i),k(i)>=k(2i+1)}或{k(i)<=k(2i),k(i)<=k(2i+1)}(1<=i<=[n/2])**,用顺序表实现
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
大根堆 | 90 | 80 | 70 | 60 | 10 | 40 | 50 |
k[i]=70>k[2i]=40, k[i]=70>k[2i+1]=50
小根堆同理
主要思想:待排序序列构成一个堆(比如大根堆),整个序列最大值就是堆顶结点,将他移开(也就是和末尾结点交换位置),剩下的堆重新组成大根堆,移开,重组,如此往复······,就成了一个从小到大排列的顺序表。
问题关键集中在了根堆调整,我将它总结为12字法则:从下到上(寻找有叶节点的根节点顺序从大下标到小下标),从右到左(同一层次根节点,其实跟第一条差不多,也是大下标到小下标),从上到下(针对某一根节点,将它之后的结点调整,也就是顺序表中下标比他大的所有点),具体怎么肥事呢?看我灵魂画手
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GKP5gUIC-1621241510124)(https://i.loli.net/2018/07/28/5b5c1e7257490.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UC73I9e3-1621241510126)(https://i.loli.net/2018/07/28/5b5c1df83eccf.jpg)]
//堆排序
//从下到上。从左到右依次调整根堆,将堆顶元素依次与尾结点交换位置
#include<stdio.h>
#define Maxsize 10
typedef struct
{
int r[Maxsize];//储存要排序数组,r[0]用作哨兵
int length;//顺序表长度
}Sqlist;
void swap(Sqlist *L,int i,int j)//交换顺序表中i和j位置上的值
{
int temp=L->r[i];
L->r[i]=L->r[j];
L->r[j]=temp;
}
void AdjustHeap(Sqlist *L,int s,int m)
{
int temp,i;
temp=L->r[s];
for(i=2*s;i<=m;i*=2)//从上到下原则
{
if(i<m)//当前下标为i的结点还有右节点
i=L->r[i+1]>L->r[i]?i+1:i;//较大的孩子下标
if(temp>=L->r[i])
break;//根节点比孩子大,不需要调整
L->r[s]=L->r[i];//否则调整位置
L->r[i]=temp;
s=i;//控制循环作用,后面的每个结点都要调整为大根堆
}
}
void HeapSort(Sqlist *L)
{
int i;
//把初始状态的r建立成一个大根堆
for(i=L->length/2;i>0;i--)//从下到上原则
{
AdjustHeap(L,i,L->length);
}
//排序阶段,将堆顶元素和当前未经排序子序列末尾结点交换位置
for(i=L->length;i>0;i--)
{
swap(L,1,i);
AdjustHeap(L,1,i-1);
}
}
int main()
{
Sqlist L;
int i;
for(i=1;i<6;i++)
scanf("%d",&L.r[i]);
L.length=5;
HeapSort(&L);
for(i=1;i<6;i++)
printf("%d ",L.r[i]);
}
算法复杂度O(nlogn)
6.归并排序
我用java建了一个基类实现基本的交换方法,子类对sort函数进行重写
package sort;
public class NecessarySort {
public int[] a= {1,6,4,2,9,7};
public int l=a.length;
public void Swap(int[] a,int i,int j) {
int m=a[i];
a[i]=a[j];
a[j]=m;
}
public void print(int[] a) {
for(int i=0;i<a.length;i++) {
System.out.print(a[i]+" ");
}
System.out.println();
}
public void Sort() {};
}
package sort;
//归并排序
public class MergeSort extends NecessarySort {
public static void main(String[] args) {
MergeSort mergeSort=new MergeSort();
mergeSort.Sort(mergeSort.a,0,mergeSort.l-1);
mergeSort.print(mergeSort.a);
}
public void Sort(int[] a,int p,int q) {
if(p>=q) return;
int s=p+(q-p)/2;
Sort(a,p,s);//对两边分别递归处理
Sort(a,s+1,q);
MergeAll(a,p,s,q);//合并过程
}
public void MergeAll(int[] a,int p,int r,int q) {
int i=p,j=r+1,k=0;
int[] newArray=new int[q-p+1];
//对临时数组进行操作,使之有序
while(i<=r&&j<=q) {
if(a[i]<=a[j]) {
newArray[k++]=a[i++];
}
else
newArray[k++]=a[j++];
}
// 判断哪个子数组中有剩余的数据
int start = i;
int end = r;
if (j <= q) {
start = j;
end = q;
}
//剩余数据拷入临时数组
for(int m=start;m<=end;m++)
newArray[k++]=a[m];
//将数组拷贝回原数组
for(i=0;i<newArray.length;++i)
a[p+i]=newArray[i];
}
}
7.快速排序
也是一个局部有序达到整体有序的算法,分而治之思想,选取一个中间枢纽的值,小的排在他前面,大的排在他后面,这样就分成了两部分,然后对每一部分重复此动作,即选取中间枢纽调整次序,然后再选取调整·····相信你已经看出,这需要迭代
(1)初始状态r[9],L->length=8
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
数字 | 5 | 7 | 1 | 9 | 4 | 3 | 8 | 6 |
(2)刚开始选取r[1]作为枢纽,确定low,high位置为表第一个1和最后一个位置8
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
数字 | 5(low) | 7 | 1 | 9 | 4 | 3 | 8 | 6(high) |
(3)high比枢纽值大或相等,保持原位不变,high–否则high与low交换位置,high与low指向的位置不变
r[8]=6>r[1]=5,high–变为7,同理变为6,但是因为r[6]=3<5,所以low与high交换数字,high仍为6
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
数字 | 3(low) | 7 | 1 | 9 | 4 | 5(high) | 8 | 6 |
(4)low比枢纽值大或相等,保持原位不变,low++,否则high与low交换位置,high与low指向的位置不变
r[1]=3<5,low++变为2,但是因为r[2]=7>5,所以low与high交换数字,low仍为2
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
数字 | 3 | 5(low) | 1 | 9 | 4 | 7(high) | 8 | 6 |
(5)继续进行,看high,r[6]=7>5,high变为5,r[5]=4<5.low,high交换位置
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
数字 | 3 | 4(low) | 1 | 9 | 5(high) | 7 | 8 | 6 |
看low,r[2]=4<5,low变为3,同理low变为4此时r[4]=9>5,low,high换位
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
数字 | 3 | 4 | 1 | 5(low) | 9(high) | 7 | 8 | 6 |
(6)最后r[5]>5,high=low=4,这一轮终结,此时左边都比5小,右边都比5大,后续将r[1···3],r[5···8]同样步骤进行处理,然后每个再分为两部分,最后整体有序
//快速排序
//
#include<stdio.h>
#define Maxsize 10
typedef struct
{
int r[Maxsize];//储存要排序数组,r[0]用作哨兵
int length;//顺序表长度
}Sqlist;
void swap(Sqlist *L,int i,int j)//交换顺序表中i和j位置上的值
{
int temp=L->r[i];
L->r[i]=L->r[j];
L->r[j]=temp;
}
int Partition(Sqlist *L,int low,int high)
{
int p=L->r[low];//用表中第一个位置作为枢纽记录
while(low<high)
{
//比枢纽小的交换到低端
while(low<high&&L->r[high]>=p)
high--;
swap(L,low,high);
//比枢纽大的交换到高端
while(low<high&&L->r[low]<=p)
low++;
swap(L,low,high);
}
return low;//返回枢纽所在位置
}
//对顺序表的子序列做快排r[low...high]
void QSort(Sqlist *L,int low,int high)
{
int p;
if(low<high)
{
p=Partition(L,low,high);//将L一分为二,求出枢纽值位置p
QSort(L,low,p-1);//低子表递归排序
QSort(L,p+1,high);//高子表递归排序
}
}
void QuickSort(Sqlist *L)
{
QSort(L,1,L->length);
}
int main()
{
Sqlist L;
int i;
for(i=1;i<6;i++)
scanf("%d",&L.r[i]);
L.length=5;
QuickSort(&L);
for(i=1;i<6;i++)
printf("%d ",L.r[i]);
}
优化
(1)优化中间枢纽值
在这里我们的中间枢纽值总是选取第一个值,这难免会产生问题,就是这个值是一个极端的值,比如{9,3,2,4,5,7},那么只是把9与7交换位置,其他不改变,之后的比较是多余的,使得整体性能下降。这个中间枢纽值理论上在为整个数组中值的时候算法性能最优,所以我们可以采取三点取中法,由于随机选取三点对系统开销太大,所以我们固定这三点为low,high和middle
//p值选取变为三点取中low,high,middle
#include<stdio.h>
#define Maxsize 10
typedef struct
{
int r[Maxsize];//储存要排序数组,r[0]用作哨兵
int length;//顺序表长度
}Sqlist;
void swap(Sqlist *L,int i,int j)//交换顺序表中i和j位置上的值
{
int temp=L->r[i];
L->r[i]=L->r[j];
L->r[j]=temp;
}
int Partition(Sqlist *L,int low,int high)
{
int p,m;
m=low+(low+high)/2;//计算数组中间下标
if(L->r[low]>L->r[high])
swap(L,low,high);//交换左右数据,保证左端较小
if(L->r[m]>L->r[high])
swap(L,m,high);//交换右中数据,保证中间较小
if(L->r[low]<L->r[m])
swap(L,low,m);//交换左中数据,保证中间较小,low即为中间值
p=L->r[low];//用表中第一个位置作为枢纽记录
while(low<high)
{
//比枢纽小的交换到低端
while(low<high&&L->r[high]>=p)
high--;
swap(L,low,high);
//比枢纽大的交换到高端
while(low<high&&L->r[low]<=p)
low++;
swap(L,low,high);
}
return low;//返回枢纽所在位置
}
//对顺序表的子序列做快排r[low...high]
void QSort(Sqlist *L,int low,int high)
{
int p;
if(low<high)
{
p=Partition(L,low,high);//将L一分为二,求出枢纽值位置p
QSort(L,low,p-1);//低子表递归排序
QSort(L,p+1,high);//高子表递归排序
}
}
void QuickSort(Sqlist *L)
{
QSort(L,1,L->length);
}
int main()
{
Sqlist L;
int i;
for(i=1;i<6;i++)
scanf("%d",&L.r[i]);
L.length=5;
QuickSort(&L);
for(i=1;i<6;i++)
printf("%d ",L.r[i]);
}
(2)优化交换位置
low,high交换数据也是影响速度的一个因素,可以把交换改成直接赋值,方法就是将枢纽值存在r[0]中,真是千呼万唤始出来啊,r[0]终于有用了。
//把swap变为=
#include<stdio.h>
#define Maxsize 10
typedef struct
{
int r[Maxsize];//储存要排序数组,r[0]用作哨兵
int length;//顺序表长度
}Sqlist;
int Partition(Sqlist *L,int low,int high)
{
int p=L->r[low];//用表中第一个位置作为枢纽记录
L->r[0]=p;//枢纽关键值备份到r[0]
while(low<high)
{
//比枢纽小的交换到低端
while(low<high&&L->r[high]>=p)
high--;
L->r[low]=L->r[high];//替换而不是交换
//比枢纽大的交换到高端
while(low<high&&L->r[low]<=p)
low++;
L->r[high]=L->r[low];
}
L->r[low]=L->r[0];//将枢纽替换回L->r[low],也可以是L->r[high],因为此时high==low
return low;//返回枢纽所在位置
}
//对顺序表的子序列做快排r[low...high]
void QSort(Sqlist *L,int low,int high)
{
int p;
if(low<high)
{
p=Partition(L,low,high);//将L一分为二,求出枢纽值位置p
QSort(L,low,p-1);//低子表递归排序
QSort(L,p+1,high);//高子表递归排序
}
}
void QuickSort(Sqlist *L)
{
QSort(L,1,L->length);
}
int main()
{
Sqlist L;
int i;
for(i=1;i<6;i++)
scanf("%d",&L.r[i]);
L.length=5;
QuickSort(&L);
for(i=1;i<6;i++)
printf("%d ",L.r[i]);
}
(3)优化小数组时排序方案
《庄子·逍遥游》 中有一篇讲述了一个大葫芦的故事,惠子中了个大葫芦,因为太大不知其用途,感慨它大而无用而庄子觉得,大有大的用途,小有小的用途,大葫芦可以剖开当船,去江湖上漂。这里的快排遇到大数组确实很快,但是当遇到小数组的时候,确不如直接插入排序,因为快排递归的缘故,有点大材小用,这时就要看我们的小葫芦了。思想是:设置一个确定小数组的阈值,比它小用直接插入,比它大用快排。“大锤八十,小锤四十”,演变成:“大数快排,小数直插”
//“大数快排,小数直插”
#include<stdio.h>
#define Maxsize 10
#include<stdio.h>
#define MAX_LENGTH_QUICKSORT 2
typedef struct
{
int r[Maxsize];//储存要排序数组,r[0]用作哨兵
int length;//顺序表长度
}Sqlist;
int InsertSort(Sqlist *L)
{
int i,j;
for(i=2;i<=L->length;i++)
{
if(L->r[i]<L->r[i-1])
{
L->r[0]=L->r[i];//设置哨兵
for(j=i-1;L->r[j]>L->r[0];j--)
L->r[j+1]=L->r[j];//记录后移
L->r[j+1]=L->r[0];//插入到正确位置
}
}
}
int Partition(Sqlist *L,int low,int high)
{
int p=L->r[low];//用表中第一个位置作为枢纽记录
L->r[0]=p;//枢纽关键值备份到r[0]
while(low<high)
{
//比枢纽小的交换到低端
while(low<high&&L->r[high]>=p)
high--;
L->r[low]=L->r[high];//替换而不是交换
//比枢纽大的交换到高端
while(low<high&&L->r[low]<=p)
low++;
L->r[high]=L->r[low];
}
L->r[low]=L->r[0];//将枢纽替换回L->r[low],也可以是L->r[high],因为此时high==low
return low;//返回枢纽所在位置
}
//对顺序表的子序列做快排r[low...high]
void QSort(Sqlist *L,int low,int high)
{
int p;
if((high-low)>MAX_LENGTH_QUICKSORT)
{
p=Partition(L,low,high);//将L一分为二,求出枢纽值位置p
QSort(L,low,p-1);//低子表递归排序
QSort(L,p+1,high);//高子表递归排序
}
else
InsertSort(L);
}
void QuickSort(Sqlist *L)
{
QSort(L,1,L->length);
}
int main()
{
Sqlist L;
int i;
for(i=1;i<6;i++)
scanf("%d",&L.r[i]);
L.length=5;
QuickSort(&L);
for(i=1;i<6;i++)
printf("%d ",L.r[i]);
}
(4)优化递归操作
待排序序列分布不均衡时,递归可能会出现深度趋近于n的情况,将递归改为迭代可以让栈深度变小
//快速排序3
//迭代变为尾递归
#include<stdio.h>
#define Maxsize 10
#include<stdio.h>
#define MAX_LENGTH_QUICKSORT 2
typedef struct
{
int r[Maxsize];//储存要排序数组,r[0]用作哨兵
int length;//顺序表长度
}Sqlist;
int InsertSort(Sqlist *L)
{
int i,j;
for(i=2;i<=L->length;i++)
{
if(L->r[i]<L->r[i-1])
{
L->r[0]=L->r[i];//设置哨兵
for(j=i-1;L->r[j]>L->r[0];j--)
L->r[j+1]=L->r[j];//记录后移
L->r[j+1]=L->r[0];//插入到正确位置
}
}
}
int Partition(Sqlist *L,int low,int high)
{
int p=L->r[low];//用表中第一个位置作为枢纽记录
L->r[0]=p;//枢纽关键值备份到r[0]
while(low<high)
{
//比枢纽小的交换到低端
while(low<high&&L->r[high]>=p)
high--;
L->r[low]=L->r[high];//替换而不是交换
//比枢纽大的交换到高端
while(low<high&&L->r[low]<=p)
low++;
L->r[high]=L->r[low];
}
L->r[low]=L->r[0];//将枢纽替换回L->r[low],也可以是L->r[high],因为此时high==low
return low;//返回枢纽所在位置
}
//对顺序表的子序列做快排r[low...high]
void QSort(Sqlist *L,int low,int high)
{
int p;
if((high-low)>MAX_LENGTH_QUICKSORT)
{
while(low<high)
{
p=Partition(L,low,high);
QSort(L,low,p-1);//尾递归
low=p+1;
//这句和QSort(L,p+1,high);差不多
}
}
else
InsertSort(L);
}
void QuickSort(Sqlist *L)
{
QSort(L,1,L->length);
}
int main()
{
Sqlist L;
int i;
for(i=1;i<6;i++)
scanf("%d",&L.r[i]);
L.length=5;
QuickSort(&L);
for(i=1;i<6;i++)
printf("%d ",L.r[i]);
}
参考资料:程杰《大话数据结构》