PAT : 1045. 快速排序(25)

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

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

    新学期练手,单纯从题目提出的问题来说,解决难度一点都不大,两重循环轻松搞定,可是如果你提交一定会被大量的运行超时所打倒,毕竟200ms。这时候就需要优化了,目标也一定是减少循环次数,可是如何减少呢?当初我的思路就是:如果待判断的这个数的左边有一个数比它小,并且还是主元的话,那这个数一定的左边一定没有一个数比他大,这个数的右边也是同理。因此想到了分别遍历两次,一次从左边,一次从右边,每个数都有有两个标识,一个是左边没一个比它大的,叫lflag,一个是右边没一个数比它小的,叫rflag,第一个数的lflag必定是1,最后一个数的rflag也一定是1,判断方法就是先从左边遍历各个数,从各个数的左边一个一个找,如果有比它小且lflag为1的,也将这个数的lfalg置为1,右遍历同理,写完觉得应该没什么问题了,可这样写完拿去提交的时候还是有一个超时,一下没想到辙又去看别人是怎么做的了。てへぺろ(•ω<)


    终于找到一个,就是这个,看完时候立刻化身祥林嫂,“我真傻,真的”,完全不用我这么麻烦,同样的一次左遍历,一次右遍历,但是每个数的表示只有一个标识,是不是主元,叫flag吧,初始假设每个数都是主元,即flag都为1。左遍历先确定一个最大值,初始值就是第一个数,然后不断往后找,如果一个数比最大值大,最大值就更新,而且可以确定这个数比它的左边的任意一个数都大,毕竟已经大于之前的最大值了;如果小,洗洗睡吧,你肯定不是主元,你的左边已经有比你大的了。左遍历结束就排除掉很多数了,再从最右边开始遍历,这回是找最小值,一个道理,就不细说了,这样一来,剩下的flag仍然为1的就是主元了。完成


    总结一下,我主要输在初始的思路上,我的思路就是正面刚,先假设全都不是,然后一个一个找是的,这个做法呢,是逆向思维,先假设都是,再去一个个排除,在这个问题上一定是后者好,毕竟判断主元这个东西是一票否决制的,判断它是主元很麻烦,必须一个一个去比较,但如果判断它不是很简单,找一个左边比它大的数或者右边比它小的数,找到这个数就能否定它。逆向思维,在概率论就吃过亏的...我真傻,真的。

    #include <stdio.h>
    #include <stdlib.h>
    
    int cmp (const void *a, const void *b)
    {
    	return *(int *)a - *(int *)b;
    }
    
    int main()
    {
    	int i, N;
    	scanf("%d", &N);
    
    	int *numl = (int *) malloc (sizeof(int) * N);
    	int *zhuyuan = (int *) malloc (sizeof(int) * N);
    	int *res = (int *) malloc (sizeof(int) * N);
    
    	for(i = 0; i < N; i++)
    	{
    		scanf("%d", &numl[i]);
    		zhuyuan[i] = 1;
    	}
    	
    	int leftMax = numl[0];
    	int rightMin = numl[N-1];
    
    	for(i = 1; i < N; i++)
    	{	
    		if(numl[i] > leftMax) 
    		{
    			leftMax = numl[i];
    		}
    		else
    		{
    			zhuyuan[i] = 0;
    		}
    	}
    
    	for(i = N-2; i >= 0; i--)
    	{	
    		if(numl[i] < rightMin) 
    		{
    			rightMin = numl[i];
    		}
    		else
    		{
    			zhuyuan[i] = 0;
    		}
    	}
    
    	int count = 0;
    	for (i = 0; i < N; i++)
    	{
    		if(zhuyuan[i] == 1)
    		{
    			res[count++] = numl[i];
    		}
    	}
    
    	qsort(res, count, sizeof(int), cmp);
    
    	printf("%d\n", count);
    	for(i = 0; i < count; i++)
    	{
    		if(i > 0)
    		{
    			printf(" ");
    		}
    		printf("%d", res[i]);
    	}
    	printf("\n");
    
    	return 0;
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值