diff --git a/article/2020/greedy-algorithms-101.md b/article/2020/greedy-algorithms-101.md index 619afa77894..767c80c0e2c 100644 --- a/article/2020/greedy-algorithms-101.md +++ b/article/2020/greedy-algorithms-101.md @@ -2,27 +2,27 @@ > * 原文作者:[Mario Osorio](https://medium.com/@mario5o) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/article/2020/greedy-algorithms-101.md](https://github.com/xitu/gold-miner/blob/master/article/2020/greedy-algorithms-101.md) -> * 译者: -> * 校对者: +> * 译者:[chaingangway](https://github.com/chaingangway) +> * 校对者:[PingHGao](https://github.com/PingHGao) -# Greedy Algorithms 101 +# 贪心算法,你入门了吗? ![](https://cdn-images-1.medium.com/max/2000/0*udmPDWYUmHDNJX5D) -Greedy algorithms are easy to implement in most cases, they are one of the most used programming schemas when it comes to solving optimization problems, they are also a very good option because of their low resource consumption. +贪心算法在大多数情况下都易于实现,在求解最优问题时,也是最常用的编码套路之一,而且它的资源消耗也比较低。 -They have a little downside and it is that, they don’t always guarantee the optimal solution, they will get a close approach to the optimal but not always find it. Anyway in many cases, a close-to-optimal solution is more than enough. +不过这个算法也有缺点,它不能保证每次都能找到最优解,有时候只能找到接近最优解的方案。不管怎样,在很多情况下,能找到接近最优解的方案已经绰绰有余了。 -When talking about complexity, they typically take “**n”** iterations for an “**n”** sized problem so their complexities vary from O(n), O(n × log(n)) or as much as O(n²) +这个算法一般是对规模为 “**n**” 的问题迭代 “**n**” 次,所以它的复杂度可能是 O(n),O(n × log(n)),但是不会超过 O(n²)。 -Most problems for which they produce very good results share a couple of characteristics: +这个算法能解决的大多数问题都有以下两个特性: -1. **Greedy property**: It is based on taking in every iteration, the optimal local solution without thinking about future consequences. We trust in that taking the optimal local solution could lead to the optimal global solution, but as I said before, that not always happens. To demonstrate that in every iteration we are taking the most optimal solution we need to use the induction method (clearly not a trivial demonstration). -2. **An optimal** substructure****: I kind of mentioned it before. The problem must have the capability of been divided into subsets with each having an optimal solution. +1. **贪心属性**:它的意思是每次迭代时都采用局部最优解,而无需考虑对全局的影响。我们相信通过不断求解局部最优解终会得到全局最优解,但是正如我之前所说,这个结论不一定成立。为了证明在每次迭代中都求得了最优解,我们需要使用归纳法(显然不是简单的证明)。 +2. **最优子结构**: 我之前提到过一些。求解的问题必须能划分为子问题,每个子问题都有最优解。 -Now let me show you how to write your own greedy algorithm and then we will look at a very well known problem and solve it using our new greedy superpower. +本文中,我们将学习如何编写自己的贪心算法,然后用这个算法解决一个非常著名的难题。 -## General Greedy schema (Java) +## 贪心算法通用模版 (Java) ```Java public ArrayList greedy(ArrayList candidates) { @@ -43,33 +43,33 @@ public ArrayList greedy(ArrayList candidates) { } ``` -Before I explain the code, let’s first define some of the terminology I used in the pseudocode +我首先给出一些在伪代码中用到的术语的定义。 -1. **Candidates:** List of possible solutions to the problem. This can be any kind of data type, but usually an iterable one. You will see it better when we get to the example problem, bear with me for now 😁. -2. **Candidate:** The selected possible current solution to our problem -3. **Solution:** The first instance of the solution variable should simply be a data structure in which we will store our current solution to the problem. -4. **isSolution, candidatesLeft, removeCandidate, addCandidate, isGoodCandidate:** These are functions that we will also build, some of them, depending on the problem we are approaching, don’t even need to be whole functions but, for the shake of summarizing the schema, I decided to put them as functions. +1. **Candidates:** 所有可能的解集。它可以是任意的数据类型,但通常是可迭代的。在我们处理示例问题时,会加深对它的理解。现在请先记住结论 😁。 +2. **Candidate:** 在解集中,我们当前选中的一个解。 +3. **Solution:** 解变量的第一个实例只需是一种数据结构,在这里我们将存储当前的解。 +4. **isSolution, candidatesLeft, removeCandidate, addCandidate, isGoodCandidate:** 这些也是我们要创建的方法,其中的某些方法在一些实际问题中不必是完整的,但是为了总结代码模版,我把它们全部定义为方法。 -First we initialize our solution data structure, this could be an array, a boolean, an int… we simply need to state it. +首先,我们初始化解的数据结构,它可以是数组,布尔值,整数…… 我们只需要声明一下。 ``` solution ``` -Then we see this main **while** loop with a couple of functions inside. Those functions must also be build but sometimes you don’t need a whole other function to see for example if you have left candidates to try. +然后,我们看一下这个 while 循环,它的循环条件中有两个方法。这些方法必须编写,但有时并不需要完整的方法体,例如,判断是否有剩余备选解的这个方法。 ```js while (!isSolution(solution) && (candidatesLeft(candidates)) ``` -Once we have checked that we didn’t yet find a solution and that we still have candidates left to try, we select one candidate and immediately remove it from our list of candidates. +当我们发现当前尚未找到解,并且有剩余备选解可以尝试时,我们将选择一个备选解,并立即将其从我们的备选解集中删除。 ```js cadidate = selectCandidate(candidates); removeCandidate(candidate, candidates); ``` -The next step is pretty straight forward. If the candidate is suitable for our solution, then simply add it to the solution structure. +下一步很简单。如果候选解是正确的解,则只需将其添加到解结构中。 ```js if (isGoodCandidate(candidate, solution)) { @@ -77,7 +77,7 @@ if (isGoodCandidate(candidate, solution)) { } ``` -Then we simply check if we have reached the solution state and finally return it +然后,我们只需检查问题是否已达到解决的状态,然后将解返回。 ```js if (isSolution(solution)) { @@ -87,29 +87,29 @@ if (isSolution(solution)) { } ``` -OK! so once we have seen the code and broadly explained it, I will give you a problem and you should try and solve it yourself. It is a very well known problem so you will easily find it on the internet but I recommend giving it a try. +至此我们已经看完了代码并对其进行了粗略的解释,现在我给您出道题,请您尝试自己解答。这是一个众所周知的问题,在网上很容易就能搜到答案,但我建议您还是尝试自己解决。 --- -## Coin change problem +## 零钱兑换的问题 -You have 6 types of coins, and the value of each type is given as {50, 20, 10, 5, 2, 1} respectively, they are passed as an argument already sorted in decreasing value. **Each of the possible coins would be our candidates.** You must find a way of giving away the optimal change (**least amount of coins and the exact amount of change**) +有6种硬币,每种硬币的值分别为 {50,20,10,5,2,1},它们按递减排序作为参数传递。 **每种硬币都可能成为我们的候选解**。您必须找到一种最佳的兑换方式。(**用最少的硬币找零**) -**Example input:** 15 (**We must return a sum of 15 with the least amount of coins possible**) +**示例输入:** 15(**我们必须以最少的硬币数量凑齐 15 并返回硬币集合**) -**Example output:** 10, 5 (**We gave back a sum of 15 with the least amount of coins possible**) +**示例输出:** 10、5(**我们返回了和为 15 的硬币集合,且硬币数量最少**) -For this exact monetary system {50, 20, 10, 5, 2, 1} the algorithm should find an optimal solution but it is worth mentioning that any change in the available candidates, can result in the algorithm not giving an optimal solution. +在这个确定的硬币系统 {50,20,10,5,2,1} 中,该算法能找到最优解,但是请注意,如果候选解发生改变,可能会导致该算法无法找到最优解。 -#### Hint +#### 提示 -If you didn’t try hard enough, you shouldn’t be reading this 🤨… Just kidding, go ahead, I’m sure I̶ ̶h̶o̶p̶e̶ you already learned something new 😄 +如果您没有足够努力尝试,就不应该看这一段内容 🤨…… 开个玩笑,继续吧,我确信~~我希望~~您已经学到了一些新知识 😄。 -* A good **selectCandidate()** function is to start choosing big coins, and then filling the remaining change with smaller coins. Always check you are not surpassing the remaining change. +* 在 **selectCandidate()** 方法中,首先选择面额最大的硬币,然后用较小的硬币填充剩余的零钱。在这个过程中,您要一直检查是否超出剩余的零钱。 -#### Solution +#### 解法 -I will provide my solution written in java, so I’m using OOP. +我提供的解法用 Java 编写的,其中用到了面向对象的知识。 ```Java public class Coin { @@ -158,17 +158,17 @@ ArrayList < Coin > greedySchema(ArrayList < Integer > values, int quantity) { } ``` -#### Resources +#### 资源 -If you did like how Greedy algorithms work and now feel the urge to go and investigate more about them, pages like [Hackerrank](https://www.hackerrank.com/) or [Hackerearth](https://www.hackerearth.com/practice/) provide a huge amount of problems to solve, I’m sure you already knew about them but it is always good to mention 😊. +如果您喜欢贪心算法的工作原理,并且想深入研究,请访问 [Hackerrank](https://www.hackerrank.com/) 或者 [Hackerearth](https://www.hackerearth.com/practice/),这里有有很多要解决的问题,我相信您已经对它们有一定了解 😊。 -Sometimes what I personally do is use GitHub as a search engine and simply write the topic I’m looking for [[greedy algorithms](https://github.com/search?q=greedy+algorithm)]. +有时,我个人也会把 GitHub 作为搜索引擎,并简单地写下我寻找的主题 [[贪心算法](https://github.com/search?q=greedy+algorithm)]。 -## Conclusion +## 结论 -So to sum up, Greedy Algorithms are really good even for personal easy projects, they should not take to much to think and they consume little resources. If that wasn’t enough, a lot of interview questions might be easily solved using a good Greedy algorithm, most of the times the memory and complexity requirements are satisfied using either Greedy or Dynamic Programming, but that’s another story 😉. +综上所述,即使对于简单的个人项目,贪心算法也能表现优异,它不需要你花费太多时间去思考,并且只消耗很少的资源。而且,使用贪心算法可以轻松解决很多面试问题。大多数时候,使用贪心或动态规划都可以满足内存和复杂度方面的要求,但这就是另一个话题了 😉。 -Thanks for reading and feel free to comment out anything 😄. +感谢您的阅读,请多多评论哦 😄。 > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。