[学习报告]《LeetCode零基础指南》(第六讲) 贪心

[学习报告]《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;
}

三、今日收获

611. 有效三角形的个数

这道题的解题思路,贪心的思路比较有意思,大概可能是理解了,但内心还未非常确切,找时间网上看下解题思路,是不是和我理解的一样。

四、今日疑问

image-20220319164239291

五、其他参考

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忘词木头人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值