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

题目

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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容