PAT 快速排序

著名的快速排序算法里有一个经典的划分过程:我们通常采用某种方法取一个元素作为主元,通过交换,把比主元小的元素放到它的左边,比主元大的元素放到它的右边。 给定划分后的 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;
}

运行结果:
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值