一、前言
先来看一道简单的数学问题:
小明有30元钱,每瓶酒要5元钱,每3个空瓶子可以换1瓶酒,请问小明最多可以喝到多少瓶酒?
这道题目显然是一道求最优解的问题,由于数据量小我们可以用最简单最直接的枚举法来解决,但是如果将题目泛化一下呢:
小明现在购买了m瓶酒,每n个空瓶子可以换1瓶酒,请问小明最多可以喝到多少瓶酒?
引入未知数后,无法采用枚举的方法来解决问题了,即使使用枚举法,在数据量很大的情况下也很容易出现考虑不全面的问题,因此对于这种比较复杂的问题需要我们利用算法来进行快速准确地解决。这道题目就是贪心算法的一道经典例题[换酒问题]。
二、什么是算法
要想学习算法,首先就需要了解一下算法究竟是什么,来看一下百度百科的定义:
算法是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。
其实算法在我们的生活中非常常见,比如炒菜其实就是一个简单的算法,先热油,再下食材,然后加调料,最后起锅之前撒点盐炒匀。(说的我都有点饿了)
三、什么是贪心算法
1.含义
了解了算法的基础知识后,我们来看一下本篇要重点介绍的常见算法之一[贪心算法]。贪心算法是指在对复杂问题求解时,将复杂问题拆分成多个子问题,总是做出在当前子问题看来是最好的选择。
2.基本思路
- 建立数学模型来描述问题
- 把求解的问题分成若干个子问题
- 对每个子问题求解,得到子问题的局部最优解
- 把子问题的解局部最优解合成原来问题的一个解
3.适用场景
贪心策略适用的前提是:局部最优策略能导致产生全局最优解。
因此贪心算法十分适用于以下场景:
- 同质子结构:每次做完选择后,面对的还是同样的问题
- 最优子结构:一个问题的最优解包含其子问题的最优解
- 贪心选择:每次都贪心选择后,最后可以得到整体最优解
四、代码实现
现在再回到起初的[换酒问题],按照基本思路来分析一下这个问题。首先建立数学模型,如下所示:
然后再来分解问题,将问题分解成四步:
第一步:判断当前空瓶的数量是否能够兑换到酒。如果可以就执行后续步骤,如果不行,当前已经喝到酒的数量就是最大数量;
第二步:将当前所有的空瓶最大限度的兑换酒,获得本次兑换的最优解。
第三步:喝掉兑换来的酒,获得空酒瓶。
第四步:统计当前所有空瓶数量,第二步中换酒后剩余的空瓶加上第三步中获得的空瓶。
分解问题之后,可以清晰地看出这个问题完全符合贪心算法的适用场景:每次用空瓶子换完酒喝掉之后面对的还是空瓶子换酒同样的问题;想喝到最多的酒就要每次换酒的时候最大限度的兑换;每次兑换酒时进行贪心选择,最后喝到的酒一定是最多的。
其实分析完之后,代码逻辑基本就了然于胸了,这里给出一个简单的实现:
public static int drink(int m, int n) {
// 喝到酒的总数和空酒瓶的总数初始值为均为已购买的m瓶酒
int drinked = m;
int empty = m;
// 判断空瓶数量是否可以继续换酒
while ((empty / n) != 0) {
// 空瓶换酒
int changed = empty / n;
// 喝掉换来的酒
drinked += changed;
// 空瓶数量=换酒剩下的空瓶+喝掉换来的酒的空瓶
empty = empty % n + changed;
}
return drinked;
}
五、经典例题分析
买卖股票的最佳时机 (力扣122)
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。可以尽可能地完成更多的交易(多次买卖一支股票),但是同时只能参与一笔交易(买入之前必须卖出手上的股票),计算获得的最大利润。
首先来分析一下这个问题的组成:每次买卖股票后,面对的还是同样的买卖股票的问题,由无数次买卖股票的子问题组成。因此利用贪心算法,我们只要保证每次买卖股票都进行贪心选择即每次交易收益都为正,然后不断地交易就能获得最大利润。再简化一下就是只要第i天的股票价格大于第i-1天的,我们就在第i-1天买入,第i天卖出,依次循环。
这里给出一个简单的实现:
public static int maxProfit3(int[] prices) {
int maxProfit = 0;
for (int i = 1; i < prices.length; i++) {
// 只要当天价格高于前一天就在进行买卖交易
if (prices[i] > prices[i - 1]) {
maxProfit += prices[i] - prices[i - 1];
}
}
return maxProfit;
}
(买卖股票是力扣上面最长的一个系列了,根据不同的算法有不同的变种,涉及到了常见的各种算法,如贪心算法,动态规划,搜索算法等等,有兴趣可以把整个股票系列的题都撸一遍,既学了算法,说不定还能成为股神呢。O(∩_∩)O哈哈~)
六、总结
学习算法并不是为了记住几个题目,然后遇到同样的题目的时候把代码搬过来,主要还是为了锻炼我们的逻辑思维能力。如果我们能合理的利用算法来解决问题,那么肯定能够写出高质量的代码,达到事半功倍的效果。这里给大家强烈推荐力扣:https://leetcode-cn.com/,这里不仅可以学习和练习算法,还能看到各路大神写的优秀的题解,看完一定受益匪浅。