美文网首页
(原创,步进分析,24ms)PAT乙级1045 快速排序

(原创,步进分析,24ms)PAT乙级1045 快速排序

作者: 仰天蓬蒿人 | 来源:发表于2019-02-01 10:19 被阅读0次

    题目

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

    分析

    这条题,按照我做题原则,先好好思考,整理逻辑,实在不行再参考别人。
    一开始感觉应该挺简单,首先排除暴力分析,用步进分析法+双指针应该可以一次循环搞掂。
    可是做着做着,我用一些测试用例通不过,发现这个逻辑有漏洞,这一分析,发现更多种情况要填要弥补,my god
    对于我来说,思考太久一直是我弱项,可能没有写草稿的习惯,后来我尝试通过用例来弥补各种情况的逻辑编写

    (请大家不吝赐教一些快速思路。)

    最后也发现这样写完后也顺利一次通过。
    然后再去看看别人写的,除了我们最后输出的思路一样,但在处理上他们基本上用STL封装好的排序sort,或者结合reverse。我才知道我们思考的区别,因为根据快速排序的主元特性,我一开始觉得没必要去排序或者再增加空间再来筛选,它已经就是按照原样进来。

    主题思路

    辅助空间:nums保存主元的数组,pre指针保存前一个主元,cur指针当前接收的值,minValue遇过的历史最小值,maxValue遇过的历史最大值
    过程:(就是逻辑的暴解)
    1.循环每一个进来的值
    1.1判断 pre是否小于cur
    当前值是否大于上一个主元的值,是就进入与历史最大值或最小值比较的判断
    1.2如果pre==cur
    与历史最大值,历史最小值比较

    这就是我写的其中一些用例分析思路

    
    一种情况
    1 3 5 1000  //pre=2 cur =3,min =1,max =1000,只需要变化pre = cur,cur++
    1 3 5 1000 7 //pre =3,cur =4,min =1 ,max=1000,明显nums[cur]<nums[pre],这两个不能要,MULength-=2,最后pre要退却一位即pre--,cur=pre+1;
    1 3 5 1000(作废) 7(作废) 9 //pre =2 ,cur = 3,虽然符合nums[cur]>nums[pre],但历史中出现过最大值1000,你的cur的值还是不能要,继续cur = pre+1,pre不变
    1 3 5 1000(作废) 7(作废) 9(作废) 2. //pre =2 ,cur = 3,明显nums[cur]>nums[pre],这两个不能要,MULength-=2,最后pre要退却一位即pre--,cur=pre+1;
    最后结果是1 3 5(作废) 1000(作废) 7(作废) 9(作废) 2(作废),但是这个1和3结果明显不能接受,明显3不能要,需要改写,尝试用局部循环判断nums[cur]<nums[pre],直到把pre退却到适合的位置
    
    另一种情况
    2 3 5 1000  //pre=2 cur =3,min =2,max =1000,只需要变化pre = cur,cur++
    2 3 5 1000 1//pre =3,cur =4,min =2 ,max=1000,明显nums[cur]<nums[pre],再发现nums[cur]比历史最小值还要小,前面所有作废,这里就要pre与cur重置了,cur =pre=0,nums[0]=nums[cur],更新一下MULength-cur
    2(作废) 3(作废) 5(作废) 1000(作废) 1(作废)
    
    另一种情况
    2 1 //pre =0,cur =1,min=2,max=2;明显nums[cur]<nums[pre],这两个不能要,MULength-=2,最后pre要退却一位即pre--, 若是pre退无可退,也就是负值,设置pre=cur = 0,否则cur=pre+1;
    
    

    代码

    #include <stdio.h>
    #define MAX_NUM 100000
    #define MAX_INT 0x7fffffff
    unsigned int GetInputToInt();
    int main()
    {
        //nums用来放入数据,N是输入个数, MULength是主元个数长度 
        int nums[MAX_NUM]={0},i=0,N,MULength =0;
        //处理输入
        //例子:2 4 3 1 5 ,只有5才是主元 
        //例子:4 5 6 2 1 8 7 9 3 10   只有10才是主元   用于这个测试点,发现我第一个版本错了,漏了判决 
        MULength = N = GetInputToInt();
        
        //处理与控制:采用双指针控制
        int prePos=0,curPos=0,curMinValue=MAX_INT,curMaxValue=0;
        for(;i<N;i++)
        {
            nums[curPos] = GetInputToInt();
            //(尝试步进分析,一旦遇到前者大于后者,马上重置prePos,剔除该两个,因为都不可能是主元了,
            //1还保留一直遇到过的最小值curMinValue,和遇到过的最大值curMaxValue) 
            if(prePos<curPos)
            {
                //1.1若当前值大不过上一个值 
                if(nums[prePos]>nums[curPos])
                {
                    //(作废情况)
                    //1.3看看是否遇上了比当前最小值还要小的当前值 
                    if(curMinValue>nums[curPos])
                    {
                        //1.3.1 要是发现历史最小值都比不过当前值小,OK,前面全部作废 
                        curMinValue = nums[curPos];
                        //更新MULength,这里分开写,让可读性好点 
                        MULength = MULength - curPos;//前面作废,有多少个,curPos个(curPos是下标,curPos-1就是个数) 
                        MULength--; //再减去当前第curPos那一个 
                        prePos = curPos = 0; 
                    } 
                    else
                    {
                        MULength-=2;//当前这两个肯定不要了(pre与cur)
                        --prePos;
                        //1.3.2 要是发现前面最小值依然坚挺能接受考验。 需要把prePos退却到刚好nums[curPos]>nums[pre]的位置上
                        while(prePos>=0&&nums[curPos]<nums[prePos])
                        {
                            prePos--;
                            MULength--;
                        }
                        if(prePos<0)
                        //什么情况下会来到这里,应该没有,因为有1.3.1过滤了,最小值是来不了  例如,6(作废) 1(作废) 5(作废) 3 ,但pre=0.cur=0,明显,这个例子来不了 ,希望能找到例外 
                            prePos = curPos = 0; 
                        else
                        //来到这里,就是已经找到刚好 nums[curPos]>nums[pre]的位置上,重置curPos 
                            curPos = prePos+1; 
                    }   
                }
                //1.2要是比得过,接下来看情况看下是否更新一下最大值 
                else
                {
                    //(正常情况) 看看是否遇上了比历史最大值还要大的当前值
                    if(curMaxValue<nums[curPos])
                    {
                        curMaxValue = nums[curPos];//保留 
                        prePos = curPos;
                        curPos++; 
                    }
                    //若然当前值比不上历史最大值 
                    else
                    {//(作废情况)
                    //这种情况就好像1,1000,2,3,4,此时cur=4,pre=3,虽然符合pre<pos,但是前面出现过巨无霸1000,在巨无霸后面到当前值都得作废,除非出现过比巨无霸还要猛的。
                        MULength = MULength -1;//减一就是当前cur的值不要了 
                        curPos = prePos+1; //更新下一次应该要填的位置 
                        //prePos = curPos = 0; 
                    }    
                }
                
            }
            //2. 一般进入这里的情况都是,初始化,或者是前面都失效的情况下。
            else if(prePos==curPos)//
            {
                //看情况更新最大值与最小值 
                //2.1 
                if(curMaxValue<nums[curPos])
                    curMaxValue = nums[curPos];
                else
                {//若是连历史最大值都大不上,也就是当前值作废  如 20(作废) 10(作废) 15 ,历史最小值是10 
                    MULength--;
                    prePos = curPos = 0;
                    continue;
                }
                
                //2.2
                if(curMinValue>nums[curPos])
                    //遇上了比历史最小值还小的值,更新 。如 2(作废) 3(作废) 1 ,历史最小值是2 
                    curMinValue = nums[curPos]; 
                else
                {//若是连历史最小值都小不过. 如 20(作废) 10(作废) 21 ,历史最小值是10;如果是  20(作废) 10(作废) 15 ,这个有2.1过滤,不会来到这里 
                    //不用操作 , 
                }
                
                prePos = curPos++;
            }
        }
        //2. 输出
        printf("%d\n",MULength);
        for(i=0;i<MULength-1;i++)
            printf("%d ",nums[i]);
        if(i==MULength-1)
            printf("%d",nums[i]);
        putchar('\n');
        return 0; 
    }
    
    unsigned int GetInputToInt()
    {
        char c;
        unsigned int sum =0;
        while((c=getchar())!=EOF&&c!=' '&&c!='\n')
            sum = sum*10+c-'0';
        return sum;
    }
    

    相关文章

      网友评论

          本文标题:(原创,步进分析,24ms)PAT乙级1045 快速排序

          本文链接:https://www.haomeiwen.com/subject/afirsqtx.html