排序算法和妙用


前言

  • 在2020初学排序就把我差点劝退信竞,经历了很多很多,两年之后的今天,我才能熟练的掌握这貌似简单的基础算法

  • 排序也算是计算机科学最经典的问题了,在这一部分我们主要介绍静态排序,不过其中思想在后面很多地方我们都有用到

一,归并排序

1,基础简述

  • 递归处理左右区间,使得左右区间有序(分治的思想)
  • 双指针同步遍历左右区间,把两个有序数组合并到辅助数组(这一步我们可以嵌套很多操作,CDQ分治等等)
  • 把辅助数组的元素复制到元数组中
  • 调用入口:merge_sort(a,l,r) a a a 数组 l l l r r r的部分排序
void merge_sort(int a[],int l,int r)
{
    if(l==r)return ;
    int mid = l+r >> 1;
    merge_sort(a,l,mid);
    merge_sort(a,mid+1,r);
    int i = l;
    int j = mid+1;
    int k = 0;
    while (i <= mid && j <= r) 
    {
        if(a[i]<=a[j]) w[k++]=a[i++];
        else w[k++]=a[j++];
    }
    while (i <= mid) w[k++]=a[i++];
    while (j <= r) w[k++]=a[j++];
    i=l;
    for(int pos = 0;pos < k; pos ++)
    {
        a[i++]=w[pos];
    }
}

2,简单扩展

LL merge_sort(int a[],int l,int r)
{
    if(l==r)return 0 ;
    int mid = l+r >> 1;
    LL res = merge_sort(a,l,mid) + merge_sort(a,mid+1,r);
    
    int i = l,j = mid+1;
    int k = 0;
    while (i<=mid && j<=r)
    {
        if(a[i]<=a[j]) w[k++]=a[i++];
        else w[k++]=a[j++],res+=mid-i+1;
    }
    while (i<=mid)w[k++]=a[i++];
    while (j<=r)w[k++]=a[j++];
    i = l;
    for(int pos=0;pos<k;pos++) a[i++]=w[pos];
    return res;
}

二,快速排序

1,基础简述

  • 使用边界而非终点在极限条件下会卡时间( 1 e 5 1e5 1e5
  • 调用入口:quick_sort(a,l,r) a a a 数组 l l l r r r的部分排序

界值与判断指针:

1,取得中点 l + r > > 1 l+r>>1 l+r>>1 为界,只能使用 j j j 作为递归的边界
2,取得区间左界 l l l 为界,只能使用 j j j 作为递归边界(jl记为吉林)
3,取得区间右界 r r r 为界,只能使用 i i i 作为递归边界
4,判断指针位置数值和界值大小的关系只能用严格不等关系

参数意义:

1, 指针 i i i 划分小于等于界值的位置
2, 指针 j j j 划分大于等于界值的位置
3,在双指针没有交互之前,遇到不合法位置会停步,此时需要交换保证指针意义
4,迭代终止条件是指针交互
5,注意迭代终止的时候指针是不合法位置,因为我们使用 d o do do 语句,先跳后判断

void quick_sort(int a[],int l,int r)
{
    if(l>=r)return ;
    int x = a[l+r>>1];
    int i = l-1;
    int j = r+1;
    while(i<j)
    {
        do i++; while (a[i]<x);
        do j--; while (a[j]>x);
        if(i<j)swap(a[i],a[j]);
    }
    
    quick_sort(a,l,j);
    quick_sort(a,j+1,r);
}

2,简单扩展

寻找第k大的数,只用递归一半排序即可

  • 注意 l e n = = p o s len==pos len==pos 也要到第一区间,思考实际意义即可
  • 注意更新新递归的 p o s pos pos
int quick_sort(int a[],int l,int r,int pos)
{
    if(l==r||pos==0)return a[l];
    int mid = l+r >>1;
    int x = a[mid];
    int i = l-1;
    int j = r+1;
    while (i<j)
    {
        do i++; while (a[i]<x);
        do j--; while (a[j]>x);
        if(i<j) swap(a[i],a[j]);
    }
    int len = j-l+1;
    if(len>=pos) return quick_sort(a,l,j,pos);
    return quick_sort(a,j+1,r,pos-len);
}

三,基数排序(radix sort)

详细讲解

  • 针对元素大小已知的情况下,我们思考一种快与均摊快排的排序手法
  • 其在有一定范围中的非负整数排序中具有优秀的表现
  • 我们会在后缀数组中再次见到
  • 调用入口:radix_sort(a,n); a a a 数组 [ 0 [0 [0 n − 1 ] n-1] n1]的部分排序

基数的随机的,看自己需要的大小

  • 针对在基数进位制度下的每一位拆分并使用稳定排序(桶排序)
  • 复杂度和数字大小成正相关
  • 从小开始,先是第一关键字其次是第二关键字
  • 使用范围:内存稀缺,排序元素值域已知
    O ( N   ∗   n u m m a x / r a d i x ) O(N~∗~ num_{max}/radix) O(N  nummax/radix),空间复杂度为 O ( n + r a d i x ) O(n + radix) O(n+radix),其中 r a d i x radix radix为选择的基数, n u m m a x num_{max} nummax是序列最大值
in
t cnt[256];

void radix_sort(int *date,int length,int radix = 256)
{
    int *tem =new int[length];
    int p = 1;
    for(int d=0;d<4;d++)
    {
        memset(cnt,0,sizeof cnt);
        for (int i = 0; i < length; i ++ )
        {
            int k = (date[i]/p)%radix;
            ++cnt[k];
        }
        
        for(int i = 0; i < radix; i ++ ) cnt[i]+=cnt[i-1];
        for(int i= length-1; i >= 0; i--)
        {
            int k = (date[i]/p)%radix;
            tem[--cnt[k]]=date[i];
        }
        swap(date,tem);
        p *= radix;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流苏贺风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值