[学习报告]《LeetCode零基础指南》(第五讲)排序(qsort)

野生的小小风256 2022-04-20 21:13:20

目录

前言

qsort()

解题报告

912. 排序数组

169. 多数元素

217. 存在重复元素

164. 最大间距

905. 按奇偶排序数组

539. 最小时间差

976. 三角形的最大周长

881. 救生艇


前言

这是九日集训打卡的第五天,今天的内容是通过 qsort()函数实现排序功能。

qsort()

qsort()函数的使用格式为:

qsort(数组首元素地址,数组容量,sizeof(数据类型),比较函数)

其中的比较函数是一个需要自己来进行定义的函数,这个函数的传入参数为两个指向数组元素(假设为a和b)的指针,当这个数组返回了小于 值则将元素 a 排在元素 的前面,如果返回值大于 0 则将 b 排在前面,等于 时排序不确定。(但是当两个元素的数值相等的时候其实不不需要对这两个元素进行排序,所以令比较函数在元素相等时返回 0 是最佳的)下面以一个简单的递增的比较函数进行举例:

int cmp(const void*a ,const void*b){                        //定义一个递增比较函数
    return *(int*)a-*(int*)b;                               //将指针强转为整型
}

这个比较函数的传入参数是两个指针,但是,在使用之前并不知道需要传入的参数是什么类型的指针,所以可以在定义传入参数时先将其定义为空(类型)指针 void* ,然后在后续直接为其强转类型即可。空类型指针与空指针是不同的,空指针在定义指针后为其赋值 NULL 使得这个指针指向一个非常小的地址(这个地址是不能用来操作的),方便后续为其赋值。而空类型指针与普通的指针其实是类似的,只是没有为其选定数据类型。

解题报告

912. 排序数组

给你一个整数数组 nums,请你将该数组升序排列。

  • 1 <= nums.length <= 5 * 10e4
  • -5 * 10e4 <= nums[i] <= 5 * 1e04
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int cmp(const void*a ,const void*b){                        //定义一个递增比较函数
    return *(int*)a-*(int*)b;                               //将指针强转为整型
}
int* sortArray(int* nums, int numsSize, int* returnSize){
    qsort(nums,numsSize,sizeof(int),cmp);                   //使用qsort进行排序
    *returnSize = numsSize;                                 //返回数组容量
    return nums;                                            //返回数组
}

首先,数组的元素的范围是 [-50000,50000] ,所以任意的两个数组的差都不会溢出,比较函数就可以使用比较简洁的写法了。在定义好了比较函数之后,直接使用排序函数进行排序即可,记得在最后返回数组之前要先返回数组容量。

169. 多数元素

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

int cmp(const void*a,const void*b){
    return *(int*)a-*(int*)b;
}
int majorityElement(int* nums, int numsSize){
    qsort(nums,numsSize,sizeof(int),cmp);
    return nums[numsSize/2];
}

定义一个递增的比较函数。多数元素总是存在的话,那么只需要对原函数进行排序(递增递减都可以,此处以递增实现)后,那么超过一半的元素都是多数元素且集中在数组的一侧,那么这时候只要取最中间的元素(或者其前一个元素)都将会是多数元素。

217. 存在重复元素

给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。

int cmp(const void*a,const void*b){
    return *(int*)a-*(int*)b;
}
bool containsDuplicate(int* nums, int numsSize){
    qsort(nums,numsSize,sizeof(int),cmp);
    for(int i=1;i<numsSize;i++){
        if(nums[i]==nums[i-1]){             //注意不可以越界
            return true;
        }
    }
    return false;
}

对数组中的元素全都进行排序,在排序之后相同的数字就必然相邻了,然后直接比较相邻的数字,当有相同时直接返回 true 即可。需要注意数组的下标一定不可以越界。

164. 最大间距

给定一个无序的数组 nums,返回 数组在排序之后,相邻元素之间最大的差值 。如果数组元素个数小于 2,则返回 0 。

int cmp(const void*a,const void*b){
    return *(int*)a-*(int*)b;
}
int maximumGap(int* nums, int numsSize){
    if(numsSize<2){
        return 0;
    }
    int max=0;
    qsort(nums,numsSize,sizeof(int),cmp);
    for(int i=1;i<numsSize;i++){
        if((nums[i]-nums[i-1])>max){
            max = nums[i]-nums[i-1];
        }
    }
    return max;
}

直接排序之后使用一个循环不断比较相邻元素差,然后储存在 max 中最后输出即可。

905. 按奇偶排序数组

给你一个整数数组 nums,将 nums 中的的所有偶数元素移动到数组的前面,后跟所有奇数元素。

返回满足此条件的 任一数组 作为答案。

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int Qua(int x){                                                  //与比较函数配套使用,将奇数变成1,偶数变0
    return x&1;
}
int cmp(const void*a,const void*b){
    return Qua(*(int*)a)-Qua(*(int*)b);                          //奇数在处理后将会大于偶数
}
int* sortArrayByParity(int* nums, int numsSize, int* returnSize){
    int *ret=(int *)malloc(sizeof(int)*numsSize);                //返回的是新数组因此需要申请内存
    for(int i=0;i<numsSize;i++){
        ret[i]=nums[i];                                          //将原来的数据全都填充到新数组
    }
    qsort(ret,numsSize,sizeof(int),cmp);                         //排序
    *returnSize = numsSize;                                      //返回容量
    return ret;                                                //返回数组
}

此题与先前几题会略有区别,需要返回的是新数组,因此需要定义一个新的数组并且为其申请内存,然后将原来的数组内的数据填入新的数组中之后再进行排序,直接返回即可。下面对比较函数稍微进行解释:首先为了比较函数书写的简洁性,定义了一个函数 Qua()。其功能是将奇数变为 1 ,将偶数变为 0 。随后是比较函数,本题要求的是使得偶数全都排在奇数的前面,因此当 为偶数, 为奇数时,需要比较函数的返回值小于 0 。回到函数中可以看到,偶数在 Qua()处理后会小于奇数,刚好符合了需要的功能。

539. 最小时间差

给定一个 24 小时制(小时:分钟 "HH:MM")的时间列表,找出列表中任意两个时间的最小时间差并以分钟数表示。

int cmp(const void *a, const void *b) {
    return *(int *)a - *(int *)b;
}

int min(int a, int b) {
    return a < b ? a : b;                                       //定义一个简单的取最小值函数,也可以不定义
}

int findMinDifference(char ** timePoints, int timePointsSize){
    int *ret=(int*)malloc(sizeof(int)*timePointsSize);          //定义了一个整型的数组,后续用于储存分钟数据
    int i,ans=1440;                                             //定义最终返回值,其最大为60*24=1440
    int a,b;                                                    //a和b分别为小时数和分钟数
    for(i=0;i<timePointsSize;i++){
        sscanf(timePoints[i],"%d:%d",&a,&b);                    //利用sscanf()读取字符对应位置的值赋值给a和b
        ret[i]=a*60+b;                                          //将时间转化为分钟后存储在数组中
    }
    qsort(ret,timePointsSize,sizeof(int),cmp);                  //对ret数组进行排序
    for(i=1;i<timePointsSize;i++){
        ans=min(ans,ret[i]-ret[i-1]);                           //再用一个循环把所有的相邻差比较出最小的差
    }
    ans=min(ans,ret[0]-ret[timePointsSize-1]+1440);             //考虑时间循环
    return ans;
}

一道看似比较复杂的题目,具体的代码部分的功能全都在代码注释中较为详细的写出,指的一提的是,要充分考虑到时间循环的情况,这一点在时间相关问题上尤为重要。而且时常要注意好数组下标越界的问题。此题还可以积累到 sscanf()函数的用法。

976. 三角形的最大周长

给定由一些正数(代表长度)组成的数组 nums ,返回 由其中三个长度组成的、面积不为零的三角形的最大周长 。如果不能形成任何面积不为零的三角形,返回 0

int max(int a,int b){                                   //与上题使用了类似的写法可以使得代码简洁
    return a>b?a:b;
}
int cmp(const void*a,const void*b){
    return *(int*)a-*(int*)b;
}
int largestPerimeter(int* nums, int numsSize){
    qsort(nums,numsSize,sizeof(int),cmp);               //这里的排序很有必要,详见题解
    int ans=0;
    for(int i=2;i<numsSize;i++){
        if((nums[i-2]+nums[i-1])>nums[i]){              //满足两边之和大于第三边面积才不为0
            ans=max(ans,nums[i]+nums[i-1]+nums[i-2]);   //通过循环与比较使得ans为最大值
        }
    }
    return ans;
}

三角形的两边只和大于第三边,这是优先考虑的一点。那么排序就显得非常重要了,在进行排序后,前两个元素就一定小于等于下个元素了,而当一个三角形的短的两边只和大于了第三边时,就一定满足每两边只和都大于第三边,这样就可以只通过一次判断就十分容易的判断出了能否组成三角形。

881. 救生艇

给定数组 people 。people[i]表示第 i 个人的体重 ,船的数量不限,每艘船可以承载的最大重量为 limit。

每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。

返回 承载所有人所需的最小船数 。

int cmp(const void *a, const void *b) {
    return *(int *)a - *(int *)b;
}

int numRescueBoats(int* people, int peopleSize, int limit){
    int i;
    int l = 0, r = peopleSize-1;                     //这里的l和r实际上指的是最轻的人和最重的人
    int ans = 0;
    qsort(people, peopleSize, sizeof(int), cmp);     //按从小到大排序
    while(l <= r) {                                  //人全被送走之前
        if(l == r) {
            ++ans; break;                            // 当l=r时也就只剩下一个人了,坐一艘然后直接跳出循环即可
        } else if(people[l] + people[r] > limit) {   // 当最重和最轻都超过限制时,最重就只能自己坐一艘了
            ++ans, r--;                              //所以这时候把最重的用一艘船送走最重的变成下一个
        } else                                       //这种情况指的是最重和最轻两个人可以坐同一艘的情况
            ++ans, ++l, --r;                         //所以这个时候只需要一艘可以送走最轻和最重两个人
    }
    return ans;
}

详细内容都已经在代码注释中写出。

...全文
307 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

64,194

社区成员

发帖
与我相关
我的任务
社区描述
学习「 算法 」的捷径就是 「 题海战略 」,社区由「 夜深人静写算法 」作者创建,三年ACM经验,校集训队队长,亚洲区域赛金牌,世界总决赛选手。社区提供系统的训练,答疑解惑,面试经验,大厂内推等机会
社区管理员
  • 英雄哪里出来
  • 芝麻粒儿
  • Amy卜bo皮
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

QQ群:480072171

英雄算法交流 8 群

 

试试用AI创作助手写篇文章吧