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

给定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的性能较接近,快速排序需要优化才能发挥出它的力量。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用,数据结构PAT练习题中包含了关于树的题目。其中有一道题目将二叉树看作图,并对它作图的深度优先遍历,与原二叉树的结果是相同的。答案选项包括前序遍历、中序遍历、后序遍历和层次序遍历。 根据引用和引用,数据结构PAT练习题还包含了测试各排序算法的题目。这些题目旨在测试不同排序算法在不同数据情况下的表现。测试数据包括只有1个元素、11个不相同的整数、随机整数、顺序整数、逆序整数、基本有序的整数和随机正整数等。 综上所述,数据结构PAT练习题涉及到树的遍历和排序算法的应用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [数据结构习题-Dijkstra](https://download.csdn.net/download/li_guizhen/2853501)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [数据结构(陈越)PAT练习题 第七周:排序(上)](https://blog.csdn.net/ustcfdm/article/details/43671249)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值