由于leetcode题库的顺序不是按难易程序来排序的,按照顺序来上手难度有点大。所以目前会优先从简单题目中选择题目,后面再选择中等难度以及困难难度。今天选择的题目是第860题-柠檬找水,先看题目:
在柠檬水摊上,每一杯柠檬水的售价为 5 美元。
顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
如果你能给每位顾客正确找零,返回 true ,否则返回 false 。
这道题的标签是“贪心算法”,何为贪心算法?先来看看它的定义:在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。
定义有点懵,直接开始做题。看到这道题的时候的第一反应是:这个看起来简单,但应该怎样转化成代码!后面就想到用一个对象当做钱包,把收到的钱给存起来,于是说干就干,直接上代码:
var lemonadeChange = function(bills) {
if (bills[0] >= 10 || bills[1] >= 20 || bills[2] >= 20) return false
const wallet = {
5: 0,
10: 0,
20: 0
}
for (let i = 0; i < bills.length; i++) {
if (bills[i] === 5) {
wallet[5]++
} else if (bills[i] === 10) {
if (wallet[5] > 0) {
wallet[10]++
wallet[5]--
} else {
return false
}
} else if (bills[i] === 20) {
if (wallet[10] > 0 && wallet[5] > 0) {
wallet[10]--
wallet[5]--
wallet[20]++
} else if (wallet[10] == 0 && wallet[5] >= 3) {
wallet[5] -= 3
wallet[20]++
} else {
return false
}
}
}
return true
};
经过一轮调试,算是通过了。但这代码也太长了,一点都不优雅,再优化一下。参考一下leetcode官方的题解,其实20元这个变量是不会用到的,所以可以去掉:
var lemonadeChange = function(bills) {
let five = 0; let ten = 0;
for (let i = 0; i < bills.length; i++) {
if (bills[i] === 5) {
five++
} else if (bills[i] === 10) {
if (five === 0) return false
five--; ten++;
} else {
if (ten > 0 && five > 0) {
ten--;five--;
} else if (ten == 0 && five >= 3) {
five -= 3
} else {
return false
}
}
}
return true
};
再回过头来看看这道题,目前是把问题给拆成3份,当用户给的是5美金有什么应对方式;当用户给的是10美金又是怎样应对,相应的20美金同理。贪心算法就是针对当前问题给出最优解,从而希望能达到全局的最优。
今天这题,题目简单,难就难在如何理解贪心算法的概念。所以平时工作中,可以经常用到某种算法,只是不知道这种算法的定义。就好比突然看到某个自己经常说出口,但从没写过的字,恍然大悟:原来这个字是这样写的。