二次理解排序算法
🐧 这里先要友情提示一下,本篇文章可能对没有算法基础的同学有点难度,这里重点讲解 排序边界问题的处理方式 ,顺带提一下快速排序基于二分思想的应用好题。
排序的边界问题
如果大家经常写排序代码,会发现时不时会报错,循环结束不了,这就是排序边界问题引起的。
🐧 例子
假设我们对数组 a[2] = { 1 , -1 } 进行快速排序
👍quick_sort ( a, 0 ,1)
当我们使用快速排序后,在这个快速排序的调用里面有两个子调用,这里我先用含有 l , r 表达式表示出来
😁quick_sort( a , l , i - 1) , quick ( a, i, r); // l = 0 ,r = 1, i = ( l + r ) /2 = 0
如果留心看这里,我们会发现调用的式子为
🐧quick_sort( a , 0, - 1) , quick ( a, 0, 1); // l = 0 ,r = 1, i = ( l + r ) /2
这里注意看,我标记的两个式子,一模一样对吧。这不就是我们刚开始调用的函数吗?那么是不是就会一直这样调用下去呢?
显然,是的,这就是为什么会死循环的原因了。
🐧边界问题小结
这里会出现两种边界问题。
😒情况1: 母调用quick_sort ( a, 0 ,1):
子调用: quick_sort( a , l , i - 1) , quick ( a, i, r); 如果是这样就会陷入死循环
其实这里我们只要修改成 : quick_sort ( a ,low, j), quick_sort ( a, j+1 , high)
😒情况2:其实这里对应的和情况1 有一种相反的感觉。情况1出现陷入死循环的原因其实就是因为,我们快速排序中点的选择为左端点
左端点算法: (0+1)/2 所以当我们是左端点时候,由以上情况1的分析,我们需要用 sort( low ,j ), sort( j, high)
右端点算法 (0+1+1)/2 那么反过来,当我们使用的是右端点的时候,我们就使用sort(low ,i - 1),sort (i , high)
🐧代码模板
😁这里我给出 y 总代码模板,如果实在不想去讨论边界问题的话,那我们直接上模板永远不会错。
#include <stdio.h>
#define N 1000000
int q[N],n;
void quick_sort(int *q,int low,int high)
{
if(low >= high) return ;
int i = low - 1, j = high + 1, x = q[(low + high) >> 1];
while( i < j )
{
do i++; while(q[i] < x);
do j--; while(q[j] > x);
if(i < j) q[i] = q[i] ^ q[j] , q[j] = q[j] ^ q[i] , q[i] = q[i] ^ q[j];
}
quick_sort(q,low,j),quick_sort(q,j+1,high);
}
int main()
{
scanf("%d",&n);
for(int i = 0;i < n ; i++)
scanf("%d",&q[i]);
quick_sort(q,0,n-1);
for(int i = 0;i < n ; i++)
printf("%d ",q[i]);
return 0;
}
🤞 这里再提一点,归并排序的边界问题也是对应我们快速排序,对于中间点的左值选择或者右值选择而言的,这里我给出归并排序的模板。
void merge_sort(int q[], int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)
if (q[i] < q[j]) tmp[k ++ ] = q[i ++ ];
else tmp[k ++ ] = q[j ++ ];
while (i <= mid) tmp[k ++ ] = q[i ++ ];
while (j <= r) tmp[k ++ ] = q[j ++ ];
for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}
这里特别指出,这都是再Acwing上y总给出的模板
排序结合二分的洛谷好题
这里通过快速排序的一道改进题来讲解,排序和分治以及二分法结合的应用
洛谷 寻找第k个数p1932
🐧这道题的数据是被加强了的,所以直接排序是不能做出来的。
如果是按照 Acwing 上第 k 个数的做法只能得到80分,第五个测试点会时间超限
ac demo
#include<bits/stdc++.h>
using namespace std;
int x[5000005],k;
void qsort(int l,int r)
{
int i=l,j=r,mid=x[(l+r)/2];
do
{
while(x[j]>mid)
j--;
while(x[i]<mid)
i++;
if(i<=j)
{
swap(x[i],x[j]);
i++;
j--;
}
}
while(i<=j);
//快排后数组被划分为三块: l<=j<=i<=r
if(k<=j) qsort(l,j);//在左区间只需要搜左区间
else if(i<=k) qsort(i,r);//在右区间只需要搜右区间
else //如果在中间区间直接输出
{
printf("%d",x[j+1]);
exit(0);
}
}
int main()
{
int n;
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)
scanf("%d",&x[i]);
qsort(0,n-1);
}