您的位置:首页 > 理论基础 > 数据结构算法

PAT 数据结构 07-排序1. 排序(25) 8种排序的比较

2015-07-13 16:48 429 查看
给定N个(长整型范围内的)整数,要求输出从小到大排序后的结果。

本题旨在测试各种不同的排序算法在各种数据情况下的表现。各组测试数据特点如下:

数据0:只有1个元素;

数据1:11个不相同的整数,测试基本正确性;

数据2:103个随机整数;

数据3:104个随机整数;

数据4:105个随机整数;

数据5:105个顺序整数;

数据6:105个逆序整数;

数据7:105个基本有序的整数;

数据8:105个随机正整数,每个数字不超过1000。

输入格式:

输入第一行给出正整数N(<= 105),随后一行给出N个(长整型范围内的)整数,其间以空格分隔。

输出格式:

在一行中输出从小到大排序后的结果,数字间以1个空格分隔,行末不得有多余空格。
输入样例:
11
4 981 10 -17 0 -20 29 50 8 43 -5

输出样例:
-20 -17 -5 0 4 8 10 29 43 50 981
/*2015.7.13cyq*/
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

//选择排序O(N2),case0~8(ms):1,1,2,39,后面100K个int的5个case超时
void selectSort(vector<int> &a,int begin,int end){
for(int i=begin;i<end;i++){
int min=i;
for(int j=i+1;j<=end;j++){
if(a[j]<a[min])
min=j;
}
swap(a[i],a[min]);
}
}
//冒泡排序O(N2),case0~8(ms):1,1,2,124(比选择排序差),后面100K个int的5个case超时
void bubbleSort(vector<int> &a,int begin,int end){
int n=end-begin+1;
for(int i=0;i<n-1;i++)
for(int j=begin;j<end-i;j++){
if(a[j]>a[j+1])
swap(a[j],a[j+1]);
}
}
//直接插入排序,case0~8(ms):1,1,2,21,1656,38,超时,53,1653
void insertSort(vector<int> &a,int begin,int end){
int tmp;
for(int i=begin+1;i<=end;i++){
tmp=a[i];
int j=i-1;
while(j>=begin&&a[j]>tmp){
a[j+1]=a[j];
j--;
}
a[j+1]=tmp;
}
}
//希尔排序,将序号相差increment的记录组成子序列,在子序列内部直接插入排序,让整个序列基本有序
//逐渐减小increment,最后一次增量为1,退化为对基本有序数组进行直接插入排序
//case0~8(ms):1,1,2,6,48,40,41,40,40
void shellSort(vector<int> &a,int begin,int end){
int i,j,tmp;
int n=end-begin+1;
int increment=n;
do{
increment=increment/3+1;//increment为1时就是普通的直接插入排序
for(i=begin+increment;i<=end;i++){
tmp=a[i];
j=i-increment;
while(j>=begin&&a[j]>tmp){
a[j+increment]=a[j];
j=j-increment;
}
a[j+increment]=tmp;
}
}while(increment>1);
}
//堆排序,建立大根堆,排序时每次都把堆顶元素换到最后,调整大根堆
//具体注释可见我的另一篇博客PAT 数据结构 04-树9. Path in a Heap (25)
//case0~8(ms):1,1,1,6,47,44,44,44,40
void adjustDown(vector<int> &a,int root,int n){
a[0]=a[root];
for(int i=2*root;i<=n;i*=2){
if(i<n&&a[i]<a[i+1])
i++;
if(a[0]>=a[i])
break;
else{
a[root]=a[i];
root=i;
}
}
a[root]=a[0];
}
void buildMaxHeap(vector<int> &a,int n){
for(int i=n/2;i>0;i--)
adjustDown(a,i,n);
}
void heapSort(vector<int> &a,int end){//begin固定为1,便于编码
buildMaxHeap(a,end);
for(int i=end;i>1;i--){
swap(a[i],a[1]);
adjustDown(a,1,i-1);
}
}
//归并排序,每2个,每4个,每8个...进行归并,此处我用迭代写法
//case0~8(ms):1,1,2,6,52,48,46,46,44
void merge(vector<int> &a,int begin1,int end1,int begin2,int end2){//合并两个有序序列
int n=end2-begin1+1;
vector<int> b(n);
int i1=begin1;
int i2=begin2;
int i=0;
while(i1<=end1&&i2<=end2){
if(a[i1]<=a[i2])  //此处=号保证排序的稳定性
b[i++]=a[i1++];
else
b[i++]=a[i2++];
}
if(i1<=end1)
while(i<n)
b[i++]=a[i1++];
if(i2<=end2)
while(i<n)
b[i++]=a[i2++];
i=begin1;
int j=0;
while(j<n)
a[i++]=b[j++];
}
void mergePerCount(vector<int> &a,int begin,int end,int count){//目标是每count个为一组
int i=begin;
while(i<=end){
if(i+count-1<=end){//两个子数组加起来够count个
merge(a,i,i+count/2-1,i+count/2,i+count-1);
}else{
if(i+count/2<=end)//存在两个子数组,后一个残缺
merge(a,i,i+count/2-1,i+count/2,end);
}
i=i+count;
}
}
void mergeSort(vector<int> &a,int begin,int end){
int count=1;
int n=end-begin+1;
while(count<n){
count=count*2;
mergePerCount(a,begin,end,count);
}
}
//普通快速排序,piovt太小或者太大会导致性能很差
//case0~8(ms):1,1,超时,超时,超时,超时,超时,2222,超时
int partition1(vector<int> &a,int begin,int end){
int pivot=a[begin];
while(begin<end){
while(begin<end&&a[end]>pivot)
end--;
a[begin]=a[end];
while(begin<end&&a[begin]<pivot)
begin++;
a[end]=a[begin];
}
a[begin]=pivot;
return begin;
}
void quickSort1(vector<int> &a,int begin,int end){
if(begin<end){
int pivotpos=partition1(a,begin,end);
quickSort1(a,begin,pivotpos-1);
quickSort1(a,pivotpos+1,end);
}
}
//快速排序和直接插入排序混合
//case0~8(ms):1,1,2,11,超时,39,39,76,超时
int partition2(vector<int> &a,int begin,int end){
//3数取中
int mid=begin+(end-begin)/2;
if(a[begin]>a[end])
swap(a[begin],a[end]);
if(a[mid]>a[end])
swap(a[mid],a[end]);
if(a[mid]>a[begin])
swap(a[mid],a[begin]);
int pivot=a[begin];
while(begin<end){
while(begin<end&&a[end]>pivot)
end--;
a[begin]=a[end];
while(begin<end&&a[begin]<pivot)
begin++;
a[end]=a[begin];
}
a[begin]=pivot;
return begin;
}
void quickSort2(vector<int> &a,int begin,int end){
if(begin<end){
if(end-begin<5000)
insertSort(a,begin,end);
else{
int pivotpos=partition2(a,begin,end);
quickSort2(a,begin,pivotpos-1);
quickSort2(a,pivotpos+1,end);
}
}
}
int main(){
int N;
cin>>N;
vector<int> a(N+1);
for(int i=1;i<N+1;i++)  //从1开始是为了便于堆排的层序编码
cin>>a[i];
//selectSort(a,1,N);
//bubbleSort(a,1,N);
//insertSort(a,1,N);
//shellSort(a,1,N);
//heapSort(a,N);
//mergeSort(a,1,N);
//quickSort1(a,1,N);
//quickSort2(a,1,N);
sort(a.begin()+1,a.end());
cout<<a[1];
for(int i=2;i<N+1;i++)
cout<<" "<<a[i];
return 0;
}


测试结果:



选择排序:O(n2);
冒泡排序:O(n2),顺序最好O(n),逆序最差n2/2,考虑到交换次数多,略差于选择排序;
直接插入排序:O(n2),顺序最好O(n),逆序最差n2/2,平均n2/4,好于选择和冒泡,在简单排序中性能最好;
希尔排序:O(n3/2),相当于先粗调,再精调的直接插入排序。
堆排序:O(nlogn),初始建堆次数较多,不适合排序个数较少的情况。
归并排序:O(nlogn),使用迭代写法,空间复杂度为O(n)。
快速排序:O(nlogn),最差O(n2),这是因为最好情况递归树深度为logn,最坏情况递归树深度为n。pivot的选取非常重要,太大或者太小会导致性能恶化。
可在选取pivot,序列较短时使用直接插入排序,迭代代替递归等方面进行优化。
STL sort:快排、直插、堆排(防止递归树太深)混合。

改进算法中,对于100K以内的数据,希尔排序、堆排序、归并排序与STL sort的性能较接近,快速排序需要优化才能发挥出它的力量。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: