著名的快速排序算法里有一个经典的划分过程:我们通常采用某种方法取一个元素作为主元,通过交换,把比主元小的元素放到它的左边,比主元大的元素放到它的右边。 给定划分后的 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
采用单调栈
本题的关键就是判断元素X的左边是否存在比X更大的元素,X的右边是否存在比X更小的元素。如果使用嵌套for循环的话可以暴力解决,时间复杂度为O(n2);当然,也可以使用数组max和数组min,分别存储元素X左边的最大值和X右边的最小值,采用动态规划思想解决。
这里我使用的是单调栈,其实也就是动态规划的思想,使用一个单调递增栈和一个单调递减栈。
注意,如果没有元素满足情况,在输出0之后还要再输出一个空行
#include<iostream>
#include<algorithm>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<cstdlib>
using namespace std;int main(){int n;cin>>n;long long *num=new long long[n+2];int *small=new int [n+2];int *big=new int[n+2];for(int i=0;i<n;i++){cin>>num[i];small[i]=-1;big[i]=-1;}stack<int> s1;// 向右单调递增栈 for(int i=0;i<n;i++){while(!s1.empty()&&num[i]<num[s1.top()]){small[s1.top()]=i;s1.pop();}s1.push(i);}stack<int> s2;
// 向左单调递减栈 for(int i=n-1;i>=0;i--){while(!s2.empty()&&num[i]>num[s2.top()]){big[s2.top()]=i;s2.pop();}s2.push(i);}vector<long long> v1;for(int i=0;i<n;i++){if(small[i]<0&&big[i]<0) v1.push_back(num[i]);}cout<<v1.size()<<endl;if(v1.size()>0){sort(v1.begin(),v1.end(),[&](long long a,long long b){return a<b;});cout<<v1[0];for(int i=1;i<v1.size();i++)cout<<" "<<v1[i];}// 如果没有元素了,直接输出一个空行 cout<<endl;return 0;
}