[学习报告]《LeetCode零基础指南》(第六讲) 贪心
学习内容:https://blog.csdn.net/WhereIsHeroFrom/article/details/121586834
一、今日知识点总结
贪心,就是做当前看来最好的选择。不从整体最优考虑,得到的是某种意义上的局部最优解 ---- 最容易最快能达成目的的思路
如,在一堆正整数里面找两个数,乘积最大。有两个思路,① 就是让每个数和其他数都相乘,比较乘积大小。②可以先找出最大和次大数,则他们的乘积就是最大。
二、今日解题
战绩:
1913. 两个数对之间的最大乘积差
乘积差最大值,也就是最大乘积和最小乘积,使用贪心的思路,最大乘积就是最大与次大的乘积,最小乘积则为最小和次小的乘积
int cmp(const void *a,const void *b){
return *(int *)a - *(int *)b;
}
int maxProductDifference(int* nums, int numsSize){
qsort(nums,numsSize,sizeof(int),cmp);
return nums[numsSize-1]*nums[numsSize-2] - nums[0]*nums[1];
}
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]){
return nums[i] + nums[i-1] + nums[i-2];
}
}
return 0;
}
561. 数组拆分 I
两个数为一组,每组的最小值之和最大 ==> 每组的最小值尽可能取最大,那就是次大。
贪心算法:升序排列,从0开始顺序分对,即[0,1] [2,3] …,这样的分对,每对的最小值之和可以取得最大。
最大值必定不能去,那就让最大值和次大值组对,min此时是可以取得最大值。依次递推,取得剩下的每个次大值
//2n个数分成n对,从每对中取最小的一个,加起来最大
//因为是取两个数的最小数,又需要这个最小数尽可能大
//则每个最大数的次小数是最大的min了
//顺序排序,取0、2、4 。。。n-2
int cmp(const void * a,const void *b){
return *(int *)a - *(int *)b;
}
int arrayPairSum(int* nums, int numsSize){
qsort(nums,numsSize,sizeof(int),cmp);
int sum = 0;
for(int i=0;i<=numsSize-2;i += 2){
sum += nums[i];
}
return sum;
}
//两个数为一组,每组的最小值之和最大 ==> 每组的最小值尽可能取最大,那就是次大。
//贪心算法:升序排列,从0开始顺序分对,即[0,1] [2,3] ...,这样的分对,每对的最小值之和可以取得最大。
//最大值必定不能去,那就让最大值和次大值组对,min此时是可以取得最大值。依次递推,取得剩下的每个次大值
int cmp(int *a,int *b){
return *a - *b;
}
int arrayPairSum(int* nums, int numsSize){
qsort(nums,numsSize,sizeof(int),cmp);
int sum = 0;
for(int i = 0;i<numsSize/2;i++){
sum += nums[i*2];
}
return sum;
}
881. 救生艇
救生艇问题:一次可以取两个数,且两数之和不大于limit,求最小次数。
这类问题可采用贪心算法,目的要尽可能让更多的人可以两个一次上,也可以转换为:尽可能让体重重的人不单独上,或者从经济效能角度,尽可能满载,就可以尽早载完。
上述思路,代码的呈现都是:先排序,让最重的和最轻的组合,若超重,那最重的人必定只能自己上。让指针在两头选择,拿一边上了,就往中间移一位。直至都上船了。
双指针问题,用while比较方便,判断逻辑是两个指针是否重叠了或数是否都取完了
//最好就是一次两人,但如何最高效率呢,就是刚好能匹配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;
}
//最好就是一次两人,但如何最高效率呢,就是刚好能匹配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(peopleSize){
if(l>=r){ //要判断剩最后一人的情况
peopleSize--;
cnt++;
break;
}
if(people[l]+people[r]<=limit){
l++;r--;
peopleSize-=2;
}else{
r--;
peopleSize--;
}
cnt++;
}
return cnt;
}
324. 摆动排序 II
摆动排序问题,就是下标的奇数位大于两边的偶数位。
从贪心的角度,对数组排序后,后半段的数均大于前半段的数,只需要把后半段的数放在奇数位,前半段的数放在偶数位。即可满足题目要求。
//分析:下标为奇数的,始终大于下标为偶数的
//对数组升序排列
//下标为偶数位,从左半段取值插入
//下标为奇数位,从右半段取值插入
//则偶数位均为前n/2,奇数位均为后n/2。后半段的数,必定都大于前半段
int cmp(const void *a,const void *b){
return *(int *)a - *(int *)b;
}
void wiggleSort(int* nums, int numsSize){
int i;
int r;
//先建一个数组操作比较方便
int *ret = (int *)malloc(sizeof(int)*numsSize);//申请一段内存空间给新数组,首地址赋值给*ret
for(i=0;i<numsSize;i++){
ret[i] = nums[i];//复制数组
}
qsort(ret,numsSize,sizeof(int),cmp);
i=1;
r = numsSize-1;
for(;i<numsSize;i+=2){//倒序取原数组元素(后半段的数)填入到新数组的奇数位
nums[i] = ret[r];
r--;
}
//取原数组元素插入偶数位,此时取得原数组的元素已经是比上一个循环取的都小了。
//所以上一个循环放置的数比这个循环放置的数都大
for(i=0;i<numsSize;i+=2){
nums[i] = ret[r];
r--;
}
}
455. 分发饼干
发饼干问题,就是尽可能让饼干去满足孩子。我取出一块最大的饼干,找到刚好能满足胃口的小孩,分给他。就让孩子按照胃口大小倒序过来,胃口最大的孩子,最大的饼干如果能满足,就给他,不能就下一个。若下一个能满足,那么手上这个饼干给他。再取一块次大的饼干,继续与下一位胃口次大的孩子比较。重复上诉步骤。直到要么饼干没了,要么孩子都比较完了。
双指针问题,还是用while方便
int cmp(const void *a,const void *b){
return *(int *)a - *(int *)b;
}
int findContentChildren(int* g, int gSize, int* s, int sSize){
int i,j,sum = 0;
qsort(g,gSize,sizeof(int),cmp);//对孩子进行升序排序
qsort(s,sSize,sizeof(int),cmp);//对饼干进行升序排序
i = gSize-1;j=sSize-1;//饼干、孩子都从最大的开始取
//双指针问题,还是用while方便
while(j>=0&&i>=0){//要么饼干分完,要么孩子比较完
if(s[j]>=g[i]){//当前饼干能满足孩子胃口吗?
sum++;//能满足,能满足的计数器加一,
i--;//孩子下一个
j--;//饼干分掉,取下一个
}else{
i--;//孩子下一个
//饼干还不用分
}
}
return sum;
}
1827. 最少操作使数组递增
贪心,当前数比前一个数多1,即可满足递增且操作最少 — 多一毛钱都不给
int minOperations(int* nums, int numsSize){
int i;
int ans=0;//操作加一的次数
for(i = 1;i<numsSize;i++){//从第二个数开始比较
if(nums[i]<=nums[i-1]){//若前一个数大于或等于当前数,则需要操作
ans += nums[i-1]-nums[i]+1;//操作的次数则为两数的差值+1
nums[i] += nums[i-1]-nums[i]+1;//当前的数=操作后的值= 本身 + 差值 + 1
}
}
return ans;
}
945. 使数组唯一的最小增量
每次可对一个数进行+1,让每个数都不一样。
贪心:把数组升序排列,只需要找到相等的两个数,对后者+1,即可用最少的操作达成要求。
注意:① 如果这两个数的后者,和它的后一个元素只差一,会出现加1后,和后一个元素相等,此时需要再对后一个元素+1,并继续判断后一个数操作后是否和后一个数相等。② 也存在着好几个相同大小的数,则当第一个元素+1后,后面的元素则比他小了。
int cmp(const void *a,const void *b){
return *(int *)a - *(int *)b;
}
int minIncrementForUnique(int* nums, int numsSize){
qsort(nums,numsSize,sizeof(int),cmp);//升序排序
int i,moves = 0;//操作次数记录
for(i=1;i<numsSize;i++){//从第二个数开始
if(nums[i]<=nums[i-1]){//比较当前数和前一个数是否
moves += nums[i-1] - nums[i] +1;//找出差值+1
nums[i] += nums[i-1] - nums[i] +1;
}
}
return moves;
}
611. 有效三角形的个数
暴力破解:三层循环,对每一个数,都和其他两个数都计算一遍看是否满足三角形
贪心算法:对数组进行升序排序,只要判断到不满足 a+b>c,则c后续的值就不需要再比较了,肯定都大于a+b
// 暴力破解:三层循环,对每一个数,都和其他两个数都计算一遍看是否满足三角形
// 贪心算法:对数组进行升序排序,只要判断到不满足 a+b>c,则c后续的值就不需要再比较了,肯定都大于a+b
// 此题目的贪心算法关键在于用最小的代价(站在前人的si体上)去找到能满足的最大边
int cmp(const void *a,const void *b){
return (*(int *)a - *(int *)b);
}
int triangleNumber(int* nums, int numsSize){
qsort(nums,numsSize,sizeof(int),cmp);//数组升序
int i,j,k,ans=0;
for(i=0;i<numsSize;i++){//从第0个元素开始比较 == a =num[i]
j = i+1;//b = num[i+1] ==>比a大的最小数
k = j+1;//c = num[j+1] ==>比b大的最小数
while(j<numsSize)c
while(k<numsSize){//长边还没到底的时候
if(nums[i]+nums[j]<=nums[k]){//一旦不满足,后续的c都会不满足,均比 a + b要大
break;
}
k++;
}
ans += k-j-1;//满足的情况:就是有多少条长边满足,也就是最后k从j+1起,到了哪个位置。
//此时,k停留着
j++;//j向后面取一个数,继续判断a + b > c;因为上一个j是满足k之前的三角形的,所以不需要再让k从j+1开始计算了,通过上一步的相减即可。
//此题目的贪心算法关键在于用最小的代价(站在前人的si体上)去找到能满足的最大边
if(k==j){
k++;
}
}
}
return ans;
}
//超时,没思考清除具体问题在哪?
int cmp(const void *a,const void *b){
return (*(int *)a - *(int *)b);
}
int triangleNumber(int* nums, int numsSize){
qsort(nums,numsSize,sizeof(int),cmp);
int i,j,k,ans=0;
//超时
for(int i = 0;i<numsSize-2;i++){
if(nums[i]==0) continue;
for(j = i+1;j<numsSize-1;j++){
for(k = j+1;k<numsSize;k++){
if(nums[i]+nums[j]>nums[k]){
ans++;
}else{
break;
}
}
}
}
return ans;
}
三、今日收获
这道题的解题思路,贪心的思路比较有意思,大概可能是理解了,但内心还未非常确切,找时间网上看下解题思路,是不是和我理解的一样。