前言
终于打算写点普及难度的题,看到一个跟分治思想有关的题目,就打算去写写试试。写放题目位置:
P1923 【深基9.例4】求第 k 小的数
解题思路
这题的解题思路有很多,例如
(1)你可以先给读入的数组排序,然后选择第k个数作为答案打印出来,这样肯定也是对的。我归纳了三种排序算法的模板,快速排序,堆排序和归并排序,链接在此:三种排序算法
(2)在前述排序的基础上,你也可以不进行完全的排序。这就是说,既然我要的是第k小数,那么我不断从最小的第0小数开始找,直到找到第k小数不就行了吗?理论上可以,但是如果k特别大,且几乎接近n,那么这样做时间复杂度被拉到O(
n
2
n^2
n2),也可能会导致TLE
(3)第三种做法,也是本次我AC的做法,就是,既然我只要第k小数,那么我就把注意力放到第k小数上去,而不关心其余的次序上的数。这么依赖,有可以借鉴的吗? 有,那就是快速排序。回想一下,快速排序的一个思想核心不就是,一趟排序完通过基准将原序列划分为小于基准的一部分和大于基准的一部分。一趟排序完成,那么基准是第m小,我们通过下标找到。那么,现在一个显而易见的思路就是,将寻找第k小数嵌入到快速排序中。
如何嵌入到快速排序中呢?我们可以想,我们要的是在left到right区间上找第k小数,那么,我们就先对left到right区间上的数做一个快速排序,可以选择固定基准a[left],这样,排序完成后返回基准的新位置loc。这样,当k恰好就是loc-left的时候,这个第k小数就是我们要找的;当k小于loc-left的时候,k就在left到loc-1的区间内,那么问题就变成了在left到loc-1区间上找第k小数;当k大于loc-left的时候,问题就变成了在loc+1到right的区间上找第k-(loc+1-left)小数(这里的一个前提如题目要求,第k小数的k从0开始)。这样,问题就是一个典型的分治法策略了。给出我的AC代码:
#include <stdio.h>
#include <stdlib.h>
int quick_sort(int left,int right,int a[]){
int i=left,pro=a[i],j=right;
while(i<j){
while(i<j&&a[j]>pro)
j--;
if(i<j){
a[i]=a[j];
i++;
}
while(i<j&&a[i]<=pro)
i++;
if(i<j){
a[j]=a[i];
j--;
}
}
a[i]=pro;
return i;
}
int find_k(int left,int right,int a[],int k){
int num,loc;
loc=quick_sort(left,right,a);
if(left+k<loc)
num=find_k(left,loc-1,a,k);
else if(left+k==loc)
num=a[loc];
else
num=find_k(loc+1,right,a,k-(loc-left+1));
return num;
}
int main(int argc, char *argv[]) {
int i,n,k,num;
scanf("%d",&n);
scanf("%d",&k);
int a[n];
for(i=0;i<n;i++){
scanf("%d",&a[i]);
}
num=find_k(0,n-1,a,k);
printf("%d\n",num);
return 0;
}