Leetcode分类——贪心算法

本文深入探讨了贪心算法的基本概念、适用前提及其在解决实际问题中的应用。通过Leetcode经典题目,如分配饼干问题(Leetcode455)、移除数字问题(Leetcode402)等,详细解析了贪心策略的制定与实施过程,同时提供了完整的代码示例。此外,还介绍了贪心算法在摇摆序列(Leetcode376)、跳跃游戏(Leetcode55)和射箭问题(Leetcode452)等题目的运用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、贪心法

遵循某种规律,不断贪心地选取最优策略来求解。

二、贪心的前提

最优解能够划分成多个次优解,例如找零钱问题中,零钱的种类必须是倍数包含关系(如100,20,10,15,1元),如果包含了50元或7元类型,贪心法求解可能出错,此时应该使用动态规划来做。

三、算法举例

Leetcode 455

题目

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

注意:
你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。

示例 1:
输入: [1,2,3], [1,1]
输出: 1
解释: 
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

示例 2:
输入: [1,2], [1,2,3]
输出: 2
解释: 
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.

贪心规律

  1. 如果某个饼干不能满足该孩子,一定不能满足需求因子更大的孩子;
  2. 如果能用更小的饼干满足该孩子,则没必要用更大的;
  3. 需求因子小的孩子更容易被满足,故应从小到大开始分配。

算法思路

  1. 对饼干大小和孩子的需求因子从小到大排序;
  2. 二路遍历,满足条件即比较下一个,直至一路完毕。
class Solution {
    public int findContentChildren(int[] g, int[] s) {
    	//步骤1:排序
        Arrays.sort(g);
        Arrays.sort(s);
        int count = 0, childIndex = 0, cookieIndex = 0;
       	//步骤2:遍历比较
        while (childIndex < g.length && cookieIndex < s.length) {
            if (g[childIndex] <= s[cookieIndex]) {
            	//只有匹配成功,才开始比较下一个(需求因子更大的),计数器也要加1
                ++count;
                ++childIndex;
            }
            //无论成功失败,饼干序列都要加1
            ++cookieIndex;
        }
        return count;
    }
}

Leetcode 402

题目,贪心算法经常和堆栈等数据结构一起出现

给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。

注意:
num 的长度小于 10002 且 ≥ k。
num 不会包含任何前导零。
示例 1 :

输入: num = "1432219", k = 3
输出: "1219"
解释: 移除掉三个数字 4, 3,2 形成一个新的最小的数字 1219。
示例 2 :

输入: num = "10200", k = 1
输出: "200"
解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。
示例 3 :

输入: num = "10", k = 2
输出: "0"
解释: 从原数字移除所有的数字,剩余为空就是0

贪心规律

  • 从高位向地位遍历,如果对应的数字大于下一位数字,则把该位去掉。

算法思路

  • 最暴力的解法,删除k个数字即从最高位开始遍历k次;
  • 如何简化:利用栈存储,依次压栈,如果当先待压入的数字小于栈顶元素,则弹栈。(比较过的位数不用动,已经是最小的了)

特殊情况

  • 如果栈空后k仍大于0怎么办?——
  • 如果存在0怎么办?——
public String removeKdigits1(String num, int k) {
        Stack<Integer> stack = new Stack<>();
        String res = "";
        //遍历整个字符串
        for (int i = 0; i < num.length(); ++i){
            int number = num.charAt(i) - '0';
            //贪心算法删除数字,一个数字可以一直删
            while (!stack.empty() && stack.peek() > number && k > 0) {
                stack.pop();
                --k;
            }
            //首位不为0,或者0不在首位时,入栈
            if (number != 0 || !stack.empty())
                stack.push(number);
        }

        //k还有剩余的情况
        while (!stack.empty() && k > 0) {
            stack.pop();
            --k;
        }
        
        //将栈内数字输出到字符串
        while (!stack.empty()){
            res = stack.pop() + res;
        }
        return (res == "")? "0" : res;
    }

其他题目

Leetcode 376

贪心规律:当序列有一段连续的递增或递减时,为形成摇摆子序列,我们只需要保留这段连续的递增或递减的首尾元素,这样更可能使得尾部的后一个元素成为摇摆子序列的下一个元素。
在这里插入图片描述

Leetcode 55

Leetcode 452

### LeetCode 678 题 贪心算法解决方案分析 #### 问题描述 LeetCode第678题名为“有效的括号字符串”,题目要求判断给定的字符串是否为有效括号串。一个有效括号串满足以下条件: - 字符串为空; - 或者是一个被匹配好的括号对 `()`; - 或者是由多个上述情况组成的组合。 为了简化处理,题目引入了星号`*`作为通配字符,它可以表示左括号`(`、右括号`)`或空格。 #### 方案解析 针对这个问题,采用贪心算法的核心在于维护两个变量来追踪可能的最大和最小未闭合左括号数量。这有助于动态调整并验证整个过程中是否存在合法路径使得所有括号都得到正确匹配[^1]。 ```java public boolean checkValidString(String s) { int low = 0; int high = 0; for (char c : s.toCharArray()) { if (c == '(') { ++low; ++high; } else if (c == ')') { --low; --high; } else { // * --low; ++high; } if (high < 0) break; // 提前终止条件 low = Math.max(low, 0); } return low == 0; } ``` 这段代码展示了如何运用贪心原则解决本题。遍历输入字符串的同时更新高低边界值,其中高边界的增加代表乐观估计——假设遇到的是最有利于形成更多开放状态的情况;而低边界的减少则反映了悲观预期下的变化趋势。当发现即使是最理想的状况也无法维持非负数目的待关闭左括号时,则立即返回失败标志。最后如果能保持至少一种情形下不存在多余未封闭的左括号,则认为该表达式是合理的[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值