【算法学习】排序
排序这个东西真的挺卷的,有些就追求极致的速度,有些追求极致的简洁,有必要学一学提一提,尽管 \(sort\) 能解决绝大部分问题。
学习笔记
冒泡排序
mpft
第一个学的排序也是最简单的排序,也就是一个一个比较,如果右边的数大于左边的数就交换,我当初老师说这一个一个交换是不是就像冒泡一样,那确实哈。
#include<bits/stdc++.h>
using namespace std;
int n;
int a[100000];
int main(){
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]){
swap(a[j],a[j+1]);
}
}
}
for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
return 0;
}
选择排序
就如同他的名字一样,每次从剩下的区间的中选择最小的数放到区间最开始,确实挺好理解。
#include <bits/stdc++.h>
using namespace std;
int n;
int a[1000000];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
int minn=10000,id=0;
for(int j=i;j<=n;j++){
if(a[j]<minn){
id=j;
minn=a[j];
}
}
swap(a[i],a[id]);
}
for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
return 0;
}
插入排序
感觉和选择排序有点像,每个数向前找,如果比他大就交换位置,如果小就停止判断下一个数。
#include <bits/stdc++.h>
using namespace std;
int n;
int a[1000000];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
for(int j=i;j>=1;j--){
if(a[j]<a[j-1]){
swap(a[j],a[j-1]);
}
else{
break;
}
}
}
for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
return 0;
}
堆排序
用一个堆的数据结构来维护单调性,单调队列的底层原理,后来的平衡树还会再见的。
stl
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n;
ll a[50005];
priority_queue<ll,vector<ll>,greater<ll> > q;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
q.push(a[i]);
}
for(int i=1;i<=n;i++){
cout<<q.top()<<"\n";
q.pop();
}
return 0;
}
手写堆
#include <bits/stdc++.h>
using namespace std;
int n,x;
int heap[1000005];
int siz=0;
void up(int idx){
while(idx!=1 && heap[idx]<heap[idx/2]){
swap(heap[idx],heap[idx/2]);
idx/=2;
}
}
void add(int k){
heap[++siz]=k;
up(siz);
}
void down(int idx){
int r=idx;
if(heap[r]>heap[idx*2]&&idx*2<=siz){
r=idx*2;
}
if(heap[r]>heap[idx*2+1]&&idx*2+1<=siz){
r=idx*2+1;
}
if(r==idx){
return;
}
swap(heap[r],heap[idx]);
down(r);
}
void del(){
heap[1]=heap[siz--];
down(1);
}
int main (){
cin>>n;
while(n--){
cin>>x;
if(x==1){
cin>>x;
add(x);
}
else if(x==2){
cout<<heap[1]<<endl;
}
else{
del();
}
}
return 0;
}
归并排序
分而治之好
1.逆序对
发明分治的人真是个人才,真的太厉害了,像我们的线段树最底层就是靠分治来实现的,而分治排序就更厉害了,直接把逆序对秒了。
#include<bits/stdc++.h>
using namespace std;
int n;
int a[100005];
int b[100005];
void mergee(int l,int r){
int mid=(l+r)>>1;
int lp=l,rp=mid+1,x=l;//左端到中间,中间到右端点
//x=l 为了最后修改在原数组的位置的数
while(lp <= mid && rp <= r){
if(a[lp]<=a[rp]){//左右比较
b[x++]=a[lp];//修改b
lp++;
}
else{
b[x++]=a[rp];
rp++;
}
}
while(lp<=mid){//左边没到中间,比已经排好的打,依次放入最后
b[x++]=a[lp];
lp++;
}
while(rp<=r){
b[x++]=a[rp];
rp++;
}
for(int i=l;i<=r;i++){//修改原数组
a[i]=b[i];
}
return;
}
void mergesort(int l,int r){
if(l==r){//缩小到一个点返回
return;
}
int mid=(l+r)>>1;//分治
mergesort(l,mid);//左区间
mergesort(mid+1,r);//右区间
mergee(l,r);//合并左右区间
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
mergesort(1,n);
for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
return 0;
}
基数排序
依次对每一位进行排序,对排序后进行整理后继续对按位排序,直到全部数排完序为止。
#include<bits/stdc++.h>
using namespace std;
int n;
int a[100005];
vector<int> v[10];
int mod=1;
void solve(){
for(int i=1;i<=10;i++){
for(int j=1;j<=n;j++){
v[(a[j]/mod)%10].push_back(a[j]);
}
int cnt=1;
for(int k=0;k<=9;k++){
for(int j=0;j<v[k].size();j++){
a[cnt++]=v[k][j];
}
v[k].clear();
}
mod*=10;
}
}
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
solve();
for(int j=1;j<=n;j++){
cout<<a[j]<<" ";
}
return 0;
}