「总结」LeetCode 上一行代码就能解决的智力算法题

点击蓝色“五分钟学算法”关注我哟

加个“星标”,天天中午 12:15,一起学算法

「总结」LeetCode 上一行代码就能解决的智力算法题

不知不觉更新了 LeetCode 一百多道题目,今天特意总结 LeetCode 上一行代码就能解决的智力算法题,希望你也能领略算法的魅力。

第一道:除数博弈

题目来源于 LeetCode 上第 1025 号问题:除数博弈。

题目解析

对于这种博弈类的题目,如果没有思路的话我们不妨多举几个例子,尝试着从中找寻规律。

  • 假设 N = 1,爱丽丝没得选择,直接失败,即 鲍勃获胜
  • 假设 N = 2,爱丽丝有选择,她可以选择 x = 1,鲍勃面对的就是 N = 2 - 1 = 1,无法操作,爱丽丝获胜
  • 假设 N = 3,爱丽丝只能选择 x = 1,因为选 x = 2 不满足 3 % 2 = 0,鲍勃面对的就是 N = 3 - 1 = 2,参考上面 N = 2 的情形,此时鲍勃为 N = 2 的先手,鲍勃获胜
  • 假设 N = 4,爱丽丝可以选择 x = 1  来使鲍勃遇到 N = 3 的情况,爱丽丝获胜

貌似有个规律:N 为奇数时, 鲍勃获胜;N 为偶数时, 爱丽丝获胜

是这样吗?

是的。

事实上,无论 N 为多大,最终都是在 N = 2 这个临界点结束的。谁最后面对的是 N = 2 的情形,谁就能获胜(这句话不太理解的话,仔细看看 N = 2、N = 3 这两种情形)。

接下来,我们得知道一个数学小知识:奇数的因子(约数)只能是奇数,偶数的因子(约数)可以是奇数或偶数

千万不要忽略 1 也是因子!

爱丽丝是游戏开始时的先手。

  • 当她面对的 N 为偶数时,她 一定可以 选到一个 N 的奇数因子 x(比如 1 ),将 N – x 这个奇数传给鲍勃;用 N - x 替换黑板上的数字 N ,鲍勃面对的就是奇数 N,只能选择 N 的奇数因子 x,奇数 - 奇数 = 偶数,此时传给爱丽丝的又是偶数。这样轮换下去爱丽丝会遇到 N = 2 的情形,然后获胜;
  • 当爱丽丝遇到的 N 是奇数时,只能传给鲍勃偶数或无法操作 (N = 1) ,无法获胜。

代码实现

//@五分钟学算法
class Solution {
    public boolean divisorGame(int N) {
        return N % 2 == 0;
    }
}

第二道:灯泡开关

题目来源于 LeetCode 上第 319 号问题:灯泡开关。

题目分析

首先,因为电灯一开始都是关闭的,所以某一盏灯最后如果是点亮的,必然要被按奇数次开关。

我们假设只有 6 盏灯,而且我们只看第 6 盏灯。需要进行 6 轮操作对吧,请问对于第 6 盏灯,会被按下几次开关呢?这不难得出,第 1 轮会被按,第 2 轮,第 3 轮,第 6 轮都会被按。

为什么第 1、2、3、6 轮会被按呢?因为 6 = 1×6 = 2×3。一般情况下,因子都是成对出现的,也就是说开关被按的次数一般是偶数次。但是有特殊情况,比如说总共有 16 盏灯,那么第 16 盏灯会被按几次?

16 = 1 × 16 = 2 × 8 = 4 × 4 

其中因子 4 重复出现,所以第 16 盏灯会被按 5 次,奇数次。现在你应该理解这个问题为什么和平方根有关了吧?

不过,我们不是要算最后有几盏灯亮着吗,这样直接平方根一下是啥意思呢?稍微思考一下就能理解了。

就假设现在总共有 16 盏灯,我们求 16 的平方根,等于 4,这就说明最后会有 4 盏灯亮着,它们分别是第 1 × 1 = 1 盏、第 2 × 2=4 盏、第 3 × 3 = 9 盏和第 4 × 4 = 16盏。

我们不是想求有多少个可开方的数吗,4 是最大的平方根,那么小于 4 的正整数的平方都是在 1~16 内的,是会被按奇数次开关,最终亮着的灯。

就算有的 n 平方根结果是小数,强转成 int 型,也相当于一个最大整数上界,比这个上界小的所有整数,平方后的索引都是最后亮着的灯的索引。所以说我们直接把平方根转成整数,就是这个问题的答案。

代码实现

class Solution {
    public int bulbSwitch(int n) {
         return (int)Math.sqrt(n);
    }
}

第三道:3的幂

题目来源于 LeetCode 上第 326 号问题:3的幂。

题目解析

正常的思路是不停地去除以 3,看最后的迭代商是否为 1。这种思路的代码使用到了循环,逼格不够高。

这里取巧的方法 用到了数论的知识:3 的幂次的质因子只有 3

题目要求输入的是 int 类型,正数范围是 0 – 231,在此范围中允许的最大的 3 的次方数为 319 = 1162261467 ,那么只要看这个数能否被 n 整除即可。

代码实现

//@五分钟学算法
class Solution {
    public boolean isPowerOfThree(int n) {
         return n > 0 && 1162261467 % n == 0;
    }
}

