常见排序算法

首先C++不是必须需要学会排序这个东西的,但是有时候如果要一边排序、一边干点东西,或者有什么严苛的要求,那就得会手写排序

这属于模板性的东西

桶排序

时间复杂度: O ( n )

优点:速度最快

缺点:占用空间最大(和值域有关)、难以一边排序一边保留其他信息、数组不能用小数或者分数为编号

太容易了,不写代码了

冒泡排序

时间复杂度:O(n^{2})

优点:手写快

缺点:太慢,不稳定

#include<iostream>
using namespace std;
int a[100001];
int main()
{
	int n,num;
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n-i;j++)
	{
		if(a[j]>a[j+1])
		{
			num=a[j];
			a[j]=a[j+1];
			a[j+1]=num;
		}
	}
	for(int i=1;i<=n;i++)
	cout<<a[i]<<" ";
	return 0;
}

“剪枝”后:

#include<iostream>
using namespace std;
int a[100001],n;
bool check1()
{
	bool flag=1;
	for(int i=2;i<=n;i++)
	if(a[i-1]>a[i]) flag=0;
	if(flag) return true;
	else return false;
}
bool check2()
{
	bool flag=1;
	for(int i=2;i<=n;i++)
	if(a[i-1]<a[i]) flag=0;
	if(flag) return true;
	else return false;
}
int main()
{
	int num,cnt_1=0,cnt_2=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	for(int i=2;i<=n;i++)
	{
		if(a[i]>a[i-1]) cnt_1++;
		if(a[i]<a[i-1]) cnt_2++;
	}
	if(cnt_1>=cnt_2)
	{
		for(int i=1;i<=n;i++)
		{
			if(check1()) break;
			for(int j=1;j<=n-i;j++)
						{
							if(a[j]>a[j+1])
							{
								num=a[j];
								a[j]=a[j+1];
								a[j+1]=num;
							}
						}
		}
			
		for(int i=1;i<=n;i++)
		cout<<a[i]<<" ";
	}
	else{
		for(int i=1;i<=n;i++)
		{
			if(check2()) break;
			for(int j=i;j<n;j++)
						{
							if(a[j]<a[j+1])
							{
								num=a[j];
								a[j]=a[j+1];
								a[j+1]=num;
							}
						}
		}
			
		for(int i=n;i>=1;i--)
		cout<<a[i]<<" ";
	}
	return 0;
}

但是仍然不够快,只能说还是骗分的排序算法,只是多骗点分罢了

归并排序

也是手写的情况下我最喜欢的一种排序

时间复杂度:O ( n log n )

优缺点:非常折中的一种算法

#include<iostream>
using namespace std;
int n,a[100001],medium[100001],num;
void solve(int left,int right)
{
	if(left==right) return;
	if((left+1)==right) {
		if(a[left]>a[right])
		{
			num=a[left];
			a[left]=a[right];
			a[right]=num;
		}
	}
	else
	{
		solve(left,(left+right)/2);
		solve((left+right)/2+1,right);
		int leftpoint=left;
		int rightpoint=(left+right)/2+1;
		int cnt=leftpoint;
		while(1)
		{
			if(leftpoint==((left+right)/2+1))
			{
				for(int i=rightpoint;i<=right;i++)
				{
					medium[cnt]=a[i];
					cnt++;
				}
				
				break;
			}
			else if(rightpoint==(right+1))
			{
				for(int i=leftpoint;i<=(left+right)/2;i++)
				{
					medium[cnt]=a[i];
					cnt++;
				}
				
				break;
			}
			
			if(a[leftpoint]<=a[rightpoint]){
				medium[cnt]=a[leftpoint];
				leftpoint++;
			}
			else{
				medium[cnt]=a[rightpoint];
				rightpoint++;
			}
			cnt++;
		}
		for(int i=left;i<=right;i++)
		a[i]=medium[i];
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	solve(1,n);
	for(int i=1;i<=n;i++)
	cout<<a[i]<<" ";
	return 0;
}

如何分析算法时间复杂度?可以大概想一想这个图,样子就是个二叉树

每次合并时间复杂度O(n)(忽略常数),一共合并n次(原理就是任意一个正整数都可以被拆成2 的幂之和),那么时间复杂度是O(n^{2})吗?

不是,因为每次合并时间复杂度其实是O(n/2^{i}),除去的(也就是忽略的)并不是一个不依赖于n的常数

那么对于每一层考虑,分别是n,n/2+n/2,n/4+n/4+n/4+n/4,...

其实是n+n+n+...

一共有多少个n?是二叉树的深度log n

所以时间复杂度 O ( n log n )

快速排序

时间复杂度:最快近似 O ( n ) ,平均 O ( n log n ),最慢 O ( n^{2} )退化成冒泡排序

优点:有时候很快

缺点:不稳定

我最开始写的不太行,速度太慢还是TLE了,这个代码如下:

#include<iostream>
using namespace std;
int n,a[100001],num;
void solve(int l,int r)
{
	if(l>=r) return;
	int lpoint=l,rpoint=r;
	while(1)
	{
		if(lpoint==rpoint)
		{
			num=a[lpoint];
			a[lpoint]=a[l];
			a[l]=num;
			solve(l,lpoint-1);
			solve(rpoint+1,r);
			break;
		}
		else if(a[rpoint]>=a[l]) rpoint--;
		else if(a[lpoint]<=a[l]) lpoint++;
		if((a[rpoint]<a[l])&&(a[lpoint]>a[l]))
		{
			num=a[rpoint];
			a[rpoint]=a[lpoint];
			a[lpoint]=num;
		}
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	solve(1,n);
	for(int i=1;i<=n;i++)
	cout<<a[i]<<" ";
	return 0;
}

多开几个数组存下不同序列,再合并,改进之后:

#include<iostream>
using namespace std;
int n,a[100001],num;
int b[100001],c[100001],d[100001];
void solve(int l,int r)
{
	if(l>=r) return;
	int cnt_b=0,cnt_c=0,cnt_d=0;
	c[cnt_c++]=a[l];
	for(int i=l+1;i<=r;i++)
	{
		if(a[i]<a[l]) b[cnt_b++]=a[i];
		else if(a[i]==a[l]) c[cnt_c++]=a[i];
		else d[cnt_d++]=a[i];
	}
	
	int cnt=l;
	int ll=l+cnt_b-1;
	int rr=r-cnt_d+1;
	while(cnt_b--) a[cnt++]=b[cnt_b];
	while(cnt_c--) a[cnt++]=c[cnt_c];
	while(cnt_d--) a[cnt++]=d[cnt_d];
	solve(l,ll);
	solve(rr,r);
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	solve(1,n);
	for(int i=1;i<=n;i++)
	cout<<a[i]<<" ";
	return 0;
}

这样还是会TLE的,因为没有随机化,随机化挺麻烦的,所以一般手写用归并排序(又稳定又好写)

竞赛可以用随机数,但是注意不要用时间当种子(因为那样直接就零分了)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值