这是一个新手的总结。
数据的排序方法有许多,这里总结一下我所知道的。
一:选择排序。
插入排序的时间复杂度最好的情况是已经是正序的序列,只需比较(n-1)次,时间复杂度为O(n),最坏的情况是倒序的序列,要比较n(n-1)/2次,时间复杂度为O(n^2 ) ,平均的话要比较时间复杂度为O(n^2 )。
插入排序适用于数据量小的时候,当数据量增大时就会过于消耗时间。
基本思想:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在待排序的数列最前面,直到所有的待排序数据元素排完,需要用两层循环。
方法步骤
1,读入数据,存放在一个数组中,
2,从数组a[1]~~a[n]中寻找最小(或最大)的元素与第一位的元素交换
3,从数组a[2]~~a[n]中寻找最小(或最大)的元素与第二位的元素交换
......
直到第n-1各元素和最后一个元素比较排序后结束。
代码如下:
#include<iostream>
using namespace std;
int s[10002];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;++i)
cin>>s[i];
int x;//记录最小的位置
int temp;//交换的时候用 也可以用swap
for(int i=1;i<n;++i)//循环n-1次
{
x=i;
for(int j=i;j<=n;++j)
if(s[x]>s[j])
x=j;
if(x!=i)
temp=s[i],s[i]=s[x],s[x]=temp;
/*
或者直接用 swap(s[i],s[x]);
*/
}
for(int i=1;i<=n;++i)
cout<<" "<<s[i];
return 0;
}
二:冒泡排序
冒泡排序的平均时间复杂度为O(n^2),但当输入的数据已经是正序的序列时,只需比较(n-1)次,时间复杂度为O(n)。
同样冒泡排序适用于数据量少的时候,但大量数据时比较容易超时。
基本思想:比较相邻的两个元素的大小如果逆序就交换,一直到数列的最后,经过一次的比较能够找出最大或最小的元素并放在正确位置,然后前面剩余的元素重复这个过程,一直到比较最后两个元素。这样就像冒泡一样将最大或最小的元素放到数组的最后面。
方法步骤:(以升序排列为例)
1,读入数据,并存放在数组中,
2,比较相邻的前后两个数据,如果前面的数据大于后面的数据,交换两个数据。
3,对数组所有元素进行一次遍历后,最大的元素就好像冒泡一样跑到了数组的最后
4,对剩余的元素重复上一个的过程,直到剩下两个元素比较结束。
代码如下:
#include<iostream>
using namespace std;
int s[10001];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;++i)
cin>>s[i];
for(int i=n-1;i>=1;--i)//进行n-1次循环
{
for(int j=0;j<i;++j)
{
if(s[j]>s[j+1])//判断大小
{
swap(s[j],s[j+1]);//交换两个元素
}
}
}
for(int i=0;i<n;++i)
cout<<s[i]<<" ";
return 0;
}
冒牌排序可以加一个bool变量优化,见代码:
bool ok;
for(int i=1;i<=n-1;++i)//进行n-1次循环,另一种写法
{
ok=true;
for(int j=0;j<n-i;++j)
{
if(s[j]>s[j+1])
{
swap(s[j],s[j+1]);
ok=false;
}
}
if(ok)
break;
}
这种优化减少了无用的循环次数。
三:插入排序
插入排序的平均时间复杂度与前两种方法相同,时O(n^2)。当待排数组有序时,此时复杂度为O(N),当待排数组是逆序时,时间复杂度为O(N^2)。同样对小数据量的运算占有优势,另一个优势是可以对大小相同的元素进行元素元素下标排序。
基本思想:回忆一下打牌时的情景,一边抓牌一边将其放到合适的位置,当结束抓牌时手牌是有序的。正式一点就是说读入一个元素,寻找它的正确位置,将其放入,当不要忘了把后面的元素都要后移一位。
方法步骤:
1,读入元素,放入数组中,
2,从第二个元素开始判断位置,将其放入,
3,循环上一个过程,直到将最后一个元素放入正确的位置。
详细见代码;
#include<iostream>
using namespace std;
int s[10001];
int main()
{
int n,j,temp;
cin>>n;
for(int i=0;i<n;++i)
cin>>s[i];
for(int i=0;i<n;++i)
{
for( j=i-1;j>=0;--j)//判断位置
if(s[j]<s[i])
break;
if(j!=i-1)//位置改变了
{
temp=s[i];//保存下来
for(int t=i;t>j+1;--t)//把数字后移
s[t]=s[t-1];
/*
for(int t=i-1;t>j;--t)
s[t+1]=s[t];
与上面那个意义一样。
*/
s[j+1]=temp;
}
}
for(int i=0;i<n;++i)
cout<<s[i]<<" ";
}
四:堆排序
堆排序的时间复杂度是最短的一种方法,只有O(n),但是对数组空间的要求对于前四个要大,且只用于数字的排序。
基本思想:若待排序的元素的值在一个明显的范围内,可将有限个相同的元素存放在一个桶内,桶的序号就是元素的值。
方法步骤:
在输入元素的同时,计算相同元素个数并将其储存起来,
输出时只要将每个元素按个数将“桶”的编号输出即可。
详细见代码:
#include<iostream>
#include<cstdio>//memset(s,0,sizeof(s)) 用到
int s[101];
using namespace std;
int main()
{
int n;
cin>>n;
int k;//输入的那个数
for(int i=1;i<=n;++i)
{
cin>>k;
s[k]++;//储存相同元素的个数
}
for(int i=0;i<=100;++i)
{
while(s[i]!=0)//每个元素按个数是输出,输出的同时就是有序的。
{
cout<<i<<" ";
--s[i];
}
}
return 0;
}
五:快速排序
快速排序的时间复杂度O(nlog2n)相较与冒泡排序来说要短。但是存在一定的不稳定性,可能时间复杂度能达到O(n^2)。
快速排序是对冒泡排序的一种改进,减少了比较和循环次数
基本思想:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分小,则可分别对这两部分继续进行排序,以达到整个序列有序。
方法步骤;
1,将序列分为前后两部分,取序列的中间值为分割数记为mid,
2,升序排列时前一部分找比mid大的数,后一部分找比mid小的数,
3,判断两者的位置,符合要求两者交换,
4,观察排序是否到了边界,如果未到则递归分开搜索左右区间。
目前而言快速排序法还是被认为最好的一种排序方法。
看代码前先看一张图解:
再来看代码:
#include<iostream>
using namespace std;
int s[100001];
int qsort(int l,int r)//将大的放在数组右边,将小的放在左边
{
int i=l,j=r;//下面的计算不能直接调用l与r,后面需要判断是否排序完成需要用到l r
int mid=s[(l+r)/2];//将当前序列的中间作为分隔数
do
{
//如果选择降序排列,将下面两个while的大小判断交换即可
while(s[i]<mid)
++i;//寻找左面大于中间数的数
while(s[j]>mid)
--j;//右面小于中间数的数
if(i<=j)
{
swap(s[i],s[j]);//交换
++i,--j;
}
}
while(i<=j);//继续寻找
if(j>l) qsort(l,j);//寻找到最后j==l,i==r
if(i<r) qsort(i,r);//未到两个数的边界,递归继续搜索
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;++i)//下表表示位置,最好从1开始
cin>>s[i];
qsort(1,n);
cout<<s[1];
for(int i=2;i<=n;++i)
cout<<" "<<s[i];
return 0;
}
六:归并排序
归并排序的时间复杂度是O(nlog2n),但是所需要的内存空间要大于其它的排序方法,她需要一个辅助数组来完成。
基本思想:该算法是分治法(Divide and Conquer)的典型应用。先将每一个子序列排序,再将有序的子序列合并,得到完全有序的数列,方式称为二路归并。
步骤比较抽象,还是先看一张图片
解释在代码中:
#include<iostream>
using namespace std;
int str[10001];
int a[10001];//过程使用
void msort(int s,int t)
{
if(s==t)
return ;
int mid=(s+t)/2;
msort(s,mid);//分解左序列
msort(mid+1,t);//分解右序列
int i=s,j=mid+1,k=s;
while(i<=mid&&j<=t)
{
if(str[i]<=str[j])//寻找数小的,即生序排列
//换成大写则降序排列
{
a[k]=str[i];//将每一个符合条件的数存到a[]辅助数组中
++i,++k;
}
else
{
a[k]=str[j];
++j,++k;
}
}
//下面这一步的意思是将剩余的原本就符合条件的复制到a[]中排序
while(i<=mid)//把左边的序列剩余的从s[]复制到a[]
{
a[k]=str[i];
++k,++i;
}
while(j<=t)//把右边的序列剩余的从s[]复制到s[]
{
a[k]=str[j];
++k,++j;
}
for(int i=s;i<=t;++i)//将数组返回
str[i]=a[i];
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;++i)
cin>>str[i];
msort(1,n);
for(int i=1;i<=n;++i)
cout<<str[i]<<" ";
return 0;
}
欢迎评论指出错误,大家一起进步。
主要参考资料
(C++版)信息学奥赛一本通