蓝桥杯第09届 2018年省赛C/C++大学生B组——试题5 快速排序

试题5 快速排序

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

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

解题思路:

快速排序的一个算法模板,只要你懂的快速排序的原理,就只需要稍微阅读下这个代码就可以填写出来了,答案在本博客最后。
如果不懂的快速排序,你可以继续往下看:

快速排序

快速排序是一个算法复杂度为 O(N) 的一种排序算法,他处理的过程是:

  1. 从一堆数中,任意找一个数作为基准,将这堆数中的比他小的放到他的前面,比他大的放到后面。(如果是从大到小,反着来)
  2. 然后递归左半边和右半边

话不多说,来个例子。

有8个数:8 4 7 58 2 1 5 23,从小到大排序。

我们定义一个函数 sort(int a, int b) 函数,表示从下标 a,到下标 b-1 这段区间进行快速排序,并且我们规定每次都选取这段区间的第一个数作为基准数。

那么一开始 sort(0, 8) ,用变量 x 来存放这个基准数,x = 8
那么我们要怎么样才能把大于大的数换到左边去,小于与到的数换到右边去呢?

可以这样,我们定义两个变量 i,j,分别指向这段数组的头的位置尾的位置

  1. 首先让 j 往前移动,当找到第一个比他小的数,就将这个数的值放到 i 所指的下标。(如下图)
    在这里插入图片描述

在这里插入图片描述
填值

  1. 接下来,轮到 i 下标开始往前移动,当找到第一个比基准值大的值,就把 i 下标下的值放到 j 下标对于的位置。
    在这里插入图片描述

在这里插入图片描述
填值

  1. 循环 1,2 这个过程,直到不满足 i < j 这个条件的时候,结束这个循环。在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 结束循环后,i 和 j 的位置如图:
    在这里插入图片描述
    这个个时候,将原先的X的值,放到这个位置 i 和 j 所指的位置上。
    在这里插入图片描述
    这样左边就是比8小或等于的数,右边就是比8大的数。
    然后循环递归左半边,和右半边。
题目代码解析

而蓝桥的题目,不是要我们排序,而是要我们找出数组中,第K小的数。
那我们每次找的基准数,按从小到大,在基准数前面的数的个数+1,就是他要找的K。

所以关键就是懂的题目给出的这个函数的意思

// 函数参数的意思:a数组,l左端点,r右端点,k要找的第几个小的数
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);
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hiram Fan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值