PAT乙级真题 1045 快速排序 C++实现

题目

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

思路

一开始只想到了暴力求解,但时间复杂度是不允许的,在网上看了大神们的思路,重点是:

若当前元素是上一趟快速排序选的主元,则快排后其位置不变。

基于此,猛地以为对原数组排序后,看哪些元素位置不变即可认定是主元。但结果仍是错的,如测试用例:

5 4 3 2 1

虽然3的位置在排序后不变,但该数列实则没有一个主元。

可见,“排序后元素位置不变”是“是主元”的必要不充分条件,为了确定主元,还需加上额外判断:

主元大于其左侧所有元素。

即可。

总体来说,加上位置相同的信息后,相比于暴力求解能少做一半的比较。

最后需要注意主元个数为0的情况,第二行可能是空行(空行也要输出)。

算法

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n;
    cin >> n;
    vector<int> a(n);
    
    for (int i=0; i<n; i++){
        cin >> a[i];
    }

    vector<int> b(a);
    sort(a.begin(), a.end());

    int max = 0;
    vector<int> c;
    for (int i=0; i<n; i++){
        if (a[i]==b[i] && b[i]>max){
            c.push_back(a[i]);
        }
        if (b[i]>max){
            max = b[i];
        }
    }

    cout << c.size() << endl;
    if (c.size()>0){
        sort(c.begin(), c.end());
        cout << c[0];
        for (int i=1; i<c.size(); i++){
            cout << " " << c[i];
        }
    }
    cout << endl;
    return 0;
}

发布了105 篇原创文章 · 获赞 7 · 访问量 1754

猜你喜欢

转载自blog.csdn.net/zhang35/article/details/103794740