算法题——贪心算法(错题总结)

顾名思义,贪心算法或贪心思想采用贪心的策略,保证每次操作都是局部最优的,从而使最后得到的结果是全局最优的。

注:以下问题引入是GitHub某位大佬的著作里面的原话,这里仅供各位学习。本书永久免费地址:github.com/changgyhub/leetcode_101

分配问题

题目描述
有一群孩子和一堆饼干,每个孩子有一个饥饿度,每个饼干都有一个大小。每个孩子只能吃
一个饼干,且只有饼干的大小不小于孩子的饥饿度时,这个孩子才能吃饱。求解最多有多少孩子
可以吃饱。
输入输出样例
输入两个数组,分别代表孩子的饥饿度和饼干的大小。输出最多有多少孩子可以吃饱的数
量。
Input: [1,2], [1,2,3]
Output: 2
在这个样例中,我们可以给两个孩子喂 [1,2] [1,3] [2,3] 这三种组合的任意一种。
题解
因为饥饿度最小的孩子最容易吃饱,所以我们先考虑这个孩子。为了尽量使得剩下的饼干可
以满足饥饿度更大的孩子,所以我们应该把大于等于这个孩子饥饿度的、且大小最小的饼干给这
个孩子。满足了这个孩子之后,我们采取同样的策略,考虑剩下孩子里饥饿度最小的孩子,直到
没有满足条件的饼干存在。
简而言之,这里的贪心策略是,给剩余孩子里最小饥饿度的孩子分配最小的能饱腹的饼干。
至于具体实现,因为我们需要获得大小关系,一个便捷的方法就是把孩子和饼干分别排序。
这样我们就可以从饥饿度最小的孩子和大小最小的饼干出发,计算有多少个对子可以满足条件。

区间问题

题目描述
给定多个区间,计算让这些区间互不重叠所需要移除区间的最少个数。起止相连不算重叠。
输入输出样例
输入是一个数组,数组由多个长度固定为 2 的数组组成,表示区间的开始和结尾。输出一个
整数,表示需要移除的区间数量。
Input: [[1,2], [2,4], [1,3]]
Output: 1
在这个样例中,我们可以移除区间 [1,3] ,使得剩余的区间 [[1,2], [2,4]] 互不重叠。
题解
求最少的移除区间个数,等价于尽量多保留不重叠的区间。在选择要保留区间时,区间的结
尾十分重要:选择的区间结尾越小,余留给其它区间的空间就越大,就越能保留更多的区间。因
此,我们采取的贪心策略为,优先保留结尾小且不相交的区间。
具体实现方法为,先把区间按照结尾的大小进行增序排序,每次选择结尾最小且和前一个选
择的区间不重叠的区间。我们这里使用 C++ Lambda ,结合 std::sort() 函数进行自定义排
序。
在样例中,排序后的数组为 [[1,2], [1,3], [2,4]] 。按照我们的贪心策略,首先初始化为区间
[1,2] ;由于 [1,3] [1,2] 相交,我们跳过该区间;由于 [2,4] [1,2] 不相交,我们将其保留。因
此最终保留的区间为 [[1,2], [2,4]]

做题记录

leetcode605:种花问题(难度:简单)

假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

给你一个整数数组  flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false。

示例 1:

输入:flowerbed = [1,0,0,0,1], n = 1
输出:true
示例 2:

输入:flowerbed = [1,0,0,0,1], n = 2
输出:false
 

第一次提交受书中分糖题目的影响(题目中用vector类,采用顺序和逆序两次遍历),当时没有看到两个题目之间的区别,竟也使用了两次遍历(当时一定是脑抽了,其实这题一次遍历就可以)。

当时第一次提交的错误答案:

class Solution {
    public boolean canPlaceFlowers(int[] flowerbed, int n) {
        int count=0;
        for(int i=0; i<flowerbed.length; i++){
            if(flowerbed[i]==1){
                break;
            }else{
                if(i>=1&&flowerbed[i-1]==1){
                    break;
                }else{
                    flowerbed[i]=1;
                    count++;
                }
            }
        }
        for(int j=flowerbed.length-1; j>=0; j--){
            if(flowerbed[j]==0){
                break;
            }else{
                if(j<flowerbed.length-2&&flowerbed[j+1]==1){
                    flowerbed[j]=0;
                    count--;
                }else{
                    break;
                }
            }
        }
        if(count<=n){
            return true;
        }
        return false;
    }
}

其实这里我觉得理论上是可以的,不知道为什么测试结果不太正确,而且这里的两次遍历完全是多此一举,后来看到了别人的解题思路,修改后提交:

class Solution {
    public boolean canPlaceFlowers(int[] flowerbed, int n) {
        for(int i=0; i<flowerbed.length; i++){
            if(flowerbed[i]==1){
                i++;
            }else if(flowerbed[i]==0&&(i+1==flowerbed.length||flowerbed[i+1]==0)){
                n--;
                i++;
            }
        }
        return n<=0;
    }
}

这里的算法思想是:从头到尾遍历一次,如果是1,则直接跳过下一个遍历,因为后两个才有可以种花的可能。如果是0,则证明前一个肯定是0,因为前一个如果是1的话可能不会遍历后一个,所以我们这里只需要再判断一下后面一个是0的话就可以种花啦。这里注意一下细节问题:如果遍历到最后一个元素,则i+1个元素是不存在的,则直接种花就可以啦。

leetcode122  买卖股票的最佳时机(难度:中等)

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润 。

示例 1:

输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
     总利润为 4 + 3 = 7 。
示例 2:

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

做题耗时25分钟。其实思路很快就有了,但可能基础不太好,写代码过程中对一些条件语句的思考耗时比较长,而且测试用例错误,加上修修改改,,自然耗时比较长了。提交了一次就通过了,相比之前还是有进步的。

class Solution {
    public int maxProfit(int[] prices) {
        int profit=0;
        int buy=0;
        int sell=0;
        while(sell<prices.length){
            if(prices[sell]<=prices[buy]){
                buy=sell;
            }else{
                if(sell+1==prices.length || prices[sell+1]<prices[sell]){
                    profit = prices[sell]-prices[buy]+profit;
                    buy = sell;
                    }
                }
            sell++;
        }
        return profit;
    }
    
}

 提交结果:

算法思路:设定两个指针buy(买入)和sell(卖出),再设置总利润profit。遍历一遍数组,当prices[sell]比prices[buy]低时,则不会卖出,否则亏本,这时经较低价格的price[sell]赋值给买入;当prices[sell]比prices[buy]高时,再判断prices[sell+1]是否更高,更高则不宜卖出,当prices[sell+1]<peices[sell]则卖出,每次卖出的价格都累计到profit中。这里再注意一下当遍历到结尾的情况,此时不存在sell+1。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值