其他分享
首页 > 其他分享> > [学习报告]《LeetCode零基础指南》(第四讲) 指针

[学习报告]《LeetCode零基础指南》(第四讲) 指针

作者:互联网

学习内容:https://blog.csdn.net/WhereIsHeroFrom/article/details/120875926

一、今日知识点总结

实现简单,时间效率低:冒泡、选择、插入排序
实现相对复杂,效率较高的:归并、快排、希尔排序 等
非比较排序:基数排序、计数排序、桶排序

C语言排序API:qsort

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*));
参数说明
base要排序的数组的第一个元素的指针
nitems数组的个数
size数组元素的大小(数组数据类型的字节大小)
compar用来比较两个元素大小的函数,即函数指针(比较算法的回调函数)

qsort的使用范式

//排序回调函数:用于表示比较的两个数谁在前谁在后的规则
int cmp(const void *a, const void *b){
    return (*(int *)a) - (*(int *)b);//升序排列
    //or 
    return (*(int *)b) - (*(int *)a);//降序排列
}

//...

int a[7] = {0,2,4,1,2,6,1};
qsort(a,7,sizeof(int),cmp);

//...

奇偶排序

int qua(int x){
    return x & 1;//(1) 当奇数,返回1,偶数返回0 
}

int cmp(const void *a, const void *b){
      return qua(*(int *)a) - qua(*(int *)b); //(2) 实现了偶数排前面,奇数排后面
}

为何是偶数排前面,奇数排后面?
qua(x)判断结果是,奇数返回1,偶数返回0;(2)这行表达式实现的是升序排列
代表偶数的0小于代表奇数的1,所以偶数排前面,奇数排后面

关键思想是,根据实际题目需要,根据数据的特点,对排序的元素进行一个转换计算,进而改变排序的规则

二、今日解题

战绩:

image-20220318192549995

912. 排序数组

int cmp(const void *p1,const void*p2){
    return *(int *)p1 - *(int *)p2; //(1) 比较函数
}

int* sortArray(int* nums, int numsSize, int* returnSize){

    qsort(nums,numsSize,sizeof(int),cmp);//(2)调用排序API
  
    *returnSize = numsSize;//(3)告诉调用者排序后的元素个数
    return nums;//(4)返回排序后的数组首地址
}

169. 多数元素

int cmp(const void *p1,const void *p2){
    return *(int *)p1 - *(int *)p2; //比较函数,升序排列
}

int majorityElement(int* nums, int numsSize){

    qsort(nums,numsSize,sizeof(int),cmp);//调用排序函数,得到升序排列后的nums

    return nums[numsSize/2];//题意全部数组都满足存在多数元素(个数超过一半,也就是>n/2),无论这个多数元素是大还是小,[n/2]肯定是他

}

217. 存在重复元素

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;


}

164. 最大间距

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

int maximumGap(int* nums, int numsSize){

    if(numsSize<2){
        return 0;
    }

    qsort(nums,numsSize,sizeof(int),cmp);//升序排序

    int max = 0;//最大值,若后续最大值比max大,max就等于新的最大值
    for(int i = 1;i<numsSize;i++){//后一个减前一个,所有i从1开始
        if(nums[i]-nums[i-1]>max){//相邻元素相减,因为升序排列,故[i]-[i-1]
            max = nums[i]-nums[i-1];//替换最大值
        }
    }
    return max;

}

905. 按奇偶排序数组***

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */

int odd(int num){
    return num&1;//根据奇数二进制最后一位必定为1,偶数为0;位于1后,奇数返回1,偶数返回0;
}

int cmp(const void *a,const void *b){
    return odd(*(int *)a) - odd(*(int *)b);//升序排列,所以偶数排在前面,奇数排在后面
}



int* sortArrayByParity(int* nums, int numsSize, int* returnSize){

    int *ans = (int *)malloc(sizeof(int)*numsSize);//申请一段长度为numSize个int的内存空间,首地址赋值给ans
    for(int i = 0;i<numsSize;i++){
        ans[i] = nums[i];//复制数组元素
    }

    *returnSize = numsSize;//告诉调用者返回的新数组的元素个数
    qsort(ans,numsSize,sizeof(int),cmp);//执行排序
    return ans;

}

