各类排序算法
简单介绍各类排序算法,并给出相对应的模板
练习oj P1177 【模板】快速排序
冒泡排序
时间复杂度:O(n2)
类似水中冒气泡的原理
相邻两个气泡之间比较,大的气泡往上冒
//冒泡排序 O(n2)
void bubble_sort(int l,int r,int a[]){
for(int i=l;i<r;++i){
bool f=0;
for(int j=l;j<r+l-i;++j){
if(a[j]>a[j+1]){ //相邻气泡比较,大的往上冒
swap(a[j],a[j+1]);
f=1;
}
}
if(f==0) break; //没有发生交换,说明已经排序好
}
}
插入排序
时间复杂度:O(n2)
类似打扑克牌时,依次整理发到手里的牌
拿到一张牌,与手里已有的牌进行比较,找到合适位置,移出来一张牌的空位后插入
优化:因为手里的牌已经是有序的,可以通过二分查找,更快的找到插入位置
//插入排序 O(n2)
void insert_sort(int l,int r,int a[]){
int t;
for(int i=l;i<=r;++i){
t=a[i];
//当前插入第i张牌
int l1=l,r1=i,mid; //r=n(0~n-1)
while(l1<r1){ //二分查找
mid=(l1+r1)/2;
if(a[i]>a[mid]) l1=mid+1; //> l=m+1
else r1=mid; //r=m
} //移出来一个牌位
for(int j=i-1;j>=l1;--j) a[j+1]=a[j];
a[l1]=t; //插入
}
}
选择排序
时间复杂度:O(n2)
依次找最小的元素,逐个替换到前面
//选择排序 O(n2)
void select_sort(int l,int r,int a[]){
for(int i=l;i<r;++i){
int k=i;
for(int j=i+1;j<=r;++j){
if(a[j]<a[k]) k=j; //找到最小的元素,交换到首部
}
if(k!=i) swap(a[i],a[k]);
}
}
桶排序
时间复杂度:O(n+m)
简言之就是利用数组进行排序
时间很美观,但空间是个大问题
//桶排序 O(n+m) 空间容易MLE
//需要一个超大的数组
#define inf 123456
int cnt[123456]={0};
void bucket_sort(int l,int r,int a[]){
int ma=0,mi=inf; //记录最大最小值 优化排序时间
for(int i=l;i<=r;++i){
++cnt[a[i]];
ma=max(ma,a[i]);
mi=min(mi,a[i]);
}
int p=l;
for(int i=mi;i<=ma;++i)
while(cnt[i]--)
a[p++]=i;
}
希尔排序
时间复杂度:O(n1.3-n2)
插入排序的改进版
通过设置一个间隔,对相同间隔的数进行插入排序,从而实现大步位移(插入排序每次只能移动一位)。最后的间隔必须等于1,来确保整体有序
间隔序列有多种,这里gap=N/2
//希尔排序 O(n1.3-n2)
void shell_sort(int l,int r,int a[]){
for(int gap=(r+1-l)/2;gap>0;gap/=2) //间隔序列N/2 最后间隔为1
for(int i=l+gap;i<=r;++i) //间隔gap个元素有序
for(int j=i;j>=l+gap&&a[j]<a[j-gap];j-=gap) //间隔gap插入排序
swap(a[j],a[j-gap]); //大步位移
}
归并排序
时间复杂度:O(nlogn)
分而治之的策略
画图理解起来很快,这里推荐 图解排序算法(四)之归并排序
//归并排序 O(nlogn) 分而治之
int tmp[123456]={0};
void merge(int l,int m,int r,int a[]){
int i=l; //左序列指针
int j=m+1; //右序列指针
int p=0;
while(i<=m&&j<=r){ //两个指针依次比较
if(a[i]<a[j]) tmp[p++]=a[i++];
else tmp[p++]=a[j++];
}
while(i<=m) tmp[p++]=a[i++]; //剩余元素填充
while(j<=r) tmp[p++]=a[j++];
p=0;
while(l<=r) a[l++]=tmp[p++];
}
void merge_sort(int l,int r,int a[]){
if(l>=r) return;
int mid=(l+r)/2;
merge_sort(l,mid,a);
merge_sort(mid+1,r,a);
merge(l,mid,r,a);
}
堆排序
利用堆(优先队列)来排序
这里偷懒就不手写堆了,直接用STL
//堆排序 O(nlogn)
void heap_sort(int l,int r,int a[]){
priority_queue<int,vector<int>,greater<int>> q; //小顶堆
for(int i=l;i<=r;++i) q.push(a[i]);
int p=l;
while(!q.empty()){
a[p++]=q.top();
q.pop();
}
}
快速排序
时间复杂度:O(nlogn)
类似归并的分治思想,从待排的元素中找到一个基准数,将比它大的放在右边,小的放在左边
//快速排序 O(nlogn)
void quick_sort(int l,int r,int a[]){
if(l>=r) return;
int i=l,j=r; //左右两个指针
srand(time(0));
int pivot=rand()%(l-r)+l;
swap(a[pivot],a[l]);
int tmp=a[l]; //待排的第一个元素作为基准数(随机基准数不容易tle)
while(i!=j){ //从两边扫描,直到两个指针相遇
while(j>i&&a[j]>=tmp) --j; //从右到左找到第一个比基准数小的元素
a[i]=a[j]; //a[i]与a[j]交换
while(i<j&&a[i]<=tmp) ++i;
a[j]=a[i];
}
a[i]=tmp; //基准数放到中间
quick_sort(l,i-1,a); //对基准数左边的元素排序
quick_sort(j+1,r,a);
}