2018蓝桥杯省赛C/C++ B组——快速排序(自证自己的答案是错的,也欢迎大佬来指证)

题目大意

以下代码可以从数组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,所以,我填这个答案,总的来说应该是错的。但是根据我所了解的蓝桥测评机制,应该判对了吧。。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值