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
思路1:
暴力法:遍历每一个元素,对每一个元素的前后进行判断,判断它是否是主元(特判首尾),时间复杂度O(n^2)。最后注意格式输出即可。
参考代码1:
#include <cstdio>
int main(){
int N;
scanf("%d",&N);
int num[100010];//输入数据;
int ans[100010];//最终结果;
int count=0;//输出数据个数;
for(int i=0;i<N;i++){
scanf("%d",&num[i]);
}
for(int i=0;i<N;i++){//遍历每一个元素;
int flag=1;
if(i==0) {//特判首部;
for(int j=i+1;j<N;j++){
if(num[j]<num[i]) flag=0;
}
}
else if(i==N-1){//特判尾部;
for(int j=0;j<N;j++){
if(num[j]>num[i]) flag=0;
}
}
else{//普遍情况;
for(int j=0;j<i;j++){
if(num[j]>num[i]) flag=0;
}
for(int j=i+1;j<N;j++){
if(num[j]<num[i]) flag=0;
}
}
if(flag==1){//若flag=1,说明各方面满足条件,他是主元;
count++;
ans[count]=num[i];
}
}
printf("%d\n",count);
for(int i=1;i<count+1;i++){
if(i!=count) printf("%d ",ans[i]);
else printf("%d",ans[i]);
}
return 0;
}
思路2:
leftmax,rightmin数组用于存放当前位置左边最大的数和当前位置右边最大的数。对每一个元素进行判断,若当前元素比他左边最大的元素还要大且比他右边最小的元素还要小,则说明他是主元。最后按格式输出即可。
参考代码2:
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=100010;
const int INF=0x3fffffff;//一个很大的数;
int a[maxn],leftmax[maxn],rightmin[maxn];// 存储原数据,某一元素左边最大的数,某一元素右边最小的数;
int ans[maxn],num=0;
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
leftmax[0]=0;//a[0]左边没有比他大的数;
for(int i=1;i<n;i++){
leftmax[i]=max(leftmax[i-1],a[i-1]);//由i-1推得i;
}
rightmin[n-1]=INF;//a[n-1]右边没有比他小的数;
for(int i=n-2;i>=0;i--){
rightmin[i]=min(rightmin[i+1],a[i+1]);//由i+1推得i;
}
for(int i=0;i<n;i++){//左边所有数比他小,右边所有数比他大;
if(leftmax[i]<a[i]&&rightmin[i]>a[i]) ans[num++]=a[i];
}
printf("%d\n",num);
sort(ans,ans+num-1);
for(int i=0;i<num;i++){
if(i!=num-1) printf("%d ",ans[i]);
else printf("%d",ans[i]);
}
printf("\n");//必须要有换行;
return 0;
}
点评:
①此题虽名为快速排序但实际上与快排没有任何关系,只是借用了主元这一概念,与B1040题的思路 非常类似,注意体会两个辅助数组left,right的作用。
②题目要求在第二行中按序输出这些主元,可能会想到用sort排序一下,其实大可不必。因为根据主元的定义,每一个主元的前面元素必定小于这个主元,所以若这个主元前面有其他主元则也是小于这个主元的,这就保证了主元的递增性质。笔者不禁思考,这一性质是否具有更深层更本质的原因?由于天色已晚&&还没吃饭,不打算再过多纠缠。欢迎小伙伴们评论告知(哈哈没人看的吧)。
③若使用sort函数反而会使有的样例点超时,因为sort函数的时间复杂度为O(n*logn)。