PAT 乙级 1045 快速排序

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

这道题有多种解法,先写我的解法。

思路是:建立两张表,第一张表以元素在数组的位置为关键字,以从左开始截止到当前元素所在位置这一段内的最大元素的大小为值。第二张表以元素所在数组的位置为关键字,以从右开始截止到当前元素所在位置这一段内的最小元素的最小值为值。若一个元素为主元,则在这个元素的左边没有比该元素大的值,在该元素右边没有比该元素小的值。那么判断主元的时候直接通过查表就可以了。
时间复杂度Θ(n) 空间复杂度Θ(n)

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>

using namespace std;

void judge(int* p,int n);

int main()
{
    int n;
    cin>>n;
    int* p=new int[n];
    for(int i=0; i<n; i++)
        scanf("%d",&p[i]);
    if(n!=0)
        judge(p,n);
    else
        cout<<0<<endl<<endl;
    delete p;
    return 0;
}

void judge(int* p,int n)
{
    int* max=new int[n];
    int* min=new int[n];
    memset(max,0,n*sizeof(int));
    memset(min,0,n*sizeof(int));
    min[n-1]=p[n-1];
    max[0]=p[0];
    for(int i=1; i<n; i++)
        max[i]=p[i]>max[i-1]?p[i]:max[i-1];
        
    for(int i=n-2; i!=0; i--)
        min[i]=p[i]<min[i+1]?p[i]:min[i+1];

    min[0]=p[0]<min[1]?p[0]:min[1];

    int* vtor=new int[n];
    memset(vtor,0,n*sizeof(int));
    int x=0;
    //如果第一个元素为最小的元素则可以作为主元 
    if(p[0]<=min[0])
        vtor[x++]=p[0];
    //若最右边的元素时最大的元素则可以作为主元 
    if(p[n-1]>=max[n-1])
        vtor[x++]=p[n-1];
    
    //通过查表确定主元 
    for(int i=1; i<n-1; i++)
    {
        if(p[i]>=max[i] && p[i]<=min[i])
            vtor[x++]=p[i];
    }
    if(x!=0)
    {
        cout<<x<<endl;
        //升序排列 
        sort(vtor,vtor+x);
        for(int i=0; i<x-1; i++)
            printf("%d ",vtor[i]);
        printf("%d",vtor[x-1]);
    }
    else
        cout<<0<<endl<<endl;
    delete max;delete min;delete vtor;
}

网上还存在三种解法:

正反遍历法(和我的方法是一回事,不过这个方法的占用的空间更少)

思路是:从左到右遍历一遍,选出满足“左边的元素都比当前元素大“的元素作为待选元素,再从右到左遍历一遍,选出满足“右边的元素都不比当前元素小”且”在从左到右遍历的时候被作为待选元素“两个条件的元素作为主元。
时间复杂度Θ(n) 空间复杂度Θ(n)

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>

using namespace std;

void judge(int* p,int n);

int main()
{
    int n;
    cin>>n;
    int* p=new int[n];
    for(int i=0; i<n; i++)
        scanf("%u",&p[i]);
    if(n!=0)
        judge(p,n);
    else
        cout<<0<<endl<<endl;
    delete p;
    return 0;
}

void judge(int* p,int n)
{
    bool* q=new bool[n];
    memset(q,0,n*sizeof(bool));
    int count=1;
    q[0]=true;
    for(int i=1,max=p[0]; i<n; i++)
    {
        if(p[i-1]>max)
            max=p[i-1];
        //如果不小于当前已知的最大值,则作为待选元素 
        if(p[i]>=max)
        {
            q[i]=true;
            count++;
        }
    }
    for(int i=n-2,min=p[n-1]; i>=0; i--)
    {
        if(p[i+1]<min)
            min=p[i+1];
        //如果大于当前已知的最小值,则从待选元素中删除 
        if(p[i]>min && q[i]==true)
        {
            q[i]=false;
            count--;
        }
    }
    if(count!=0)
    {
        cout<<count<<endl;
        int i;
        for(i=0; i<n && count!=1; i++)
        {
            if(q[i]==true)
            {
                cout<<p[i]<<" ";
                count--;
            }
        }
        for(;i<n;i++)
        {
            if(q[i]==true)
                cout<<p[i];
        }
    }
    else
        cout<<0<<endl<<endl;
    delete q;
}

基于主元位置不变的方法

思路是:先把所有元素拷贝到另外一个数组里,并对该数组进行升序排序。若一个元素不小于左边的所有元素,且在原始序列中的位置和有序序列中的位置相同,则该元素为主元。
时间复杂度Θ(nlgn) [使用比较排序] Θ(n) [使用线性时间的排序] 空间复杂度Θ(n)

