PTA Basic #1045 快速排序

博客探讨了快速排序算法中的主元选择问题,通过举例说明如何确定可能的主元,并针对给定的输入格式和样例,分析了两种解决方案:暴力检测和优化后的策略。虽然优化减少了运行时间,但在某些测试点上仍未能通过。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1045 快速排序

著名的快速排序算法里有一个经典的划分过程:我们通常采用某种方法取一个元素作为主元,通过交换,把比主元小的元素放到它的左边,比主元大的元素放到它的右边。 给定划分后的 N 个互不相同的正整数的排列,请问有多少个元素可能是划分前选取的主元?

例如给定 N=5, 排列是1、3、2、4、5。则:

  • 1 的左边没有元素,右边的元素都比它大,所以它可能是主元;
  • 尽管 3 的左边元素都比它小,但其右边的 2 比它小,所以它不能是主元;
  • 尽管 2 的右边元素都比它大,但其左边的 3 比它大,所以它不能是主元;
  • 类似原因,4 和 5 都可能是主元。

因此,有 3 个元素可能是主元。

输入格式:

输入在第 1 行中给出一个正整数 N(≤105); 第 2 行是空格分隔的 N 个不同的正整数,每个数不超过 109。

输出格式:

在第 1 行中输出有可能是主元的元素个数;在第 2 行中按递增顺序输出这些元素,其间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

5
1 3 2 4 5

输出样例:

3
1 4 5

基本思路

1. 把所有输入的数字当成一个数组,分别用两个循环分别暴力检测左侧的数和右侧的数是否满足条件。

结果:这样做会导致程序运行时间超时。

2. 注意到样例输出的数字是依次递增的;那么,在一次循环之后,如果该数不行,则寻找下一个第一个比该数大的数,再进行数字的比较。这样可以大幅减少程序运行时间。

结果:大幅优化了程序运行时间,但仍然挂测试点1.

图解:

 

代码如下:

#include <stdio.h>
#include <stdbool.h>
int main(){
	int nCnt = 1;
	int nIn = 0;
	int rIndex = 0;
	
	int nNum[100001] = {0,};
	int rNumArr[100001] = {0,};
	/** 输入部分 **/
	scanf("%d", &nCnt);
	while(nIn < nCnt){
		scanf("%d", &nNum[nIn++]);
	}
	bool isBreak = false;
	int k_temp = 0;
	
	/**判断部分 **/
	
	for(int i = 0; i < nCnt; i++){
		//如果第一个数不行,那么比第一个数小的也不行。
		//因此无需再考虑该数左端的所有数;可节省运行时间
		//for(int j = 0; j < i; j++){
		//	if(nNum[j] > nNum[i]){        //左侧的数不能比选定的数大
		//		isBreak = true;
		//		break;
		//	}
		//}
		if(!isBreak){
			for(int k = i + 1; k < nCnt; k++){
				if(nNum[k] < nNum[i]){    //右侧的数不能比选定的数小
					isBreak = true;
					k_temp = k;
					break;
				}
			}
		}
		if(!isBreak){
			rNumArr[rIndex++] = nNum[i];
		}
		for(; k_temp < nCnt; k_temp++){   //找到第一个比当前数更大的数
			if(nNum[k_temp] > nNum[i]){
				i = k_temp - 1;
				break;
			}else if(k_temp == nCnt - 1){ //如果循环到数列尾部仍无法找到
									      //说明没有更多的主元了
				i = nCnt - 1;             //直接跳到数列最尾端
			}
		}
		isBreak = false;
	}
	
	/**输出部分**/
	
	if(nCnt != 0){
	printf("%d\n", rIndex);
	}else{
		printf("%d\n", rIndex);
	}
	for(int m = 0; m < rIndex; m++){
		if(m != rIndex - 1){
		printf("%d ", rNumArr[m]);
		}else{
		printf("%d", rNumArr[m]);
		}
	}
	printf("\n");
	return 0;
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值