2018年第九届蓝桥杯省赛B组真题 E题:快速排序

2018年第九届蓝桥杯省赛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-l+1))

分析:看清题意,这道题是要我们找出第k小的数,根据代码可知,k=5,就是找出第五小的数。
1、这段代码就是随机产生一个区间是[l,r]的下标p;并将a[p]的值给x;和快速排序一样,最终达到的目的就是,使x左边的数都小于x,使x右边的数都大于x。
int p=rand()%(r-l+1)+l;
int x=a[p];
2、将a[p]的值与最后一个数交换,目的是为了保留最后一个数,防止被覆盖(现在可能不理解,往下看)
{
		int t=a[p];
		a[p]=a[r];
		a[r]=t;
	}
3、本题是a[i]和a[j]从数组的两端向中间靠拢,移动的过程中,将数组中比x小的数,放在下标为i的位置上;将数组中比x大的数,放在下标为j的位置上。
4、首先,从数组的开始端往后移动,找到比x大的数,将j位置的数替换成此时找到的数;注意:替换完之后,数组中a[i]和a[j]这两个数一样,即i位置的数还没是原来的;从数组的末尾端开始移动,同理。
注1:在进行第一次将比X大的数换到j位置的时候,此时j位置指向的数是数组中的最后一个数,但是不用担心数组中最后一个元素被替换,因为x已经保存了这个数。这就是我们在2中所讲的交换。
注2:而在后面的循环中会发现这个值被替换前,这个值已经放到其它位置上了,所以不用担心有元素值会没有。
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++;
		}
	}
5、在这一步我们也会发现最后面的元素已经被i所在的元素赋值了,而当前的i还存着应该放在后面的值,所以后面会把这个值替换。
if(i<j)
		{
			a[j]=a[i];
			j--;
		}
6、从尾部往前走,找出比X大的值 放在i的位置,通过上面的步骤,你会发现那个i的值已经存放在别的地方。
while(i<j&&a[j]>x)
			j--;
		if(i<j)
		{
			a[i]=a[j];
			i++;
		}
7、文字讲解,晦涩难懂。所以,我们,“举个栗子”。
如 :{49,38,27,13,65,76,97}。如果p=2,X=27,那么将其换到最后面时就成了{49,38,97,13,65,76,27},此时: i=0,j=6。
第一次,从头开始比较,要找到比27大的数,找的i=0,此时,我们知道j=6。交换:a[6]=a[0]。数组就换成了{49,38,97,13,65,76,49}(x保存了27,所以不用担心!)。然后j=5;此时:i=0,j=5。
从后开始比较时,j=3时,13<27,所以放在i=0的位置,即取代49,变成{13,38,97,13,65,76,49}。此时:i=1,j=3。
i=1时,38>27,所以,38放在j=3的位置,即取代13,变成{13,38,97,38,65,76,49}。此时:i=1,j=2。
j继续- - ,此时i=j=1。同时i左边的数都比X小,i右边的数都比X大,最后把缺少的数(也就是X)放上去。
a[i]=x;
8、通过分析i-l+1(至于为什么加一,是因为数组中的下标是从0开始的,根据题意,我们所求的k是从1开始的,所以要加1) 和k 的大小来进行选择操作。如果因为此时i的值大小就是这个值在排序后的位置,因为i左边的所有数都小于i。
if(i-l+1==k)
		return a[i];
9、如果i-l+1比k大,那就说明k所代表的值在左边,否则就在右边,然后再进行重复的递归操作。
10、因为我们最后判断的是:i-l+1和k的值,l就是我们要求的这段区间的左端点,当我们求x的右边时,也就是i-l+1<k,此时要到x的右边找,所以就是找第k-(i-l+1)的值。在左边还是找第k个值。
if(i-l+1<k)
		return quick_select(a,i+1,r,k-(i-l+1)); //填空
	else
		return quick_select(a,l,i-1,k);

完整代码:

#include<stdio.h>
#include<algorithm>
using namespace std;
#include<string.h>
#include<math.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(a,i+1,r,k-(i-l+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,5));
	return 0;
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值