排序大杂烩
七个排序
这里介绍七个排序
选择排序
选择排序应该是最简单的排序之一
简单来说,找到剩余区间内的最小值然后放在一个有序区间的末尾
(不用纠结剩余区间什么意思,模拟一遍后你就会知道
- 模拟一下
假设给整个区间排序
区间内数为
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]);
}
- 复杂度分析
插入排序
插入排序是通过“插入”来排序
通过将一个数插入到一个有序区间的某个位置,从而保证插入后区间有序
设为插入的数为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的数的下标
}
复杂度
选择排序
选择排序应该是最简单的排序之一
简单来说,找到剩余区间内的最小值然后放在一个有序区间的末尾
(不用纠结剩余区间什么意思,模拟一遍后你就会知道
- 模拟一下
区间内数为
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]);
}
复杂度
冒泡排序
冒泡排序思想很简单,实现也很简单
我们知道数组下标是有序的,只要我们在数组中,以这个数的为下标的位置的数标记一下就可以,从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;
}
- 复杂度
堆排序
堆排序一般可以直接用优先队列实现(这里不展开讲,有兴趣同学可以到去看其他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为自定义函数
- 复杂度
归并排序
这里主要讲二路归并(还有多路归并
二路归并同样也有分治的思想
将两个有序的区间,合为一个有序的区间,和前面讲过的一样,一个数即为一个有序区间,
- 模拟。。。(又来了
假设给整个区间排序 区间内数为
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
}
还有很多排序算法没有讲到
如果有不清楚的地方可以发评论私信提问
讲的不行还望海涵