著名的快速排序算法里有一个经典的划分过程:我们通常采用某种方法取一个元素作为主元,通过交换,把比主元小的元素放到它的左边,比主元大的元素放到它的右边。 给定划分后的 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
通过题目的要求,我们可以知道
主元元素:在数组这个元素的左边都是小于他的数,右边都是大于他的数
所以我们的思路是,新定义一个临时数组,用于存放主元元素,最后只要输出这个数组的元素个数,同时将这个数组按照升序进行排序,即可实现题目的要求。思路的确正确,但是最后提交的时候,我们可以发现有几个测试点没有通过,提示运行超时。
错误的代码如下:
/*
基本思路:
1、从左到右遍历数组
2、每得到一个数字,那么就判断它的左边是否含有比他大的数字,右边是否含有比他小的数字,
如果含有,那么说明这个数字不是一个主元。这时候,为了节省程序运行的时间,我们定义一个
变量,表示左边是否含有比他大的数字,如果含有,那么不需要判断右边是否含有比他小的数字,
直接跳到下一个数字,否则,那么就判断右边是否含有比它小的数字,如果这时候还是没有,就说明是
主元
*/
#include<stdio.h>
#define MAX_SIZE 100010
int count = 0;//定义一个全局变量,表示主元的个数
long target[MAX_SIZE];//定义一个数组,用于存放主元数字
void isCenterNumber(int low,int index,int high,long arr[],long val){
//val表示下标为index对应的值
int flag = 0;//表示左边是否含有比它大的数字,右边是否含有比他小的数字,1表示有,0表示无
for(; low < index; low++){
//判断左边是否含有比他大的数字
if(arr[low] > val){
flag = 1;
break;
}
}
if(!flag){
//如果左边没有比他大的数字,那么就进行比较它的右边是否含有比他小的数字
index++;
for(; index < high; index++){
if(arr[index] < val){
flag = 1;
break;
}
}
if(!flag)
//如果当前的元素是一个主元元素,那么就将其压入到target这个数组中
target[count++] = val;
}
}
/*
void shellSort(){
//利用希尔排序,对target数组进行排序,使其成升序排序
int i,j,d,tmp;
d = count / 2;
//当间隔不等于0时候,那么继续循环
label:
for(i = d; i < count; i++){
tmp = target[i];
for(j = i - d; j >= 0; j-=d){
if(target[j] <= tmp)
break;
target[j + d] = target[j];
}
target[j + d] = tmp;
}
d = d / 2;
if(d != 0)
goto label;
}
*/
/*
void bubbleSort(){
int i,j,tmp;
int flag;
for(i = 0; i < count; i++){
flag = 0;
for(j = 0; j < count - 1 - i; j++){
if(target[j + 1] < target[j]){
tmp = target[j];
target[j] = target[j + 1];
target[j + 1] = tmp;
flag = 1;
}
}
if(!flag)
break;
}
}
*/
int dividArray(int low,int high){
int index = (low + high) / 2;
int pivot = target[index];
int tmp;
while(1){
while(low < high && target[low] < pivot)
low++;
while(low < high && target[high] > pivot)
high--;
if(low >= high)
break;
else{
tmp = target[low];
target[low] = target[high];
target[high] = tmp;
}
}
return low;
}
void quickSort(int low,int high){
if(low >= high)
return;
int divid = dividArray(low,high - 1);
quickSort(low,divid - 1);
quickSort(divid + 1,high);
}
int main(){
int n,i;
long arr[MAX_SIZE];
scanf("%d",&n);
for(i = 0; i < n; i++){
scanf("%ld",&arr[i]);
}
for(i = 0; i < n; i++){
isCenterNumber(0,i,n,arr,arr[i]);//判断当前下标对应的值是否为一个主元元素
}
printf("%d\n",count);
// shellSort();
// bubbleSort();
quickSort(0,count - 1);
for(i = 0; i < count; i++){
if(i != 0)
printf(" ");
printf("%ld",target[i]);
}
printf("\n");
return 0;
}
原本以为是对于target数组进行排序的时候造成程序超时的,但是最后即使是使用快速排序,希尔排序,依旧是这种错误。通过看他人的博客,我发现他们的代码和我原来的代码,发现他们是定义了两个数组arr1,arr2,其中arr1是用来存放我们输入的元素,而arr2则是arr1升序排序之后的
。这时候判断主元元素的时候,只需要判断 arr1[i]==arr2[i] && arr1[i] =\= max就可以判断当前的元素是否是一个主元元素了
。
但是,问题来了,为什么需要判断arr1[i] == arr2[i] && arr1[i] == max?max又是指什么咧?
好的,我的理解是:
①max表示当前数组arr1中下标 i 前面的数(包含i这个数)中的最大值
。所以如果arr1[i] == max就说明当前下标 i 对应的是他前面数字的最大值。
②由于arr2[i]时已经排序了的,并且是按照升序进行排序的,那么当arr1[i] == arr2[i]的时候,那么有可能
说明arr1[1]比它前面的数都要大,一定
会比它后面的数小(之所以说一定比它后面的数小,是因为arr2已经是升序排序了的)。所以,对于判断是否为主元元素,有一定的误差。
由此我们可以得出结论:
arr1[i] == arr2[i],是为了说明当前下标为i的元素小于它右边的所有数字 arr1[i] =\= max,是为了说明当前下标为i 的元素大于它左边的所有数字(因为他就是最大值嘛)
如果这里的讲解还不是很明白的话,可以参考一下下面的例子进行分析哟:
正确的代码:
#include<stdio.h>
#define MAX_SIZE 100010
int count = 0;//定义一个全局变量,表示主元的个数
long target[MAX_SIZE];//定义一个数组,用于存放主元数字
int dividArray(int low,int high){
int index = (low + high) / 2;
int pivot = target[index];
int tmp;
while(1){
while(low < high && target[low] < pivot)
low++;
while(low < high && target[high] > pivot)
high--;
if(low >= high)
break;
else{
tmp = target[low];
target[low] = target[high];
target[high] = tmp;
}
}
return low;
}
void quickSort(int low,int high){
if(low >= high)
return;
int divid = dividArray(low,high - 1);
quickSort(low,divid - 1);
quickSort(divid + 1,high);
}
int main(){
int n,i;
long arr[MAX_SIZE],arr2[MAX_SIZE],max;
scanf("%d",&n);
for(i = 0; i < n; i++){
scanf("%ld",&arr[i]);
arr2[i] = arr[i];
}
quickSort(arr,0,n);//对arr进行升序排序
max = -9999999;
for(i = 0; i < n; i++){
/*
由于arr是一个升序的数组,当arr2中对应下标的值等于arr对应下标的值,
那么就说明当前的数字有可能大于前面的数字,一定小于后面的数字,所以
有可能是一个主元数字,但是只有这一步并不能够说明,还需证明当前的数
字是它前面所有数字的最大值才可以(再次回到主元元素的定义中)
*/
if(arr2[i] > max)
max = arr2[i];
if(arr[i] == arr2[i] && arr2[i] == max){
//如果当前元素是它前面数字的最大值,并且下于它后面的数字,那么当前的元素就是主元元素
target[count++] = arr2[i];
}
}
printf("%d\n",count);
for(i = 0; i < count; i++){
if(i != 0)
printf(" ");
printf("%ld",target[i]);
}
printf("\n");
return 0;
}
运行结果: