七个排序

这里介绍七个排序

排序大杂烩_复杂度

选择排序

选择排序应该是最简单的排序之一
简单来说,找到剩余区间内的最小值然后放在一个有序区间的末尾
(不用纠结剩余区间什么意思,模拟一遍后你就会知道

  • 模拟一下
    假设给整个区间排序
    区间内数为
    4,3,6,9,8
    先找整个区间中的最小值
    为3,放在第一位,交换3和4
    然后找除了3以外的数的最小值(也就是剩下未排序的元素,第2个数到第5个数即为剩余区间),最小值为4,4就在第二位不用换
    然后继续找最小值(最小值在第3个数到第5个数之间)为6,放在原位
    继续找最小值(最小值在第4个数到第5个数之间)为8,交换8和9
    最终有序区间[3,4,6,8,9]

算法实现

  • code
for(int i=1;i<len;++i){
	int maxi=i; 
	for(int j=i+1;j<=len;++j)//剩余区间
		if(s[j]<s[maxi]) maxi=j;//记录区间中最小值下标
	swap(s[maxi],s[i]);
}  
  • 复杂度分析

排序大杂烩_复杂度_02

插入排序

插入排序是通过“插入”来排序

通过将一个数插入到一个有序区间的某个位置,从而保证插入后区间有序
设为插入的数为b(插入后区间长度+1
则b在区间满足的条件为 \(a[i]<b<a[i+1]\)

  • 证明
    因为\(a[1]<a[2]<....<a[n-1]<a[n]\)
    又因为a序列始终有序,插入后不破坏其性质
    所以插入后\(a[1]<a[2]<...<a[i-1]<b<a[i]<..<a[n+1]\)

举个简单例子

  • 现有有个区间1,2,6
    要插入一个数字4
    这个很显然都知道插入到2和6之间就能保证整个区间有序

其实模拟一遍就很容易懂

  • 假设给整个区间排序
    区间内数为
    4,3,6,9,8
    设此时4为有序区间(一个数当然构成一个有序区间
    找下一个数 3
    与4相比,要比4小,为保证区间有序,所以3需要插入到4的前面即有序区间为[3,4]
    再下一个数 6
    逐个与有序区间内的数相比插入到4后面,有序区间为[3,4,6]
    再下一个数 9
    重复刚刚的步骤 有序区间[3,4,6,9]
    以此类推 最终有序区间[3,4,6,8,9]

算法实现

  • code
for(int i=1;i<len;++i){
      cin>>s;
      for(int j=i-1;j>=1 && a[j]>s;--j) a[j+1]=a[j];//从后往前找
      a[j+1]=s;//此时j为第一个小于s的数的下标
} 

复杂度

排序大杂烩_数组_03

选择排序

选择排序应该是最简单的排序之一
简单来说,找到剩余区间内的最小值然后放在一个有序区间的末尾
(不用纠结剩余区间什么意思,模拟一遍后你就会知道

  • 模拟一下
    区间内数为
    4,3,6,9,8
    先找整个区间中的最小值
    为3,放在第一位,交换3和4
    然后找除了3以外的数的最小值(也就是剩下未排序的元素,第2个数到第5个数即为剩余区间),最小值为4,4就在第二位不用换
    然后继续找最小值(最小值在第3个数到第5个数之间)为6,放在原位
    继续找最小值(最小值在第4个数到第5个数之间)为8,交换8和9
    最终有序区间[3,4,6,8,9]

算法实现

  • code
for(int i=1;i<len;++i){
	int maxi=i; 
	for(int j=i+1;j<=len;++j)//剩余区间
		if(s[j]<s[maxi]) maxi=j;//记录区间中最小值下标
	swap(s[maxi],s[i]);
}  

复杂度

排序大杂烩_复杂度_02

冒泡排序

冒泡排序思想很简单,实现也很简单

我们知道数组下标是有序的,只要我们在数组中,以这个数的为下标的位置的数标记一下就可以,从1开始找,找到有标记位置就将输出

  • 模拟一下
    区间内数为
    4,3,6,9,8
    那么数组t={0,0,0,1,1,0,1,0,1,1}
    对应下标 0,1,2,3,4,5,6,7,8,9
    从t[0]开始到t[9],

算法实现

  • code
bool t[maxn];
for(int i=1;i<=len;++i){
	int num;cin>>num;
	t[num]=1; 
}   
  • 复杂度

排序大杂烩_复杂度_02

堆排序

堆排序一般可以直接用优先队列实现(这里不展开讲,有兴趣同学可以到去看其他blog

#include<queue>//需要用到这个头文件
priority_queue<>

快速排序

快速排序其实是用到了分治的思想

将一个区间分为两个区间,左区间小中间这个数,右区间大于中间这个数
逐次递归分解越来越小的区间,最后使整个区间有序

  • 老规矩模拟一遍
    区间内数为
    4,9,5,2,8
    我们可以找到中间数5,
    把所有小于它的数放在左边,大于它的数放右边则区间为
    4,2,5,9,8
    然后再看左区间,取中间数4,操作后区间则为2,4
    再看右区间,取中间数9,操作后区间则为8,9
    整个区间就为2,4,5,8,9
    (如果还不理解,可以自己多举几组数模拟一下

算法实现

  • code
void sort(int l,int r){ 
	int mid=s[(l+r)>>1];//位运算等价于(l+r)/2;
	int i=l,j=r;
	while(i<=j){//这几步就是使左右区间的数分别满足小于和大于mid 
		while(s[i]<mid) ++i;//i一定不大于 (l+r)/2
		while(s[j]>mid) --j;//j一定不小于 (l+r)/2
		//思考下为什么不大于等于 
		if(i<=j){
			swap(s[i],s[j]);
			i++;j--; 
		}
	} 
	//此时区间划分为[l,j] 和[i,r] 
	if(l<j) sort(l,j); 
	if(i<r) sort(i,r);
}

一般不手写因为stl有

#include<algorithm>
bool cmp(int a,int b){//int 可根据数组类型改为double或其他(甚至可以是结构体
  return a<b;//数组按从小到大排序
  return a>b;//数组按从大到小排序
}
sort(a+1,a+1+n,cmp)//a为数组,a+1为需要排序的数组的起始位置,a+1+n为末位置,cmp为自定义函数
  • 复杂度

排序大杂烩_复杂度_06

归并排序

这里主要讲二路归并(还有多路归并

二路归并同样也有分治的思想

将两个有序的区间,合为一个有序的区间,和前面讲过的一样,一个数即为一个有序区间,

  • 模拟。。。(又来了
    假设给整个区间排序 区间内数为
    4,9,5,2,8
    划为两个区间 4,9,5和 2,8
    再继续划分 4,9 和 5和 2和8
    继续划分 4和 9 和 5和 2和8
    不能再划分就停止
    然后开始合并
    怎么合并了?
    4和9原本属于一个[4,9]区间,又要合并回去,且保证有序
    比较4和9,4小于9,先放入区间[4],然后9又放入[4,9]
    好然后此时 [4,9]区间有序了,[5]区间也有序了,这两个又同属于原本的[4,9,5]
    合并回去,比较4和5,4比5小可以直接放入(因为此时4是左区间最小数,5是右区间最小数,所以两个区间合并后的最小数一定在这两个数之间)目前区间[4]
    (但能不能把5直接放入后面,不能,因左区间第2个数可能小于5),然后把5与9比较,9就比5大,所以放入5,区间[4,5],最后放入9,q区间[4,5,9]
    2和8同理合为区间[2,8];
    然后区间[4,5,9]与区间[2,8]合为[2,4,5,8,9]

算法实现

  • code
int m[maxn];//原序列 
int temp[maxn];//存有序区间 
void marge_sort(int l,int r){
	if(l==r) return ;
	int mid=(l+r)>>1;
	marge_sort(l,mid);//左区间 
	marge_sort(mid+1,r);//右区间 
	int i=l,j=mid+1,p=l;//i为左区间左端点,j为右区间左端点 p为合成区间的末端指针 
	while(i<=mid && j<=r){
		if(m[i]<m[j]) temp[p++]=m[i++];//两个序列相比较,小的放 
		else temp[p++]=m[j++]; 
	}
	while(i<=mid) temp[p++]=m[i++];//右区间已经放完,而左区间也是有序的则,直接放入有区间后 
	while(j<=r) temp[p++]=m[j++];//同理,直接放入 
	for(i=l;i<=r;++i) m[i]=temp[i];//把有序序列放置给m 
} 

还有很多排序算法没有讲到
如果有不清楚的地方可以发评论私信提问
讲的不行还望海涵