第四道:2的幂

题目来源于 LeetCode 上第 231 号问题:2的幂。

题目解析

如果一个数是 2 的次方数的话,那么它的二进数必然是最高位为 1,其它都为 0 ,那么如果此时我们减 1 的话,则最高位会降一位,其余为 0 的位现在都为变为 1,那么我们把两数相与,就会得到 0。

代码实现

//@五分钟学算法
class Solution {
public:
    bool isPowerOfTwo(int n) {
          return n > 0 && ((n & (n - 1)) == 0);
    } 
};

第五道:阶乘后的零

题目来源于 LeetCode 上第 172 号问题:阶乘后的零。

题目解析

题目很好理解,数阶乘后的数字末尾有多少个零。

最简单粗暴的方法就是先乘完再说,然后一个一个数。

事实上,你在使用暴力破解法的过程中就能发现规律:这 9 个数字中只有 2(它的倍数) 与 5 (它的倍数)相乘才有 0 出现

所以,现在问题就变成了这个阶乘数中能配 多少对 2 与 5

举个复杂点的例子:

10!= 【 2 *( 2 * 2 )* 5 *( 2 * 3 )*( 2 * 2 * 2 )*( 2 * 5)】

在 10!这个阶乘数中可以匹配两对 2 * 5 ,所以10!末尾有 2 个 0。

可以发现,一个数字进行拆分后 2 的个数肯定是大于 5 的个数的,所以能匹配多少对取决于 5 的个数。(好比现在男女比例悬殊,最多能有多少对异性情侣取决于女生的多少)。

那么问题又变成了 统计阶乘数里有多少个 5 这个因子

需要注意的是,像 25,125 这样的不只含有一个 5 的数字的情况需要考虑进去。

比如 n = 15。那么在 15! 中 有 35 (来自其中的5, 10, 15), 所以计算 n/5 就可以 。

但是比如 n=25,依旧计算 n/5 ,可以得到 55,分别来自其中的5, 10, 15, 20, 25,但是在 25 中其实是包含 25 的,这一点需要注意。

所以除了计算 n/5 , 还要计算 n/5/5 , n/5/5/5 , n/5/5/5/5 , ..., n/5/5/5,,,/5直到商为0,然后求和即可。

代码实现

//@五分钟学算法
public class Solution {
    public int trailingZeroes(int n) {
        return n == 0 ? 0 : n / 5 + trailingZeroes(n / 5);
    }
}

第六道:Nim 游戏

题目来源于 LeetCode 上第 292 号问题:Nim 游戏。

题目解析

我们解决这种问题的思路一般都是反着思考:

如果我能赢,那么最后轮到我取石子的时候必须要剩下 1~3 颗石子,这样我才能一把拿完。

如何营造这样的一个局面呢?显然,如果对手拿的时候只剩 4 颗石子,那么无论他怎么拿,总会剩下 1~3 颗石子,我就能赢。

如何逼迫对手面对 4 颗石子呢?要想办法,让我选择的时候还有 5~7 颗石子,这样的话我就有把握让对方不得不面对 4 颗石子。

如何营造 5~7 颗石子的局面呢?让对手面对 8 颗石子,无论他怎么拿,都会给我剩下 5~7 颗,我就能赢。

这样一直循环下去,我们发现只要踩到 4 的倍数,就落入了圈套,永远逃不出 4 的倍数,而且一定会输。

代码实现

public class Solution {
   bool canWinNim(int n) {
    // 如果上来就踩到 4 的倍数,那就认输吧
    // 否则,可以把对方控制在 4 的倍数,必胜
    return n % 4 != 0;
    }
}

第七道:石子游戏

题目来源于 LeetCode 上第 877 号问题:石子游戏。

题目解析

显然,亚历克斯总是赢得 2 堆时的游戏。通过一些努力,我们可以获知她总是赢得 4 堆时的游戏。

如果亚历克斯最初获得第一堆,她总是可以拿第三堆。如果她最初取到第四堆,她总是可以取第二堆。第一 + 第三,第二 + 第四 中的至少一组是更大的,所以她总能获胜。

我们可以将这个想法扩展到 N 堆的情况下。设第一、第三、第五、第七桩是白色的,第二、第四、第六、第八桩是黑色的。亚历克斯总是可以拿到所有白色桩或所有黑色桩,其中一种颜色具有的石头数量必定大于另一种颜色的。

因此,亚历克斯总能赢得比赛。

代码实现

class Solution {
    public boolean stoneGame(int[] piles) {
        return true;
    }
}



本文相关阅读推荐:


毕业十年后,我忍不住出了一份程序员的高考试卷

一道腾讯面试题:厉害了我的杯

十大经典排序算法动画与解析,看我就够了!(配代码完全版)

这或许是东半球分析十大排序算法最好的一篇文章

面试官,我会写二分查找法!对,没有 bug 的那种!

看《长安十二时辰》可以了解哪些算法知识

GitHub 标星 3w+,很全面的算法和数据结构知识

「总结」LeetCode 上一行代码就能解决的智力算法题

本文由 程序员小吴 创作,采用 CC BY 3.0 CN协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在先添加作者公众号二维码。
五分钟学算法 » 「总结」LeetCode 上一行代码就能解决的智力算法题

我还会在以下平台发布内容

GitHub 哔哩哔哩