为什么一个元素不小于左边所有的元素且在两个序列中位置相同就一定是主元?

  • 首先,作为主元的必要条件为,不小于左边的所有元素,不大于右边的所有元素
  • 为什么要满足”不小于左边的所有元素“不做解释
  • 为什么同时满足"原始序列中的位置和有序序列中的位置相同"就是主元?因为在排序时,若存在一个待选元素大于右边的某个元素,那么在排序时肯定位置会发生变动,那么这个元素就不能作为主元。

#include <iostream>

#include <algorithm>

#include <stdio.h>

using namespace std;

 

int main()

{

   int N;

   scanf("%d",&N);

   int num[N],num_s[N];  //一个原始数据,另一个保存已经排好的数据

   for(int i=0;i<N;i++)

   {

       scanf("%d",&num[i]);

       num_s[i]=num[i];

   }

   sort(num_s,num_s+N);

   int count=0,max=0;

   int res[N];

   

   for(int i=0;i<N;i++)

   {

       if(num[i]>max)

            max=num[i];

        if(max==num[i] && num[i]==num_s[i])  //如果当前数是从第一个数到当前数最大的一个,且与排完顺序对应位置的数相同则该数就有可能是主元

            res[count++]=num[i];

   }

    printf("%d\n",count);

    //sort(res,res+count);

    for(int i=0;i<count;i++)

        if(i==0)

             printf("%d",res[i]);

        else

            printf(" %d",res[i]);

    printf("\n");

    return 0;

}

(代码出处 https://blog.csdn.net/gq_bob/article/details/49520161

单次遍历排除法(个人喜欢叫做”在线处理“)(来自 https://www.cnblogs.com/hkevin/p/5510063.html

思路是:从左到右遍历元素,每次遍历用当前元素和当前已知的最大值比较。

  • 若当前元素不小于当前已知的最大值,则作为待选元素储存。(题目保证正整数不重复,所以把”不小于“改成”大于“也可以)。
  • 若当前元素小于当前已知的最大值,则遍布待选元素列表,把比当前元素大的待选元素删除。

时间复杂度Θ(n^2) 空间复杂度Θ(n)

这里要说明以下,在 https://www.cnblogs.com/hkevin/p/5510063.html 中作者说明自己的方法时间复杂度为O(n),这里本人提出质疑,从下面的的代码可以看出,外循环内部的一个分支会执行一个循环,而内循环最多会执行n次,那么分析时间复杂度的原则是按照时间复杂度高的分支进行分析,那么时间复杂度应该为O(n^2)。如果考虑平均情况,时间复杂度为Θ(n^2),最好的情况才为Ω(n),我把代码复制了下来在PAT跑了一遍,发现耗费的时间和前几种方法差不多,自己又造了一些数据希望可以构造出一种接近于最坏情况来使该算法效率降低,但是未果。从逻辑上来看,内循环的执行次数相当得少,但是个人认为存在一种最坏情况可以大幅度提高该算法得时间消耗。
这里提出质疑并不是要贬低他人,是要说出自己的疑惑,如果你和我有同样的想法,或者可以解决我的疑惑,还请联系我哦。

#include <iostream>  
  
using namespace std;  
  
int main()  
{  
    int N;  
    cin>>N;  
    int i = 0 , max = 0 ,  maxi = 0;  
    int Nums[100000] = {0};  
    int n;  
    for (i ; i < N; i++)  
    {  
        scanf("%d",&n);  
        if (n > max) // 如果比现在最大值大 一定可以进来  
        {  
            Nums[maxi] = n;  
            maxi++;  
            max = n;  
        }  
        else // 不能成为主元 但却可以淘汰其他主元  
        {  
            int j = 0;  
            for ( j = maxi-1; j >=0 ; j--)  
            {  
                if (Nums[j] > n)  
                {  
                    Nums[j] = 0;  
                    maxi--;  
                }  
                else  
                {  
                    maxi = j+1;  
                    break;  
                }  
            }  
  
        }  
    }  
    cout<<maxi<<endl;  
    if (maxi != 0)  
    {  
        for (i = 0 ; i < maxi-1 ; i++)  
            cout<<Nums[i]<<' ';  
        cout<<Nums[i]<<endl;  
    }  
    else  
        cout<<endl;  
    system("pause");  
}  

(代码来自:https://www.cnblogs.com/hkevin/p/5510063.html

猜你喜欢

转载自www.cnblogs.com/FDProcess/p/9241376.html