前言
-
在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] n−1]的部分排序
基数的随机的,看自己需要的大小
- 针对在基数进位制度下的每一位拆分并使用稳定排序(桶排序)
- 复杂度和数字大小成正相关
- 从小开始,先是第一关键字其次是第二关键字
- 使用范围:内存稀缺,排序元素值域已知
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;
}
}