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

题目

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

代码

#include 
#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(;inums[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[pre]的位置上,重置curPos 
                        curPos = prePos+1; 
                }   
            }
            //1.2要是比得过,接下来看情况看下是否更新一下最大值 
            else
            {
                //(正常情况) 看看是否遇上了比历史最大值还要大的当前值
                if(curMaxValuenums[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

你可能感兴趣的:((原创,步进分析,24ms)PAT乙级1045 快速排序)