diff --git a/README.md b/README.md index bf974bd97c..adb83d2ba9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ These articles are somehow kinds of **Algorithmic Thinking**. All based on LeetC I don't like one liner and confusing, I like clear and easy-understanding. -Gitbook has deployed, will sync with this branch of the repo: https://labuladong.gitbook.io/algo-en/ +**Gitbook** has deployed, will sync with this branch of the repo: https://labuladong.gitbook.io/algo-en/ If you want to clone this repo, please use following command: @@ -26,14 +26,14 @@ This command specifies the `english` branch and limit the depth of clone, get ri * [How to Check the Validation of Parenthesis](interview/valid-parentheses.md) * [How to Find Missing Element](interview/missing_elements.md) * [How to Pick Elements From a Arbitrary Sequence](interview/ReservoirSampling.md) + * [How to use Binary Search](interview/UsingBinarySearchAlgorithm.md) * [How to Scheduling Seats](interview/Seatscheduling.md) * [Union-Find Algorithm in Detail](think_like_computer/Union-find-Explanation.md) * [Union-Find Application](think_like_computer/Union-Find-Application.md) * [Find Sebesquence With Binary Search](interview/findSebesquenceWithBinarySearch.md) - * [如何运用二分查找算法](interview/koko偷香蕉.md) + * [Problems can be sloved by one line](interview/one-line-code-puzzles.md) * [如何寻找缺失和重复的元素](interview/缺失和重复的元素.md) * [如何判断回文链表](interview/判断回文链表.md) - * [一行代码就能解决的算法题](interview/一行代码解决的智力题.md) * II. Data Structure * [Binary Head and Priority Queue](data_structure/binary_heap_implements_priority_queues.md) @@ -59,7 +59,7 @@ This command specifies the `english` branch and limit the depth of clone, get ri * [Interval Scheduling: Interval Merging](think_like_computer/IntervalMerging.md) * [Interval Scheduling: Intersections of Intervals](think_like_computer/IntervalIntersection.md) * [String Multiplication](think_like_computer/string_multiplication.md) - * [烧饼排序](think_like_computer/烧饼排序.md) + * [Pancake Soring Algorithm](think_like_computer/PancakesSorting.md) * [滑动窗口技巧](think_like_computer/滑动窗口技巧.md) * [常用的位操作](think_like_computer/常用的位操作.md) * [信封嵌套问题](think_like_computer/信封嵌套问题.md) diff --git a/SUMMARY.md b/SUMMARY.md index a4d6d43793..bfbff9968f 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -13,14 +13,14 @@ * [How to Check the Validation of Parenthesis](interview/valid-parentheses.md) * [How to Find Missing Element](interview/missing_elements.md) * [How to Pick Elements From a Arbitrary Sequence](interview/ReservoirSampling.md) + * [How to use Binary Search](interview/UsingBinarySearchAlgorithm.md) * [How to Scheduling Seats](interview/Seatscheduling.md) * [Union-Find Algorithm in Detail](think_like_computer/Union-find-Explanation.md) * [Union-Find Application](think_like_computer/Union-Find-Application.md) * [Find Sebesquence With Binary Search](interview/findSebesquenceWithBinarySearch.md) - * [如何运用二分查找算法](interview/koko偷香蕉.md) + * [Problems can be sloved by one line](interview/one-line-code-puzzles.md) * [如何寻找缺失和重复的元素](interview/缺失和重复的元素.md) * [如何判断回文链表](interview/判断回文链表.md) - * [一行代码就能解决的算法题](interview/一行代码解决的智力题.md) * II. Data Structure * [Binary Head and Priority Queue](data_structure/binary_heap_implements_priority_queues.md) @@ -46,7 +46,7 @@ * [Interval Scheduling: Interval Merging](think_like_computer/IntervalMerging.md) * [Interval Scheduling: Intersections of Intervals](think_like_computer/IntervalIntersection.md) * [String Multiplication](think_like_computer/string_multiplication.md) - * [烧饼排序](think_like_computer/烧饼排序.md) + * [Pancake Soring Algorithm](think_like_computer/PancakesSorting.md) * [滑动窗口技巧](think_like_computer/滑动窗口技巧.md) * [常用的位操作](think_like_computer/常用的位操作.md) * [信封嵌套问题](think_like_computer/信封嵌套问题.md) diff --git a/interview/one-line-code-puzzles.md b/interview/one-line-code-puzzles.md new file mode 100644 index 0000000000..3a0ffc9b95 --- /dev/null +++ b/interview/one-line-code-puzzles.md @@ -0,0 +1,118 @@ +# One-line Code Puzzles + +**Translator: [tommytim0515](https://github.com/tommytim0515)** + +**Author: [labuladong](https://github.com/labuladong)** + +This is my summary of three interesting "brain teaser" puzzles from the problems I solved on LeetCode. They can all be solved by algorithmic programming. However, if you think a bit more, you may find the laws of them and figure them out directly. + +### 1. Nim Game + +The game rule is that there is a heap of stones on the table for you and friends to remove. Each of you takes turns to remove the stones and can take at least one and at most three each time. The one who takes the last stone will win the game. See more on [LeetCode page](https://leetcode.com/problems/nim-game/) and [Wikipedia](https://en.wikipedia.org/wiki/Nim). + +Suppose both of you are very clever and have optimal strategies for the game, and you are the first one to take the stone. Write an algorithm to determine whether you can win the game given the number of stones in the heap. (Input a positive integer n, output true or false depending on whether you can win the game). + +For instance, there are 4 stones in total, and the output should be false. Because no matter how many stones you take (1, 2, or 3), the opponent can always take the remaining at once including the last one. You are guaranteed to lose the game. + +First of all, dynamic programming (DP) can be implemented into this problem, because you can find the repeated sub-problems. However, this method would be very complicated as it involves games played between you and your opponent, who are both clever. + +**We usually use contrarian thinking to find a solution of this kind of problems**: + +If I win the game, I need to take the remaining stones (1\\~3 stones) at once. + +How to make this situation come into being? If there are 4 stones remaining when your opponent takes the chance to pick the stones, no matter how he takes the stones, you can always win the game because there will always be 1\~3 stones remaining. + +And how to force your opponent to face the situation when there are 4 stones left? If there are 5\~7 stones remaining by the time you take your turn, you can let your opponent face 4-stone situation. + +Then how to get into a 5\~7 stones situation when you are picking? Let your opponent face 8 stones. No matter how he plans to take the stones, we can win the game because of the remaining 5\~7 stones. + +And so on, we can find out that if n is a multiple of 4, you will fall into the trap and can never win the game. The solution to this problem is very simple: + +```cpp +bool canWinNim(int n) { + // If n is a multiple of 4, then return false + // Otherwise, return true + return n % 4 != 0; +} +``` + +### 2. Stone Game + +The game rule is that you and your friend play a game with piles of stones. The piles of stone are represented by an array, ```piles```. ```pile[i]``` refers to the number of stones in the ith pile. Each turn, a player takes the entire pile of stones from either the beginning or the end of the row. And the winner is the one who gets more stones in the end. See more on [LeetCode page](https://leetcode.com/problems/stone-game/). + +**Suppose both of you are very clever and have optimal strategies for the game**, and you start first. Write an algorithm to determine whether you can win the game given the number of stones in the heap. (Input an array, pile, output true or false depending on whether you can win the game). + +Please pay attention that the number of piles of stones should be even. So both of you get the same number of piles of stones. However, the total amount of stones is odd. So you are not able to get the same number of stones and there must be a winner. + +For instance, `piles=[2, 1, 9, 5]`, you take first, you can choose 2 or 5 and you choose 2. + +`piles=[1, 9, 5]`, your opponent's turn, he or she can choose 1 or 5, and he or she chooses 5. + +`piles=[1, 9]`, your turn, you pick 9. + +Finally, your opponent has no choice but choosing 1. + +In summary, you get $2 + 9 = 11$ stones in total, your opponents gets $5 + 1 = 6$ stones. You win, the return value is true. + +As you can see that it is not always correct to choose the one with larger number of stones. Why you should choose 2 rather than 5 at the first time? Because 9 is behind 5. You will lose the game for giving the pile of 9 stones to the opponent for chasing a moment's gain. + +And that is why we need to emphasize that both the players are clever. The algorithm is also to determine whether you can win with best decisions. + +The problem also involves playing a game by the two players. It is very complicated to use "brute force" method like dynamic programming (DP). And if we think a bit deeper we will find out that + +```java +boolean stoneGame(int[] piles) { + return true; +} +``` + +Why we can write like this? There are two important conditions about the problem: the number of the pile of stones is even, while the total number of the stone is odd. These two conditions seem to increase the fairness of the game, while they indeed let it become a "leet-cutting" game. For instance, suppose the indexes of piles of stones are 1, 2, 3, 4 from start to end sequentially when `pile=[2, 1, 9, 5]`. + +If we divide these four piles of stones into two groups according to whether the index is even or not, which equals 1, 3 in a group and 2, 4 in another group. The numbers of stones of these two groups are different as the total number of stones is odd. + +As the first one to take the stones, you can decide to take all the even group or all the odd group at once. + +In the beginning, you can choose the 1st pile or the 4th pile. If you want an even group, you can take the 4th pile. So that your opponent can only choose the 1st one or the 3rd one. No matter how he takes, you can choose the 2nd pile after that. Similarly, if you choose the 1st pile which is in the odd group, your opponent can only choose 2nd or 4th pile, no matter how he chooses, you can get the 3rd pile. + +In other words, you can observe all the strategies at the first try. You can win the game by observing which group of stones is more, even or odd. Knowing this loophole, you can play a trick on your friend who doesn't know this. + +### 3. Bulb Switcher + +The description of the problem: there are n bulbs in a room and they are initially turned off. Now we need to do n operations: + +1. Flip all the lights. + +2. Flip lights with even numbers. + +3. Flip the bulb whose number is a multiple of 3 (e.g. 3, 6, 9, ... and 3 is off while 6 is on). + +For the i-th round, you toggle every i bulb. For the n-th round, you only toggle the last bulb. + +You need to find how many bulbs are on after n rounds. See more on [LeetCode page](https://leetcode.com/problems/bulb-switcher/). + +We can simulate the condition with a boolean array, then count the result. However, this method is not smart enough. The best solution is as follows: + +```java +int bulbSwitch(int n) { + return (int)Math.sqrt(n); +} +``` + +What? What does this have to do with square roots? It's actually a pretty neat solution, and it's hard to figure out if nobody tells you how to do it. + +First, because the lights are always off at the beginning, a certain light must be flipped an odd number of times if it is turned on at the end. + +Let's say we only have six lights, and we're only looking at the sixth light and it's going to take six turns, right? How many times is the switch going to be pressed for the sixth light? It's not difficult to see that its switch will be pressed at the 1st, 2nd, 3rd and 6th round. + +Why the light will be flipped at these rounds? Because $6=1\times6=2\times3$. In general, the factors come in pairs, which means that the number of times the switch is pressed is usually even, but in a special case, if there are 16 lights, how many times will the 16th light be flipped? + +$16=1\times16=2\times8=4\times4$ + +The factor 4 repeats, so the 16th light will be flipped 5 times which is odd, and now you understand the relationships to the square root, right? + +But, we're going to figure out how many lights are on at the end, and what does that mean by square root? Just think about it a little bit. + +Suppose we have 16 lights, and we take the square root of 16, which is equal to 4, and that means we're going to end up with 4 lights on. The lights are $1\times1=1$, $2\times2=4$, $3\times3=9$, and $4\times4=16$. + +Some square root of n turns out to be a decimal. However, converting them to integers is the same thing as getting all the integers smaller than a certain integer upper bound, and the square roots of these numbers are the index of the lights on at last. so just turn the square root into an integer, that's the answer to the question. + diff --git "a/interview/\344\270\200\350\241\214\344\273\243\347\240\201\350\247\243\345\206\263\347\232\204\346\231\272\345\212\233\351\242\230.md" "b/interview/\344\270\200\350\241\214\344\273\243\347\240\201\350\247\243\345\206\263\347\232\204\346\231\272\345\212\233\351\242\230.md" deleted file mode 100644 index 446fda7eb8..0000000000 --- "a/interview/\344\270\200\350\241\214\344\273\243\347\240\201\350\247\243\345\206\263\347\232\204\346\231\272\345\212\233\351\242\230.md" +++ /dev/null @@ -1,120 +0,0 @@ -# 一行代码就能解决的算法题 - -下文是我在 LeetCode 刷题过程中总结的三道有趣的「脑筋急转弯」题目,可以使用算法编程解决,但只要稍加思考,就能找到规律,直接想出答案。 - -### 一、Nim 游戏 - -游戏规则是这样的:你和你的朋友面前有一堆石子,你们轮流拿,一次至少拿一颗,最多拿三颗,谁拿走最后一颗石子谁获胜。 - -假设你们都很聪明,由你第一个开始拿,请你写一个算法,输入一个正整数 n,返回你是否能赢(true 或 false)。 - -比如现在有 4 颗石子,算法应该返回 false。因为无论你拿 1 颗 2 颗还是 3 颗,对方都能一次性拿完,拿走最后一颗石子,所以你一定会输。 - -首先,这道题肯定可以使用动态规划,因为显然原问题存在子问题,且子问题存在重复。但是因为你们都很聪明,涉及到你和对手的博弈,动态规划会比较复杂。 - -**我们解决这种问题的思路一般都是反着思考**: - -如果我能赢,那么最后轮到我取石子的时候必须要剩下 1~3 颗石子,这样我才能一把拿完。 - -如何营造这样的一个局面呢?显然,如果对手拿的时候只剩 4 颗石子,那么无论他怎么拿,总会剩下 1~3 颗石子,我就能赢。 - -如何逼迫对手面对 4 颗石子呢?要想办法,让我选择的时候还有 5~7 颗石子,这样的话我就有把握让对方不得不面对 4 颗石子。 - -如何营造 5~7 颗石子的局面呢?让对手面对 8 颗石子,无论他怎么拿,都会给我剩下 5~7 颗,我就能赢。 - -这样一直循环下去,我们发现只要踩到 4 的倍数,就落入了圈套,永远逃不出 4 的倍数,而且一定会输。所以这道题的解法非常简单: - -```cpp -bool canWinNim(int n) { - // 如果上来就踩到 4 的倍数,那就认输吧 - // 否则,可以把对方控制在 4 的倍数,必胜 - return n % 4 != 0; -} -``` - - -### 二、石头游戏 - -游戏规则是这样的:你和你的朋友面前有一排石头堆,用一个数组 piles 表示,piles[i] 表示第 i 堆石子有多少个。你们轮流拿石头,一次拿一堆,但是只能拿走最左边或者最右边的石头堆。所有石头被拿完后,谁拥有的石头多,谁获胜。 - -**假设你们都很聪明**,由你第一个开始拿,请你写一个算法,输入一个数组 piles,返回你是否能赢(true 或 false)。 - -注意,石头的堆的数量为偶数,所以你们两人拿走的堆数一定是相同的。石头的总数为奇数,也就是你们最后不可能拥有相同多的石头,一定有胜负之分。 - -举个例子,`piles=[2, 1, 9, 5]`,你先拿,可以拿 2 或者 5,你选择 2。 - -`piles=[1, 9, 5]`,轮到对手,可以拿 1 或 5,他选择 5。 - -`piles=[1, 9]` 轮到你拿,你拿 9。 - -最后,你的对手只能拿 1 了。 - -这样下来,你总共拥有 $2 + 9 = 11$ 颗石头,对手有 $5 + 1 = 6$ 颗石头,你是可以赢的,所以算法应该返回 true。 - -你看到了,并不是简单的挑数字大的选,为什么第一次选择 2 而不是 5 呢?因为 5 后面是 9,你要是贪图一时的利益,就把 9 这堆石头暴露给对手了,那你就要输了。 - -这也是强调双方都很聪明的原因,算法也是求最优决策过程下你是否能赢。 - -这道题又涉及到两人的博弈,也可以用动态规划算法暴力试,比较麻烦。但我们只要对规则深入思考,就会大惊失色:只要你足够聪明,你是必胜无疑的,因为你是先手。 - -```java -boolean stoneGame(int[] piles) { - return true; -} -``` - -这是为什么呢,因为题目有两个条件很重要:一是石头总共有偶数堆,石头的总数是奇数。这两个看似增加游戏公平性的条件,反而使该游戏成为了一个割韭菜游戏。我们以 `piles=[2, 1, 9, 5]` 讲解,假设这四堆石头从左到右的索引分别是 1,2,3,4。 - -如果我们把这四堆石头按索引的奇偶分为两组,即第 1、3 堆和第 2、4 堆,那么这两组石头的数量一定不同,也就是说一堆多一堆少。因为石头的总数是奇数,不能被平分。 - -而作为第一个拿石头的人,你可以控制自己拿到所有偶数堆,或者所有的奇数堆。 - -你最开始可以选择第 1 堆或第 4 堆。如果你想要偶数堆,你就拿第 4 堆,这样留给对手的选择只有第 1、3 堆,他不管怎么拿,第 2 堆又会暴露出来,你就可以拿。同理,如果你想拿奇数堆,你就拿第 1 堆,留给对手的只有第 2、4 堆,他不管怎么拿,第 3 堆又给你暴露出来了。 - -也就是说,你可以在第一步就观察好,奇数堆的石头总数多,还是偶数堆的石头总数多,然后步步为营,就一切尽在掌控之中了。知道了这个漏洞,可以整一整不知情的同学了。 - -### 三、电灯开关问题 - -这个问题是这样描述的:有 n 盏电灯,最开始时都是关着的。现在要进行 n 轮操作: - -第 1 轮操作是把每一盏电灯的开关按一下(全部打开)。 - -第 2 轮操作是把每两盏灯的开关按一下(就是按第 2,4,6... 盏灯的开关,它们被关闭)。 - -第 3 轮操作是把每三盏灯的开关按一下(就是按第 3,6,9... 盏灯的开关,有的被关闭,比如 3,有的被打开,比如 6)... - -如此往复,直到第 n 轮,即只按一下第 n 盏灯的开关。 - -现在给你输入一个正整数 n 代表电灯的个数,问你经过 n 轮操作后,这些电灯有多少盏是亮的? - -我们当然可以用一个布尔数组表示这些灯的开关情况,然后模拟这些操作过程,最后去数一下就能出结果。但是这样显得没有灵性,最好的解法是这样的: - -```java -int bulbSwitch(int n) { - return (int)Math.sqrt(n); -} -``` - -什么?这个问题跟平方根有什么关系?其实这个解法挺精妙,如果没人告诉你解法,还真不好想明白。 - -首先,因为电灯一开始都是关闭的,所以某一盏灯最后如果是点亮的,必然要被按奇数次开关。 - -我们假设只有 6 盏灯,而且我们只看第 6 盏灯。需要进行 6 轮操作对吧,请问对于第 6 盏灯,会被按下几次开关呢?这不难得出,第 1 轮会被按,第 2 轮,第 3 轮,第 6 轮都会被按。 - -为什么第 1、2、3、6 轮会被按呢?因为 $6=1\times6=2\times3$。一般情况下,因子都是成对出现的,也就是说开关被按的次数一般是偶数次。但是有特殊情况,比如说总共有 16 盏灯,那么第 16 盏灯会被按几次? - -$16=1\times16=2\times8=4\times4$ - -其中因子 4 重复出现,所以第 16 盏灯会被按 5 次,奇数次。现在你应该理解这个问题为什么和平方根有关了吧? - -不过,我们不是要算最后有几盏灯亮着吗,这样直接平方根一下是啥意思呢?稍微思考一下就能理解了。 - -就假设现在总共有 16 盏灯,我们求 16 的平方根,等于 4,这就说明最后会有 4 盏灯亮着,它们分别是第 $1\times1=1$ 盏、第 $2\times2=4$ 盏、第 $3\times3=9$ 盏和第 $4\times4=16$ 盏。 - -就算有的 n 平方根结果是小数,强转成 int 型,也相当于一个最大整数上界,比这个上界小的所有整数,平方后的索引都是最后亮着的灯的索引。所以说我们直接把平方根转成整数,就是这个问题的答案。 - - - -坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章: - -![labuladong](../pictures/labuladong.jpg)