539. 最小时间差

int cmp(const void *a,const void *b){
    return *(int *)a - *(int *)b;//升序排列 -- 把时间按照最小到最大排序
  	//因为相邻的时间点的时间差肯定最小(贪心)
}

int findMinDifference(char ** timePoints, int timePointsSize){

    int *times = (int *)malloc(sizeof(int)*timePointsSize);//申请了一个长度为sizeof(int)*timePointsSize的内存空间,首地址赋值给times
    int i,h,m = 0,min = 1440;//24小时的最大时间差为1440分钟,从0点到24点(一天有1440分钟)
    for(i = 0;i<timePointsSize;i++){
        sscanf(timePoints[i],"%d:%d",&h,&m);//通过sscanf把反格式化时间字符串得到小时和分钟
        times[i] = h*60 + m;//计算分钟数,以分钟表示
    }

    qsort(times,timePointsSize,sizeof(int),cmp);//排序

    for(i = 1;i<timePointsSize;i++){//思路和最大间距类似,只是换成了「比小」
        if(times[i] - times[i-1]<min){
            min = times[i] - times[i-1];
        }
    }

    if((times[0] + 1440 - times[timePointsSize-1])<min){//要特别留意的地方:根据上面的计算方式,会漏掉首位和末位这两个时间的比较,最小值和最大值也是相邻的,它们之间是存在时间差最小的可能的情况 ==> 最小时间是排在最大时间之后,例如最大时间是11:55,最小时间是00:01,时间差为6分钟 
        min = times[0] + 1440 - times[timePointsSize-1];//如何理解加1440,从计时器的角度,可以设定每个时间都是从00:00开始计时,而00:01可能是已经走完了24小时并走了1分钟,故加 1440即为00:01的计时
    }

    return min;


}

976. 三角形的最大周长

// 贪心
// a+b>c
// 周长最大 只要c最大,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);

    for(int i=numsSize-1;i>=2;i--){//从最大一条线开始检查
        if(nums[i-2]+nums[i-1]>nums[i]){//若该线的前面两条线满足三角形 a+b>c,则该三角形周长一定最大
            return nums[i] + nums[i-1] + nums[i-2];
        }
    }
    return 0;


}

881. 救生艇

以下两个解法就只是在循环结束条件的选用不同

//一次最多两人
//两人重量小于limit

//如果重量大于limit,只能上一个,就让最重的人先上,轻的留下,看是否有可以搭配的
//试错效率最高的就是先对人的重量进行排序,让最轻的和最重的人匹配,试错次数最低。

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

int numRescueBoats(int* people, int peopleSize, int limit){

    qsort(people,peopleSize,sizeof(int),cmp);

    
    int l = 0,r = peopleSize-1,cnt = 0;
    while(peopleSize>0){
        
        if(people[l] + people[r] <= limit){
            l++;
            r--;
            peopleSize -=2;
        }else{
            r--;
            peopleSize--;
        }
        cnt++;
    }

    return cnt;

}

//最好就是一次两人,但如何最高效率呢,就是刚好能匹配limit,就是两人重量之和最接近limit
//要能凑满,就相当于凑满减,加最少的钱就能达到优惠线,也就是最大加最小

int cmp(int *a,int *b){
    return *a - *b;//升序排列
}

int numRescueBoats(int* people, int peopleSize, int limit){

    qsort(people,peopleSize,sizeof(int),cmp);
    //次数people按照升序排列了

    //先让最重的和最轻组合比较,让重的先走,能稍上轻的就稍上轻的,这样船只的承重能力最不浪费
    //且剩余的能组合成功的概率会更大
    //双指针?
    int l=0,r=peopleSize-1;
    int cnt = 0;
    while(l<=r){
        if((people[r] + people[l])<=limit){
            l++;r--;
        }else{
            r--;
        }
        cnt++;
    }
    
    return cnt;
}

三、今日收获

四、今日疑问

五、其他参考

标签:指南,return,nums,int,void,const,cmp,LeetCode,指针
来源: https://blog.csdn.net/campchan/article/details/123582674