HOT100题打卡第37天——贪心算法
贪心算法
贪心算法是解决优化问题的核心思路之一。核心结论是:贪心算法通过每一步都做出局部最优选择,最终希望得到全局最优解,但并非适用于所有问题。
核心特点
- 局部最优优先:每一步都选择当前情况下的最优解,不回溯。
- 无后效性:当前选择只依赖当前状态,不影响后续状态的选择逻辑。
- 适用场景有限:仅当问题满足 “贪心选择性质” 和 “最优子结构性质” 时,才能得到全局最优。
适用问题与经典案例
- 贪心选择性质:全局最优解可通过一系列局部最优解逐步构造。
- 最优子结构:问题的最优解包含其子问题的最优解。
- 经典案例:
- 哈夫曼编码:通过每次合并权重最小的节点,得到最优编码方案。
- 活动选择问题:每次选择结束时间最早的活动,最大化可参与活动数。
- 零钱兑换(特定面额):如美元硬币体系,每次选最大面额,可快速找零。
局限性
- 并非万能:很多问题中局部最优无法累积成全局最优,比如普通零钱兑换问题(如面额 1、3、4,兑换 6 元,贪心选 4+1+1=6,最优为 3+3=2 枚)。
- 需严格验证:使用前必须证明问题满足两大性质,否则可能得到次优解。
例题
121. 买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
示例 1:
输入:[7,1,5,3,6,4] 输出:5 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
分析题目
这道题需要结合动态规划和贪心算法的知识来解决
要求最大利润,需要求出每一天卖出股票获得的最大利润,然后取最大值
dp数组的含义
用一个数组来储存每一天卖出股票能获取的最大利润,下标 i 就表示第 i 天,dp[ i ]就表示若当天卖出股票能获取的最大利润,因此dp数组的长度=price数组长度+1,索引0位置表示第0天卖出股票获取到的最大利润,即0,后面的索引 i 就和天数一一对应
数组初始化
为了不影响结果,数组中所有位置都默认初始值为0,即股票未卖出状态
因为只能不能在买入的同天卖出,所以第一天只能买入不能卖出,dp[ 1 ]=0
循环遍历
因为只能先买入再卖出,所以倒序遍历数组,遍历到的索引位置就表示卖出当天,往当前索引之前遍历买入股票的日子,得到当日卖出股票的最大利润存入dp数组,最后求dp数组的最大值就得到了最大利润
代码
import java.util.Arrays;class Solution {public int maxProfit(int[] prices) {int[] dp = new int[prices.length+1];for (int i = dp.length-1; i >= 2; i--) {int profit = 0;for (int j = 1; j < i; j++) {int currentProfit=prices[i-1]-prices[j-1];profit=Math.max(profit,currentProfit);}dp[i]=profit;}int maxProfit=0;for (int i = 0; i < dp.length; i++) {maxProfit=Math.max(maxProfit,dp[i]);}return maxProfit;}
}
这个算法的时间复杂度是 O (n²),可以解决大多数用例,但数组长度过长还是会超时,而且dp数组其实没有起到什么作用,直接用一个变量不变更新就可以,看看下面这个算法
class Solution {public int maxProfit(int[] prices) {
//这个变量用来储存最大利润int maxProfit = 0;//这个变量储存的是我们遇到的所有元素中的最大值,初始把数组末尾的数作为当前我们遇到的最大值int maxPrice=prices[prices.length-1];
//还是倒序遍历数组,从倒数第二天开始,如果当天的股票价格小于这天之后的股票最高价,也就是maxProfit
//就可以计算出当前的利润,通过这种方式向前遍历不断更新利润的最大值for (int i = prices.length - 2; i >= 0; i--) {if(prices[i]<maxPrice){int profit=maxPrice-prices[i];maxProfit=Math.max(maxProfit,profit);}
//如果当天股票价格大于后来的最大值,那就把股价最大值更新为当前值,继续向前遍历if(prices[i]>maxPrice){maxPrice=prices[i];}}
//最后将最大值返回return maxProfit;}
}
这样就可以把时间复杂度降低到O(n),空间复杂度降低到O(1),大大提升了效率
