排序刷题总结

一、程序设计中常用算法

洛谷排序题
这个题目是排序的模板题,对于复杂度O(n^2)的排序算法,可以过三个样例,其余TLE,
对于复杂度稳定在O(nlogn)的排序算法,才会AC。

插入排序

思想史,遍历数组中的每一个数a[i],i之前的是有序的,i之后的是无序的,a[i]寻找合适的位置插入有序的序列中。插入排序就是不断的将有序合并无序的过程。

{2,4,5} a[i]=3 { 10,1,8} 一趟排序后:{2,3,4,5} a[i]=10 {1,8}

代码实现

void insertSort(){//不带哨兵的插入排序容易理解
    // 循环到i,i之前是有序的,i在有序中寻找自己的位置,i之后是无序的
    for(int i=2;i<=n;i++){
        for(int j=i-1;j>0;j--){
            if(a[j]>a[j+1]) swap(a[j],a[j+1]);
            else break;
        }
    }
}

使用哨兵的实现

void insertSort(){
    // 循环到i,i之前是有序的,i在有序中寻找自己的位置,i之后是无序的
    for(int i=2;i<=n;i++){
        a[0]=a[i];//哨兵
        int j=i-1;//从上一个开始遍历
        for(;j>0;j--){
            if(a[j]>a[0]) a[j+1]=a[j];//全部都是与哨兵比较
            else break;//直到找到哨兵应该在的位置
        }
        a[j+1]=a[0];
    }
}

插入排序优点在于可以实现链式存储的排序,对于双向指针的链表是比较容易实现交换的。

冒泡排序

每次通过相邻交换,每趟确定未排序的数字中最大的数字的位置。
洛谷-车厢重组

void BubbleSort(){
    for(int i=1;i<=n-1;i++){
        for(int j=1;j<=n-i;j++){
            if(a[j]>a[j+1]) swap(a[j],a[j+1]);
        }
    }
}

快速排序

快速排序基于分治思想,任取一元素x作为枢轴,然后小于x的放在左边,大于x的放在x右边,那么x一定放在了最终位置,左右区间的都确定了大概位置。然后依次对左右两个区间继续做该操作,直到整个集合都有序。
如何实现?使用双指针进行交换。

int getP(int low,int high){
    int x=a[low];
    while(low<high){
        while(low<high&&a[high]>=x) high--;//
        a[low]=a[high];
        while(low<high&&a[low]<=x) low++;
        a[high]=a[low];
    }
    a[low]=x;
    return low;
}
void QuickSort(int l,int r){
    if(l>=r) return;//退出循环条件
    int p=getP(l,r);//确定了p位置
    QuickSort(l,p-1);
    QuickSort(p+1,r);
}

选择排序

选择排序思想是从无序数组中选择最小的元素放在最前面,直到数组有序。

void selectSort(){
    for(int i=1;i<=n-1;i++){
        int minn=a[i],p=i;
        for(int j=i+1;j<=n;j++){
            if(a[j]<minn){
                minn=a[j];
                p=j;
            }
        }
        swap(a[p],a[i]);
    }
}

堆排序

#include<bits/stdc++.h>
using namespace std;
#define N 101010
int n;
int a[N];
//建立大根堆,即每次取出的都是剩余堆最大的,然后取出后放在后面,直到堆空,最终数组成为升序
void update(int k,int len){
    a[0]=a[k];
    for(int i=k*2;i<=len;i<<=1){
        //如果a[k]>min(a[k*2],a[k*2+1])
        //那么必须将a[k*2],a[k*2+1]之中!!最大!!的换上去
        if(i+1<=len&&a[i]<a[i+1]) i++;//寻找 k*2 与 k*2+1中较大的
        if(a[0]<a[i]){//认清是寻找a[0](原a[k]位置)所以是a[0]与a[i]比较
            a[k]=a[i];//a[i]升上去
            k=i;
        }else break;
    }
    a[k]=a[0];
}
void headSort(){
    for(int i=n/2;i>=1;i--) update(i,n);
    for(int i=n;i>1;i--){//i==1时 a[1]肯定是最小的了
        swap(a[1],a[i]);
        update(1,i-1);
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    headSort();
    for(int i=1;i<=n;i++) printf("%d ",a[i]);
}

归并排序

void mergeSort(int l,int r){
    if(l>=r) return;
    int l1=l,r1=(l+r)/2,l2=(l+r)/2+1,r2=r;
    mergeSort(l1,r1);
    mergeSort(l2,r2);
    int i=l1,j=l2,index=i;
    while(i<=r1&&j<=r2){
        if(a[i]<a[j]){
            tmp[index++]=a[i];
            i++;
        }else {
            tmp[index++]=a[j];
            j++;
        }
    }
    while(i<=r1){
        tmp[index++]=a[i];
        i++;
    }
    while(j<=r2){
        tmp[index++]=a[j];
        j++;
    }
    for(i=l1;i<=r2;i++) a[i]=tmp[i];
}

几种排序算法各有优缺,各有用途,在c++的sort()实现就是结合了快排、插入、堆排序的方式。

二、排序算法应用

简单的排序题目可能是对一个结构体数组排序,根据结构体的键进行排序,需要重写比较函数。
洛谷-奖学金

#include<bits/stdc++.h>
using namespace std;
#define N 1010
struct Node{
    int id,sum,chinese;
}a[N];
int n,c,m,e,sum;
bool cmp(Node n1,Node n2){//比较函数是关键
    return n1.sum==n2.sum?(n1.chinese==n2.chinese?(n1.id<n2.id):n1.chinese>n2.chinese):n1.sum>n2.sum;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>c>>m>>e;
        sum=c+m+e;
        a[i]={i,sum,c};
    }
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=5;i++) cout<<a[i].id<<' '<<a[i].sum<<endl;
}

洛谷-宇宙总统 两个高精度数值比较,使用string存储,先比较位数,然后再比较string字典序。

离散化

中位数

动态维护中位数

第k大数

在快速排序中,每次选取枢轴mid(一般为最左端点),假设枢轴排序后最终位置为xa[x]=mid 。在区间[l,r]一趟快速排序后,被分为了三段,a[l,x-1]<=mida[x]=mida[x+1,r]>mid三段。在求解第k大数中,每经过一次排序,我们都可以锁定第k大数所在区间.若k<x则 第k大数在区间[l,x-1]中,若x<k则 第k大数在区间[x+1,r]中,递归下去继续寻找,直到 x==k a[x]=mid=枢轴就是第k大数。

#include<bits/stdc++.h>
using namespace std;
#define N 5001010
int n,p,a[N];
int getP(int l,int r){
    a[0]=a[l];
    while(l<r){
        while(l<r&&a[r]>=a[0])r--;
        a[l]=a[r];
        while(l<r&&a[0]>=a[l]) l++;
        a[r]=a[l];
    }
    a[l]=a[0];
    return l;
}
void QuickSort(int l,int r){
    int k=getP(l,r);
    if(p<k) QuickSort(l,k-1);
    else if(p>k) QuickSort(k+1,r);
    else cout<<a[k];
}
int main()
{
    scanf("%d",&n);
    scanf("%d",&p);
    p++;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    QuickSort(1,n);
}

前K小数的话,堆排序效率较高。维护一个大小为k的堆,初始化可以是前k个元素,建成大根堆。在之后的k+1~n个元素中,如果a[i]>堆顶,可以肯定的是该元素一定不是前k小数,否则代替堆顶,更新堆。最终堆剩余元素就是前k小数,复杂度介于O(n)O(nlogk)之间。

逆序对

未完。。。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值