PAT乙级刷题之路1045 快速排序 (25分)

1045 快速排序 (25分)

著名的快速排序算法里有一个经典的划分过程:我们通常采用某种方法取一个元素作为主元,通过交换,把比主元小的元素放到它的左边,比主元大的元素放到它的右边。 给定划分后的 N 个互不相同的正整数的排列,请问有多少个元素可能是划分前选取的主元?
例如给定 N = 5 N = 5 N=5, 排列是1、3、2、4、5。则:
1 的左边没有元素,右边的元素都比它大,所以它可能是主元;
尽管 3 的左边元素都比它小,但其右边的 2 比它小,所以它不能是主元;
尽管 2 的右边元素都比它大,但其左边的 3 比它大,所以它不能是主元;
类似原因,4 和 5 都可能是主元。
因此,有 3 个元素可能是主元。

输入格式:
输入在第 1 行中给出一个正整数 N(≤10​的5次方); 第 2 行是空格分隔的 N 个不同的正整数,每个数不超过 10^9。
输出格式:
在第 1 行中输出有可能是主元的元素个数;在第 2 行中按递增顺序输出这些元素,其间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:
5
1 3 2 4 5
输出样例:
3
1 4 5

分析:

直接比较每个元素与他前面和后面的所有元素显然会超时,我们可以利用主元的特点为每个大于前面最大元素并且小于后面最小元素的元素做标记,认为他可能是主元。

代码:

#include <stdio.h>
#include <algorithm>
#include <vector>
#define N 100000 + 10
using namespace std;
int data[N]; // data用来存储输入的N个正整数
int left_flag[N];// 用来标记数组中的每个元素是否大于它左边的所有元素
int righ_flag[N];// 用来标记数组中的每个元素是否小于它右边的所有元素
int main(){
	int n;
	scanf("%d", &n);
	for(int i=0; i<n; ++i){
		scanf("%d", &data[i]);
	}
	int left_max = 0;// 由于输入的是N个不同的正整数,所以left_max初值设置为0即可
	for(int i=0; i<n; ++i){
		if (data[i] > left_max){// 如果当前元素大于左边所有元素的最大值,也就是说当前元素大于它左边的所有元素		    
			left_max = data[i];// 更新一下最大值
		    left_flag[i] = 1;// 标记一下这个元素			
		}
	}
	int righ_min = 1000000000 + 1;// 由于输入的N个整数都不超过10^9,所以righ_min初值设置为10^9 + 1   
	for(int i=n-1; i>=0; --i){
		if(data[i] < righ_min){// 如果当前元素小于右边所有元素的最小值,也就是说当前元素小于它右边的所有元素	    
			righ_min = data[i];// 更新一下最大值		
			righ_flag[i] = 1;// 标记一下这个元素
		}
	}
	vector<int> answer;
	for(int i=0; i<n; ++i){
		if(left_flag[i] == 1 && righ_flag[i] == 1){//如果它既大于左边的所有元素又小于右边的所有元素	    
			answer.push_back(data[i]);
		}
	}
	sort(answer.begin(), answer.end());// 把所有可能的主元排一下序
    int count = answer.size();
	printf("%d\n", count);// 输出有多少个主元
	if(count == 1){
		printf("%d\n", answer[answer.size()-1]);// 如果只有一个,直接输出并且换行
	}else if(count > 1){
	    for(int i=0; i<answer.size()-1; ++i){
		    printf("%d ", answer[i]);
	    }
	    printf("%d\n", answer[answer.size()-1]);
	}else{	    
		printf("\n");// 本题的一个坑,没有主元的时候需要单独输出一个换行。
	}
	
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值