各类排序算法

各类排序算法

简单介绍各类排序算法,并给出相对应的模板
练习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);
}
posted @ 2019-06-03 16:00  lidasu  阅读(202)  评论(0编辑  收藏  举报