题目大意
以下代码可以从数组a[]中找出第k小的元素。
它使用了类似快速排序中的分治算法,期望时间复杂度是O(N)的。
请仔细阅读分析源码,填写划线部分缺失的内容。
题目源码
#include <stdio.h>
int quick_select(int a[], int l, int r, int k) {
int p = rand() % (r - l + 1) + l;
int x = a[p];
{int t = a[p]; a[p] = a[r]; a[r] = t;}
int i = l, j = r;
while(i < j) {
while(i < j && a[i] < x) i++;
if(i < j) {
a[j] = a[i];
j--;
}
while(i < j && a[j] > x) j--;
if(i < j) {
a[i] = a[j];
i++;
}
}
a[i] = x;
p = i;
if(i - l + 1 == k) return a[i];
if(i - l + 1 < k) return quick_select( _____________________________ ); //填空
else return quick_select(a, l, i - 1, k);
}
int main()
{
int a[] = {1, 4, 2, 8, 5, 7, 23, 58, 16, 27, 55, 13, 26, 24, 12};
printf("%d\n", quick_select(a, 0, 14, 5));
return 0;
}
注意:只填写划线部分缺少的代码,不要抄写已经存在的代码或符号。
填空代码
网上给的答案是这样的:
a , i + 1, r , k - i - 1 + l
我写的答案是这样的:
a , i + 1, r , k - i - 1 (虽然,我的答案跑第5小的数完全OK,但是经过求证,我的答案是错的)
题解
网上给的答案是,如果你要找的数不在[l ~ i - 1]区间内,那么就重新递归到quick_select(a , l, r , k)
这个函数开始,因为rand()函数迟早会随机到正确的数字。
注: rand()函数是产生随机数的一个随机函数
虽然我的答案也可以得到正确答案,理由是这样的:下标为i的数是当前一趟排序后正确的结果,当k∈[l , i - 1] , 说明我们要在区间[l , i - 1]中找第k小的数;当k ∈[i + 1 , r]时,我们要在[i + 1 , r]区间上找到第(k - i - 1)小的数。。但是,我不知道为什么找第14小的数的时候程序就死了。。第15小的数还是可以的。
在我模拟了程序运行了之后,我终于明白了。。。
其实,这个跟rand()有关。。
是这样的。。。当k ∈[i + 1 , r]时,我们要在[i + 1 , r]区间上找到第(k - i - 1)小的数。。所给代码运行出来,在第四层递归的时候,在随机数的作用下,q = 13,那么k - i - 1 = 14 - 13 - 1 = 0,所以,没办成求第0小的数。。所以程序挂了。。
那有人就问了,为什么求其他k小的数就没挂呢。这就得从rand()函数说起。。
其实rand()函数虽然是一个产生随机数的函数,但是归根结底,rand函数生成的随机数是根据一个叫做 随机种子 的东西生成的。当随机种子没有变化时,rand出来的数是不变的,随机种子他有一个默认值 为了实现真正随机我们就要改变它,改变随机种子的函数为 srand() 括号里面带一个整数,然后真正意义的随机就要 srand( time( 0 ) ),time这个函数好像是返回现在时间,因为每时每刻时间都在变化 都不一样 所以随机种子也绝对不会相同,就实现了真正的随机。
改变成真正的随机数之后,就更有意思了。
先贴代码:
#include <stdio.h>
#include<math.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<ctime>
using namespace std;
int quick_select(int a[], int l, int r, int k) {
srand(time(0)); //这里这里,改变随机种子
int p = (rand()) % (r - l + 1) + l;
int x = a[p];
// cout << p << " " << x << endl;
{int t = a[p]; a[p] = a[r]; a[r] = t;}
int i = l, j = r;
while(i < j) {
while(i < j && a[i] < x) i++;
if(i < j) {
a[j] = a[i];
j--;
}
while(i < j && a[j] > x) j--;
if(i < j) {
a[i] = a[j];
i++;
}
}
// cout << i << " " << a[i] << endl;
a[i] = x;
p = i;
// for(int i = 0;i < 15;i++){
// cout << a[i] << " ";
// }
// cout << endl;
if(i - l + 1 == k) return a[i];
if(i - l + 1 < k) return quick_select(a , i + 1, r , k - i - 1); //填空
else return quick_select(a, l, i - 1, k);
}
int main()
{
int a[] = {1, 4, 2, 8, 5, 7, 23, 58, 16, 27, 55, 13, 26, 24, 12};
printf("%d\n", quick_select(a, 0, 14, 14));
return 0;
}
比如这个第14小的数。。运行出来或者运行不出来全靠运气。,,ԾㅂԾ,,。。
其实,这个可以理解,因为我不能确定,什么时候随机出来的数使得 (k-i-1) ≤ 0,所以,我填这个答案,总的来说应该是错的。但是根据我所了解的蓝桥测评机制,应该判对了吧。。