贪心算法总结
贪心算法:局部最优达到全局最优
举一个最简单的例子:小明和小王喜欢吃苹果,小明可以吃五个,小王可以吃三个。已知苹 果园里有吃不完的苹果,求小明和小王一共最多吃多少个苹果。在这个例子中,我们可以选用的 贪心策略为,每个人吃自己能吃的最多数量的苹果,这在每个人身上都是局部最优的。又因为全 局结果是局部结果的简单求和,且局部结果互不相干,因此局部最优的策略也同样是全局最优的策略。
贪心思想:
因为本题用到了贪心算法所以先来了解一下「贪心算法」的问题需要满足的条件:
最优子结构:规模较大的问题的解由规模较小的子问题的解组成,规模较大的问题的解只由其中一个规模较小的子问题的解决定;
无后效性:后面阶段的求解不会修改前面阶段已经计算好的结果;
贪心选择性质:从局部最优解可以得到全局最优解。
综上可得:贪心算法就是做出当前状态下的最优选择认为就可以解决问题。
第一题:
942. 增减字符串匹配
由范围 [0,n] 内所有整数组成的 n + 1 个整数的排列序列可以表示为长度为 n 的字符串 s ,其中:
如果 perm[i] < perm[i + 1] ,那么 s[i] == 'I'
如果 perm[i] > perm[i + 1] ,那么 s[i] == 'D'
给定一个字符串 s ,重构排列 perm 并返回它。如果有多个有效排列perm,则返回其中 任何一个 。
示例 1:
输入:s = "IDID" 输出:[0,4,1,3,2]
示例 2:
输入:s = "III" 输出:[0,1,2,3]
示例 3:
输入:s = "DDI" 输出:[3,2,0,1]
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Solution { public int [] diStringMatch(String s) { int length=s.length(); int low= 0 ; int high=s.length(); int []perm= new int [length+ 1 ]; for ( int i= 0 ;i<length;i++){ perm[i]=s.charAt(i)== 'I' ?low++:high--; } perm[length]=low; return perm; } } |
如果字母是‘I’,趋势是上升的,perm[i+1]>perm[i]。取perm[0]=0,下一个任意元素都大于perm[0],到达局部最优。然后比较剩下的。剩下的最大元素未high-1
如果字母是‘D’,原理类似。
第二题:
455. 分发饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
输入: g = [1,2,3], s = [1,1] 输出: 1 解释: 你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。 虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。 所以你应该输出1。
示例 2:
输入: g = [1,2], s = [1,2,3] 输出: 2 解释: 你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。 你拥有的饼干数量和尺寸都足以让所有孩子满足。 所以你应该输出2.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Solution { public int findContentChildren( int [] g, int [] s) { int cookie= 0 ,child= 0 ; Arrays.sort(g); Arrays.sort(s); while (child<g.length&&cookie<s.length){ //如果孩子的食量小于饼干,换孩子,换饼干 if (g[child]<=s[cookie]) child++; //如果孩子的食量大于饼干,只换饼干 cookie++; } return child; } } |
第三题:
135. 分发糖果
n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数。
示例 1:
输入:ratings = [1,0,2] 输出:5 解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。
实例2
输入:ratings = [1,2,2] 输出:4 解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。 第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | class Solution { public int candy( int [] ratings) { int ans= 0 ; int length=ratings.length; int []res= new int [length]; //没人至少一个糖,没人先发一个 for ( int i= 0 ;i<length;i++){ res[i]= 1 ; } for ( int i= 1 ;i<length;i++){ if (ratings[i]>ratings[i- 1 ]) res[i]=res[i- 1 ]+ 1 ; } for ( int i=length- 2 ;i>= 0 ;i--){ if (ratings[i]>ratings[i+ 1 ]) //取较大的值,有一种情况本身就是评分高的糖果多,所以就不用+1了 res[i]=Math.max(res[i],res[i+ 1 ]+ 1 ); } for ( int i= 0 ;i<length;i++){ ans+=res[i]; } return ans; } } |
第四题:
435. 无重叠区间
给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。
示例 1:
输入: intervals = [[1,2],[2,3],[3,4],[1,3]] 输出: 1 解释: 移除 [1,3] 后,剩下的区间没有重叠。
示例 2:
输入: intervals = [ [1,2], [1,2], [1,2] ] 输出: 2 解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
实例3:
输入: intervals = [ [1,2], [2,3] ] 输出: 0 解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Solution { public int eraseOverlapIntervals( int [][] intervals) { //重点是根据第二列元素对数组进行排序 Arrays.sort(intervals, new Comparator< int []>() { @Override public int compare( int [] o1, int [] o2) { return o1[ 1 ]-o2[ 1 ]; } }); int count= 0 ; int end=intervals[ 0 ][ 1 ]; for ( int i= 1 ;i<intervals.length;i++){ if (intervals[i][ 0 ]<end){ count++; } else { end=intervals[i][ 1 ]; } } return count; } } |
第五题
605. 种花问题
假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给你一个整数数组 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Solution { public boolean canPlaceFlowers( int [] flowerbed, int n) { //贪心的思想,每个位置我都想种花 int length=flowerbed.length; for ( int i= 0 ;i<length;i++){ if (flowerbed[i]== 0 &&(i== 0 ||flowerbed[i- 1 ]== 0 )&&(i==length- 1 || flowerbed[i+ 1 ]== 0 )){ flowerbed[i]= 1 ; n--; } if (n<= 0 ) return true ; } return false ; } } |
第六题:
763. 划分字母区间
字符串 S
由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
示例:
输入:S = "ababcbacadefegdehijhklij" 输出:[9,7,8] 解释: 划分结果为 "ababcbaca", "defegde", "hijhklij"。 每个字母最多出现在一个片段中。 像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class Solution { public List<Integer> partitionLabels(String s) { List<Integer>res= new ArrayList<>(); int []words= new int [ 26 ]; int start= 0 ; int end= 0 ; for ( int i= 0 ;i<s.length();i++){ //统计字母最后一次出现的次数 words[s.charAt(i)- 'a' ]=i; } for ( int i= 0 ;i<s.length();i++){ end=Math.max(end,words[s.charAt(i)- 'a' ]); //说明后面不会再出现该字母 if (end==i){ res.add(end-start+ 1 ); start=end+ 1 ; } } return res; } } |
第七题
122. 买卖股票的最佳时机 II
给你一个整数数组 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:
输入:prices = [1,2,3,4,5] 输出:4 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。 总利润为 4 。
示例 3:
输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Solution { public int maxProfit( int [] prices) { //将数组中的上升段进行累加,就得到最大利润 int length=prices.length; int max= 0 ; int res= 0 ; for ( int i= 1 ;i<length;i++){ max=Math.max( 0 ,prices[i]-prices[i- 1 ]); res+=max; } return res; } } |
第八题:
406. 根据身高重建队列
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。
示例 1:
输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]] 输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 解释: 编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。 编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。 编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。 编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。 编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。 编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。 因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列
示例 2:
输入:people = [[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]] 输出:[[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Solution { public int [][] reconstructQueue( int [][] people) { Arrays.sort(people, new Comparator< int []>() { @Override public int compare( int [] p1, int [] p2) { //首先根据身高排序 if (p1[ 0 ] != p2[ 0 ]) return p2[ 0 ] - p1[ 0 ]; //升高相同的情况下根据前面有几个人排序(升序) else return p1[ 1 ] - p2[ 1 ]; } }); List< int []> res = new LinkedList<>(); for ( int [] person : people) { //根据前面有几个人升序插入应该所在的位置 res.add(person[ 1 ], person); } return res.toArray( new int [res.size()][]); } } |
第九题:
665. 非递减数列
给你一个长度为 n 的整数数组 nums ,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中任意的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。
示例一:
输入: nums = [4,2,3] 输出: true 解释: 你可以通过把第一个 4 变成 1 来使得它成为一个非递减数列。
示例 2:
输入: nums = [4,2,1] 输出: false 解释: 你不能在只改变一个元素的情况下将其变为非递减数列。
1 2 3 4 5 6 7 8 9 10 11 12 | 这道题给了我们一个数组,说我们最多有 1 次修改某个数字的机会, 问能不能将数组变为非递减数组。题目中给的例子太少,不能覆盖所有情况,我们再来看下面三个例子: 4 , 2 , 3 - 1 , 4 , 2 , 3 2 , 3 , 3 , 2 , 4 我们通过分析上面三个例子可以发现,当我们发现后面的数字小于前面的数字产生冲突后, [ 1 ]有时候需要修改前面较大的数字(比如前两个例子需要修改 4 ), [ 2 ]有时候却要修改后面较小的那个数字(比如前第三个例子需要修改 2 ), 那么有什么内在规律吗?是有的,判断修改那个数字其实跟再前面一个数的大小有关系, 首先如果再前面的数不存在,比如例子 1 , 4 前面没有数字了,我们直接修改前面的数字为当前的数字 2 即可。 而当再前面的数字存在,并且小于当前数时,比如例子 2 ,- 1 小于 2 ,我们还是需要修改前面的数字 4 为当前数字 2 ; 如果再前面的数大于当前数,比如例子 3 , 3 大于 2 ,我们需要修改当前数 2 为前面的数 3 。 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class Solution { public boolean checkPossibility( int [] nums) { int k= 0 ; if (nums== null ||nums.length<= 0 ) return true ; for ( int i= 1 ;i<nums.length;i++){ if (nums[i]>=nums[i- 1 ]) continue ; k++; if (i- 2 >= 0 &&nums[i- 2 ]>nums[i]){ //需要对nums[i]值进行修改,会影响到下一个元素的判断 nums[i]=nums[i- 1 ]; } //一下可以不做操作,因为不会影响后面的操作 // else { // nums[i-1] = nums[i]; // } } return k< 2 ; } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南