C语言练习2之齐齐排序

        说明:本题取自于牛客竞赛的一道算法题,地址为齐齐排序 (nowcoder.com)

一.题目条件 

1.题目概述: 

2.输入和输出描述

3.备注 

 

二.题目分析

        题目涉及到较多的变量,接下来将一个个分析。

        1.n表示数字的个数,需要在第一行输入,用于标识第二行输入的整数ai的个数,所以这个数字的输入就可以知道有多少大小的数组需要创建。当然,根据题目所说1<=n<=200000,我们可以直接写上intArr[200000],也可以使用(int*)malloc(sizeof(int)*n)来创建。除此之外,这个n可以用于在后面的for循环来初始化数组

        2.m表示操作的个数,和n同理,用在for循环来不断scanf输入数据。当然也可以用于定义数组的大小,那么这里为什么需要使用两个数组呢?请看下文的解释。 

        3.ai就是1所说的是使用for来对intArr的每个数据的初始化。 

        4.ti和xi可以直接for...{scanf("%d %d",...);}进行输入。

        看到这里的4之后,貌似也不需要使用数组,传入第二个参数ti就可以代表降序还是升序,到时候调用对应的函数就行了,但是其实并不然,由于在备注里说明了1<=m<=200000,那就说明可以进行200000次操作,此时我们难道需要进行200000次排序吗

 三.算法分析

        以牛客官网上的示例2中:

        第一行的4 2表示后面创建数组大小为4,进行2步操作。第二行的1 2 4 3说明输入的数组中每个元素是1 2 4 3。第三行的2 3说明进行逆序,从1~3,第四行的1 2说明进行顺序,从1~2。

        第一步操作:1 2 4 3中,1~3的元素是1 2 4,进行降序,结果是4 2 1 3

        第二步操作:4 2 1 3中。1~2的元素是4 2,进行顺序,结果是2 4 1 3

可见第二步操作覆盖了第一步部分的前两位。

当然这个可能看不出什么,但是假设我们再来一次操作,第三步操作假设是1 4

        第三步操作:2 4 1 3中。1~4元素是2 4 1 3,进行顺序,结果是1 2 3 4。

        这下是不是就能发现,好像第三步操作了之后,全部就变顺序了,和直接少去第一,二步操作直接进行第三步操作一样的。所以我们可以总结出: 只要后面的操作包括了前面的几步的范围,那前面那几步就不需要进行操作。

        那么我们是从前往后看吗,但是上面我们总结的并不是这样,因为我们不知道后面哪个最大,没办法说明到底要不要进行操作。所以我们从最后的操作往前看就可以找到后面较大者,然后标记前面的比这个大值范围小的操作,这些操作就没必要进行操作了,这样就可以省下大部分的代码。

        接下来就解释为什么需要再创建两个数组,上面我们说过了,由于我们要从后向前看,所以如果我们不存储每次输入的内容,就没办法对之前的操作是否要进行排序标记了。由于每次输入的类型都一样,所以可以使用数组进行存储。 由于ti标识升降序排列xi标识范围,我们也可以把这下ti和xi存储起来,比较xi的大小,修改ti的内容,由于1已经标识升序,2标识降序,那我们可以多加入一个0来表示不进行操作即可

        对于排序,我们可以使用快速排序,也可以直接使用C语言内的qsort函数,这个函数的实现方法就是快速排序。

        qsort的函数使用方法:

void qsort(void *base, size_t num, size_t size, int (*compar)(const void *, const void *)); 

        base标识这个待排序的数组,num标识输入的元素个数,size标识每个元素的大小,后面的compar由于再()小括号内与*先结合,所以它是一个指针,这个指针指向的数据类型是int (const void *, const void *)这说明指向的元素是一个函数,这个函数的返回类型是int,形参是两个const void* 类的变量,总之,compar是一个函数指针,所以我们还需要定义一个函数。

        规范:函数的返回值>0就代表两个值要互换。

所以我们可以使用

int cmp1(const void* e1,const void* e2){//顺序
    return (*(int*)e1-*(int*)e2);
}
int cmp2(const void* e1,const void* e2){//降序
    return (*(int*)e2-*(int*)e1);
}

        接下来就简单解释解释两个代码,由于传入两个void*无类型指针,这个指针不能用来存取等操作,因为编译器并不知道这个形参的大小,所以我们可以把它变为(int*)强制类型转换为int指针,然后*解引用获得值,第一个e1的值-e2个值,大于0时就是e1>e2,此时互换,就把较大值换到后面了,所以是升序,第二个就是降序。

四.代码实现

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int cmp1(const void* e1,const void* e2){//顺序
    return (*(int*)e1-*(int*)e2);
}
int cmp2(const void* e1,const void* e2){//降序
    return (*(int*)e2-*(int*)e1);
}
int main() {
    int n, m, t, x;
    scanf("%d %d", &n, &m);
    int* arr = (int*)malloc(sizeof(int) * n);//n个数据
    int* Oarr1 = (int*)malloc(sizeof(int) * m);//m个操作的降/升序
    int* Oarr2 = (int*)malloc(sizeof(int) * m);//m个操作的范围
    for (int i = 0; i < n; i++) {
        //输入n个数
        scanf("%d", &arr[i]);
    }
    for (int i = 0; i < m; i++)//存储每个操作
        scanf("%d %d", &Oarr1[i], &Oarr2[i]);
    int temp = Oarr2[m - 1];//存储从后向前查找的较大值
    for (int i = m - 2; i >= 0; i--) {
        if (temp < Oarr2[i]) 
            temp = Oarr2[i];
        else 
            Oarr1[i] = 0;//设置为0表示该次不需要排序
    }
    for (int i = 0; i < m; i++)
        if (Oarr1[i] == 1)
            qsort(arr,Oarr2[i],sizeof(int),cmp1);
        else if(Oarr1[i] == 2)
            qsort(arr,Oarr2[i],sizeof(int),cmp2);
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    free(arr);
    free(Oarr1);
    free(Oarr2);
    return 0;
}

 五.思想总结

  本题的思想主要是

        1.如何处理多个排序,即可以从后往前判断是否有排序范围覆盖

         2.qsort的使用(也可以是自己写的排序算法)方法,首先是四个参数从左到右是数组,个数,大小,函数指针。其中函数指针返回大于0就互换位置。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值