每天一个小算法

753 阅读6分钟

1爬楼梯算法(斐波那契数列)

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶

示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶

示例 3:
输入: 4
输出: 5
解释: 有五种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶+ 1 阶
  2. 1 阶 + 1 阶 + 2 阶
  3. 1 阶 + 2 阶 + 1 阶
  4. 2 阶 + 1 阶 + 1 阶
  5. 2 阶 + 2 阶

很明显,这是一个斐波那契数列,即a[n] = a[n-2] + a[n-1]。n的结果都是由前两个值相加得到的。

闭包实现

假如不考虑空间复杂度的问题,可以将每次结果都缓存起来,这样下次计算就可以省略很多计算步骤。
具体代码实现如下

/**
 * @param {number} n
 * @return {number}
 */
var climbStairs = function(n) {
    let result = {
        0: 1,
        1: 1
    };
    function getData(n){
        if(!result[n]){
           result[n] = getData(n-1) + getData(n-2);//缓存结果
        }
        return result[n]
    }
    return getData(n)
} 

时间复杂度为O(n),空间复杂度也为O(n)。优点是结果可以被缓存,下次计算的时候性能较好,对于只需进行一次求值的需求来说没啥区别。缺点是比较占内存。
代码如下:

/**
 * @param {number} n
 * @return {number}
 */
var climbStairs = function(n) {
    let result = {
        0: 1,
        1: 1
    };
    function getData(n){
        if(!result[n]){
           result[n] = getData(n-1) + getData(n-2);
        }
        return result[n]
    }
    return getData(n)
} 

遍历实现

/**
 * @param {number} n
 * @return {number}
 */
var climbStairs = function(n){
    if (n == 0)
        return 0;
    else if (n == 1)
        return 1;
    else{
        let one = 0,
            two = 1,
            i = 0, 
            ret;
        for(; i < n; i++){
            ret = one + two;
            one = two;
            two = ret;
        }
    return ret;
    }
};

思路是,传入n的值,从0开始计算0-n的值,每次计算的时候将n-1和n-2的值都存起来给下次计算使用。
优点:时间复杂度O(n),空间复杂度为O(1),比用闭包思路节省了不少内存空间

2 帽子颜色

A、B、C、D四个人分别带着一顶帽子。共两顶黑帽子,两顶白帽子。其中D和A、B、C三个隔了一堵不透明的墙。A可以看到B、C帽子的颜色。B可以看到C帽子的颜色。只要能判断自己的帽子颜色,就可以立刻说出来。他们四人沉默了几分钟,这时候一个人说到,他知道自己帽子的颜色是什么了,请问这个人是谁?

解题

答案:B。

解释:因为场上一共只有 2 黑 2 白。所以:

  • 如果有一个人看到了 2 个黑帽子,那么他就可以据此推断出自己是白帽子;
  • 如果他看到了 2 个白帽子,那么他就可以据此推断出自己是黑帽子。\

因为场上只有 A 能够看到 2 个人的帽子,而他没有说话,说明他看到的是一黑一白的帽子。

因为 A 没有说话,所以 B 和 C 知道了他们俩是一黑一白的帽子。\

又因为 B 能看到 C 的帽子颜色,所以 B 就可以知道自己的颜色刚好就是与 C 相反的颜色。

3 老王卖鞋

题目:

老王卖鞋,一双进价30元,老王赔本卖,只卖20元。有个骗子来买,给老王50元假钞。老王未能识别,又没有零钱,把这假钞拿到隔壁铺子的老李换了50元零钱,回来找了骗子30。隔壁很快发现问题,拿假钞来换,老王只好把自己的家底真钞50元换给隔壁。问老王损失了多少钱?

答案:60 元。

解释:这道题容易把人绕晕,我想了一个办法理清楚,就是把老王收到的和拿出去的钱财分开单列,最后收到的和付出的相抵,就是正确答案。\

那么老王收到的东西有:

  • 一张假钞(价值0)
  • 找老李换来的 50 元零钱

老王付出的东西有:

  • 一双鞋。值 30 元。
  • 找给骗子的 30 元零钱。
  • 退给老李的 50 元钱。

所以最终老王亏了 30 + 30 + 50 - 50 = 60 元。

4 赛马

题目:25匹马,5条跑道。要选出最快的前三名,最少要跑几次?

答案:7 次。

解释:先把 25 匹马分成 5 组,我们把这 5 组叫做 A、B、C、D、E 组。这 5 组先各进行一次比赛。这样已经有 5 个小组第一了。

然后 5 个小组第一再比一次,这样就找出了第一名,我们把第一名所在的组叫 A 组,第二名所在的组叫 B 组,第三名所在组叫 C 组。

剩下我们需要判断 A2, A3, B1, B2, C1 这几个到底谁更快。这样就可以找出最快的三匹马。

之所以这样比,是因为前三名有可能有以下四种情况:

  • 前三名都直接在一个小组内。
  • 前三名分别在三个不同小组内。
  • 前三名前两名一组,第三名一组。
  • 前三名第一名在一组,另两名在一组。

比较  A2, A3, B1, B2, C1 就可以把以上情况都考虑到。

衍生问题:如果我们要找出前五名,最少要比几次?

5 抛硬币

题目:有23枚硬币在桌上,10枚正面朝上。蒙住你的眼睛(你无法分清正反),如何分成两组,让两组硬币正面朝上的一样多?

答案:把硬币分成两组,一组 10 个,另一组 13 个。然后把 10 个的那组翻面即可。

解释:因为我们是随机分成两组,所以 10 个那组( 我们叫 A 组),我们假设有 X 个硬币朝上。那么就有 10 - X 个硬币朝下。

另一组(我们叫 B 组),因为 23 个硬币一共只有 10 枚朝上,所以那组有 10 - X 个朝上,剩下的朝下。

我们将 A 组全部翻面,所以朝上朝下个数交换:朝上的变成 10 - X 个,朝下的变成 X 个。

所以 A 组有 10 - X 个朝上,与 B 组朝上的个数相同。

6 水杯

题目:有三个杯子,容量各是10升、7升、3升。把10升的装满水。问:不用别的测量,怎样能将 10 升水分成两个 5 升。

答案:

  • 先往 3 升杯子里面倒满水,然后把水从 3 升杯子中转入 7 升杯子中。
  • 以上步骤重复 3 次。到第三次的时候,7 升杯子装满了,3升杯子中还剩 2 升水。
  • 这个时候把 7 升杯子的水倒回 10 升杯子。
  • 把 3 升杯子剩的 2 升水倒到 7 升杯子。
  • 最后再倒满 3 升杯子,然后把 3 升杯子的水倒到 7 升杯子。
  • 此时,10 升杯子和 7 升杯子里,各有 5 升水。