【PAT B1045/A1101】快速排序

 

题目:

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

例如给定 , 排列是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

 

方法思路:

直接思路(也即暴力方法):对每一个位置元素,向左排查是否有元素大于它,有就标记此元素非主元;左边满足都不大于此元素的情况下,向右排查是否有右边的元素小于它,同样有就标记此元素非主元;

此算法复杂度:对n个元素输出其中的非主元元素,每一个元素都要和其余的n-1个元素进行比较,复杂度为O( n*(n-1) ) = O( n^2 )

暴力算法在这题上提交会超时。

更快算法:

 考察序列1:   3   7   5   8   10
 ​
    排序后:   3   5   7   8   10

可以观察到如果是主元元素,其排序后的位置和排序前的位置一样,如 3, 8 ,10,而 5 和 7 都不是主元。

  • 为什么主元元素排序前和后的位置一样呢?
  • 答:我们知道对一个主元元素p来说,(假设序列各元素互异),其前面a个元素都比它小,其后面b个元素都比它大,不管这a个元素和这b个元素内部是否排序,{a} < p < {b},元素p的位置是确定的,所以排序前这个位置是主元,那么排序后也必须是同样的数。
 考察序列2:   1   2   8   4   3  5
 ​
    排序后:   1   2   3   4   8  5

序列2 里 数4排序前和排序后的位置一样,但是数4却不是主元,因为前面5比它,后面3又比它小,显然不是主元。

所以仅仅满足排序前后排序后位置一样而得出是主元的结论是不够的。

观察可以得到在这个序列里只有 1 和 2 是主元, 8 ,4,3 ,5都不是主元。

数5因为和排序后位置上的8不等,所以不是主元;

而对于数4,考察其位置,从序列开始到其位置的所有数中最大值应该是8,先不管4后面的数,我们知道在某一个位置对其前面所有元素来说最大值应该在此位置上,因为主元的左边都比它小。而数4前面有了比它大的数,所以数4就不是主元。同样数5也是一样的,对于数5位置来说,从序列开始到其位置的所有数中最大值也是8,即数5前面有了比它大的数,所以数5不是主元。

  • 有人也许会问:

   对于位置 i 来说 已经满足其前面到此位置的所有数中的最大值在此位置上了,难道就不怕此位置后面有比其小的元素吗?

  • 答:如果位置 i 已经满足其前面到此位置的所有数中的最大值在此位置上,那么其前面的元素一定小于位置i上的元素,而我们的第一个必要条件是【排序前此位置上的数和排序后此位置上的数相等】,就能保证位置 i 后面的元素都比其小。

综上,可以得到判断是主元的两个条件

考察数组中某位置

1.排序前此位置上的数和排序后此位置上的数相等

2.此位置上的数也必须是从序列开始到此位置上的最大值


示例代码:
 1  #include<stdio.h>
 2  #include<algorithm>
 3  using namespace std;
 4  5  const int MAXN = 100010;
 6  int A[MAXN];//原数组
 7  int pivot[MAXN] = {0};//主元数组
 8  int A_sorted[MAXN];//对原数组排序后的数组
 9 10  int main()
11  {
12      int n;
13      int pivot_num = 0;
14      scanf("%d",&n);
15      for(int i=0;i<n;i++){
16          scanf("%d",&A[i]);
17          A_sorted[i] = A[i];
18      }
19      sort(A_sorted,A_sorted + n); //对原数组进行排序
20 21      int max_num = 0;
22 23      for(int i=0;i<n;i++){
24          if(A[i] > max_num)
25              max_num = A[i];//计算到从 序列开始 到 位置i 的 最大值
26          if(A[i] == A_sorted[i] && A[i] == max_num)//满足上述所述两个条件
27              pivot[pivot_num++] = A[i];
28 29      }
30 31      printf("%d\n",pivot_num);
32      if(pivot_num > 0)
33          printf("%d",pivot[0]);
34      for(int i=1;i<pivot_num;i++){
35          printf(" %d",pivot[i]);
36      }
37      printf("\n");
38      return 0;
39  }

 

 时间复杂度分析:sort排序O(nlogn),一个循环顺序扫描O(n),总的时间复杂度O(nlogn+n)

参考:

[PAT B1045](https://pintia.cn/problem-sets/994805260223102976/problems/994805278589960192)

[[1045. 快速排序](https://www.cnblogs.com/andywenzhi/p/5831431.html)]

posted @ 2019-07-15 17:29  dekeshile  阅读(180)  评论(0编辑  收藏  举报