排序分析-快速排序/归并排序

1.快速排序

快速排序思想
根本思想肯定是分治,每一次选出一个数,将原数组分成两部分,比该数小的放在左边,比该数大的放在右边(仅仅指从小到大的顺序排列),那么此时我们对该数的排序就算完成,然后通过递归,将数组一分再两半,将两部分又分别重新选出一个数,对该数进行一次"排序“。不断向下递归,直到每一个数都被我们排序过一次(最后剩下的一个数不用,因为他本身就是最小或者最大(也可能是局部最大或者最小)),这样操作以后排序就算完成。
快速排序实现
其实快排中最难的就是将数组按照大小分成两半的部分
法一: 一般是采用双指针方法,将数组从两头开始扫,比如说 i指针最开始指向左边第一个数,j指针最开始指向右边第一个数,mid指我们选定的那个数,while a[i]<a[mid]则i++,同理while a[j]>a[mid]则j–,当进行完这一步之后如果i仍然小于j则说明,i,j卡住了,即a[i]>=a[x],a[j]<=a[x],此时我们就可以swap(a[i],a[j]),然后重新进行上一步操作。
法二: 同时也可以采用从头到尾遍历的方法,具体操作:只需要一个指针(标记位),一般可以以第一个数作为需要进行排序的数(暂记为A),从第二个数(标记位)开始扫,如果扫到的数小于A,则将该数与标记位swap,同时标记位++(标记位表示小于A的数列尾端位置,也就是最后将数列一分为二的中间点),遍历完之后,从第二位到标记位都是小于A的数,标记位之后的数都是大于或等于A的数,此时我们仅仅只需要将A与标记位对应的数swap则可以实现将数组一分为二(一边为小于A的数,一边为大于等于A的数)的目标
AC代码

#include <bits/stdc++.h>
const int MAXN = 1e7; 
using namespace std;
int n,a[MAXN];
//三数取中 
//排序稳定:并不是指时间复杂度稳定,而是说相同值的数排序后按照原来的位置进行排序 
void quick_sort(int l,int r)
{
	if(l>=r) 	return ;
	int pivot_mid = l;
	int x = l+1;//标记位 
	for(int i=l+1;i<=r;i++)
	{
		if(a[i]<a[pivot_mid])
		{
			swap(a[x],a[i]) ;
			x++ ; 
		}
	}
	for(int i=0;i<n;i++) 	printf("%d ",a[i]);cout<<endl;
	swap(a[pivot_mid],a[x-1]);	
	quick_sort(l,x-2);
	quick_sort(x,r);
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)	scanf("%d",&a[i]);
	quick_sort(0,n-1);
	for(int i=0;i<n;i++) 	printf("%d ",a[i]);
	return 0;	
} 

排序是否稳定
排序稳定是指相同的数之间的相对位置不会改变,而在快速排序中与选中的数的相同大小的数可能会被swap,同时法二中被选中的数由于需要换到数组中间去也会影响稳定性,故:快速排序并不稳定。
快速排序时间复杂度
快速排序时间复杂度有不同情况
最坏情况: 当原数组处于正序或者倒序时,每次我们选择第一个数进行划分,那么该数就是最大数或最小数,也就意味着每次我们只能分割出一个数,那么就意味着需要向下递归n-1次,那么总的计算次数就是1+2+…+n-1,即n(n-1)/2,则时间复杂度为O(n^2);
最好情况: 就是每次我们都能选到中值进行排序,那么也就是只需要向下进行log2(n)次递归,计算次数也就是n*log2(n),时间复杂度也就是O(nlogn)
平均时间复杂度: 其实就是O(nlogn),只是不同情况下底数不同罢了
快排如何优化呢
1.为了避免原数组正序/倒序,我们可以直接将原数组打乱
2.为了避免选中最大数或者最小数,我们可以随机选取一个数来作为我们需要排序的数
3.可以采用三数取中的方法,大义为随机选取三个数,取这三个数中间大小的数进行排序
如何保证稳定性
可以将数组进行初始化,重新声明一个二维数组,第一个用来存储value,第二个用来存储等值数量,不过本人还没有想到一个时间复杂度比较小的方法进行初始化。

2.归并排序

算法思想
其实这个也是一种分治思想的应用,就是先数组一分为二,再一分为二,最后分的只剩一个,然后向上回溯,回溯的时候按照大小顺序进行数组的合并,因为最后一次递归只有一个数不需要排序,然后向上回溯过程中每一次回溯都按照大小顺序排列,则获得的每一个数组片段都是排好序的,只需要按照正确的顺序插入、合并即可。
合并代码片段

//这里的COPY数组用来存储排好序的数组片段
//A_1表示回溯上来的左边的数组的头指针,A_2表示回溯上来的右边的数组的头指针
//A_3表示COPY数组的头指针
//mid表示左边数组最后一位的下标
int A_1 = l,A_2 = mid+1,A_3 = 0;//分别为左边,右边,copy数组的指针; 
	while(A_1<=mid&&A_2<=r) //当两个数组都没有扫完时
	{
		if(ARRAY[A_1]<=ARRAY[A_2])//如果左边的数组更小一点则放前面
		//为什么要加上等号呢,为了保证排序的稳定性
			COPY[A_3++]=ARRAY[A_1++];
		else
			COPY[A_3++]=ARRAY[A_2++];
	}
	while(A_1<=mid) COPY[A_3++]=ARRAY[A_1++];
	//如果左边数组还有剩余则添加到COPY数组后
	while(A_2<=r)   COPY[A_3++]=ARRAY[A_2++];
	//同理
	for(int i=l,j=0;i<=r;i++,j++)//将排好序的片段放回原数组里面
		ARRAY[i]=COPY[j];

AC代码

#include <iostream>
#include <cstdio>
#include <ctime>
#include <algorithm>
#define MAXN 0x99ffff//10092543
using namespace std; 
int N,ARRAY[MAXN],COPY[MAXN];
void merge_sort(int l,int r)
{
	if(l>=r) return ;
	int mid = l+r >>1;//二进制数整体右移一位
	merge_sort(l,mid);merge_sort(mid+1,r);//向下递归
	int A_1 = l,A_2 = mid+1,A_3 = 0;//分别为左边,右边,copy数组的指针; 
	while(A_1<=mid&&A_2<=r) 
	{
		if(ARRAY[A_1]<=ARRAY[A_2])
			COPY[A_3++]=ARRAY[A_1++];
		else
			COPY[A_3++]=ARRAY[A_2++];
	}
	while(A_1<=mid) COPY[A_3++]=ARRAY[A_1++];
	while(A_2<=r)   COPY[A_3++]=ARRAY[A_2++];
	for(int i=l,j=0;i<=r;i++,j++)
		ARRAY[i]=COPY[j];
	return ;
} 
int main()
{
	scanf("%d",&N);
	for(int i=0;i<N;i++) 
		scanf("%d",&ARRAY[i]);
	merge_sort(0,N-1);
	for(int i=0;i<N;i++) 
		printf("%d ",ARRAY[i]);
	return 0;
} 

归并排序时间复杂度
无论怎么样都需要向下分log2(n)次,每次进行n次比较、赋值运算,故时间复杂度为O(nlogn);
归并排序稳定性
因为我们在合并的时候保证了等值数相对位置的不改变,故归并排序是稳定的排序

完结撒花

uestc公管01

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值