蓝桥杯_第九届_C/C++B组第五题_快速排序

参考答案:quick_select(a, i+1, r, k - ( i - l + 1 ) )

题目
标题:快速排序。 

以下代码可以从数组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;
}

注意:只填写划线部分缺少的代码,不要抄写已经存在的代码或符号。
哇塞

以为是道送分题,没想到其实是送命题。直接代码匆匆一瞥,快排,那不就是判断左右部分,递归就是了。
然后秒写上 == quick_select(a, i, r, k) ==,一试,可以诶,准备百度查一下验证一下答案。

:他们的怎么那么复杂
:眉头一皱,两三个都是这答案,这事情不对劲。。。

然后,用随机数产生数组,测试。 我去,这题有坑…

后知后觉,难怪题目里给的还是伪随机数…出题人好鸡贼啊。。。吃到教训了,太年轻鸭!!!
所以,要多次测试哟
——————————————————

blah blah

瞎试出来的:
return quick_select(a, i + 1, r , k - (i - l + 1) );
还真行,嘿嘿~

ps:其实就是是拿了一个简单的样例, 5 4 3 1 2,手写一遍,然后试出来的。

客官!留步,听听思路否?

以下代码段,其实就是快排的思想,是一种实现快排的思路,如果不理解的话,可以搜搜本题题解有个高赞的博文,讲得很详细!
快排算法代码段

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;		//这行是蜜汁代码,忽略,骗人的

我来讲讲,填空的部分,如下代码段,记为B

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);										  //左半部分区间

你想哈,第一次调用该函数的时候, i - l + 1代表:分界线(即x)的位置,如果相等,即找到了,返回数值即可;

如果未找到,只有两种情况:情况1——K在左半部分;情况2——K在右半部分。

情况1:递归调用,区间是左半部分,并且索引位置的值是K

情况2:递归调用,区间是右半部分,但是,索引位置不能是K了,你想哈,区间这时候变成了【i + 1,r】,当下一次执行到代码段B的时候,i - l + 1还是代表此时的分界线的位置,但是对于K, i - l + 1的值不能跟他作比较了,因为区间直接少了左半部分了,所以索引位置得值应该改为:K - (左半部分区间的长度)

感觉还是讲的不是很清楚,那我,举个栗子:
eg. 取数组a为 ### 5, 4, 3 , 1 , 2 ###,调用 quick_select(a, 0, 4, 4),注意K = 4噢噢噢噢噢噢噢,即查找第4个数是什么; 第一次运行到代码段B的时候,假设情况是:1 ,2, 3, 4, 5,,,x取的是3,下标为2,是第三个数,我们要的是第四个数。则此时要继续去右半部分查找了,那我们查找区间是【3,4】,此时若x取的是值4,那就是我们要的答案,但是i - l + 1(x的下标i = 3,l = 3)的值是,3 - 3 + 1 = 1,并不是K 的 初始值4!!!你会发现,差的值就是少掉的左区间长度,因为少了左区间的那部分,就是1、2、3这部分,差了3个数,所以我们应该调用quick_select(a, i + 1, r , k - (i - l + 1) )
————————————————————————————————
以上
END

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值