程序员老鬼

Leader今年40了,依然在一线写核心代码,我问他担不担心被优化, 他笑着说:如果你到了35岁,还要跟25岁的人拼手速~

刚看到个贴子,说有网友吐槽,他们组的 leader 都快 40 了,还天天在一线扣核心代码,业余时间研究 Go 源码。

楼主就问他:不怕哪天被优化吗?那位 leader 就笑,说大概意思是:如果你三十五岁了,还只能靠和二十五岁的人拼熬夜、拼键盘声,迟早会被换掉;但你要是能搞定别人搞不定的问题,公司就舍不得动你。

Image

怎么说呢,我更认同这种思路:年龄大了就别再卷体力,得开始卷“不可替代性”。能顶住关键故障、能拍板技术方案、能把业务和技术串起来,这些才是真正的护身符。

对年轻人也是个提醒:别老想着“熬到 35 就危险”,与其吓自己,不如早点训练自己解决难题的能力。

说到底,职场不看身份证,只看你值不值钱。焦虑没用,把自己打磨成“离了你事就黄”的那个人,比什么都实在。

面试题:森林中的兔子

这个题名字听着挺可爱,叫“森林中的兔子”,但刚看到的时候,很多人都是一脸懵:这玩意儿跟计数有什么关系?

先把场景讲清楚哈。 有一片森林,里面一堆兔子,你抓了一些出来问它们:“你们这颜色的兔子有多少只?” 每只兔子只会回答一个非负整数 x,意思是:还有 x 只跟我颜色一样的兔子。注意,是“还有”,不是“总共”。

那题目给你的,就是这些兔子的回答数组 answers,比如 [1, 1, 2],你的任务是:森林里最少有多少只兔子,跟这些回答不冲突。

你先想一个极端简单的: 如果有一只兔子说 0,它的意思是:没有别的同色兔子了,那这种颜色就只能有 1 只,对吧?所以答案里面每个 0,其实就是固定贡献一只。

麻烦的在 >0 的这些。 比如好几个兔子都说 1,那每只说 1 的兔子,都在暗示:“我这颜色总共 2 只(我 + 1 只同伴)”。 但是,你拿到的这些说 1 的兔子,有可能属于同一种颜色,也可能属于不同颜色,你又分不出来谁是谁,只能算“最少情况”。

那怎么办呢?只能按回答分组来算下限。

举个例子慢慢来,你看这样一个数组:

[1, 1, 1, 1]

所有兔子都说:还有 1 只跟我同色。 那一个颜色组里,最多允许几只说 1 的兔子?答案是 1 + 1 = 2 只。 如果超过 2,只能再开一组新颜色了。

这 4 只兔子,可以怎么分?

  • 前两只:一种颜色(一共 2 只)
  • 后两只:另一种颜色(再 2 只)

所以最少也得有 4 只兔子。你会发现:

对于某个回答值 x, 这个值出现了 cnt 次, 每组颜色最多装 x + 1 只, 那最少需要的颜色组数就是:groups = ceil(cnt / (x + 1))。

整数运算里,ceil(a / b) 很常见,可以写成:

(a + b - 1) / b

算出有多少组之后,每一组都代表 x + 1 只同色兔子,所以这部分的贡献就是:

groups * (x + 1)

把所有不同 x 的贡献加起来,就是答案。

来,我们用这个思路,走一遍经典例子 [1, 1, 2]:

  • 回答 1 的一共有 2 只

    • x = 1,cnt = 2,groupSize = 2
    • groups = ceil(2 / 2) = 1
    • 说明可以假装它们都一个颜色,一组就够,总共 2 只
  • 回答 2 的一共有 1 只

    • x = 2,cnt = 1,groupSize = 3
    • groups = ceil(1 / 3) = 1
    • 意味着这一种颜色最少要 3 只(虽然你目前只看到了 1 只,剩下两只在森林里没被你抓到)

所以总数 = 2 + 3 = 5,这就是 LeetCode 那个示例答案。

那代码怎么写比较干净?用 Java 其实就几步:

  1. 先统计每种回答 x 出现了多少次,Map<Integer, Integer> 搞定;

  2. 遍历这个 map,对每个 (x, cnt):

  • 组大小 groupSize = x + 1
  • 组数 groups = (cnt + groupSize - 1) / groupSize
  • 把 groups * groupSize 加到结果里;
  • 返回结果。

  • 贴一份完整代码,你直接能跑的那种:

    import java.util.HashMap;
    import java.util.Map;

    publicclassSolution{

    publicintnumRabbits(int[] answers){
    if (answers == null || answers.length == 0) {
    return0;
            }

            Map<Integer, Integer> countMap = new HashMap<>();
    for (int ans : answers) {
                countMap.put(ans, countMap.getOrDefault(ans, 0) + 1);
            }

    int res = 0;
    for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
    int x = entry.getKey();       // 兔子回答的数字
    int cnt = entry.getValue();   // 有多少只兔子给了这个回答

    int groupSize = x + 1;        // 一种颜色最多有几只
    // 需要多少组这种颜色,向上取整
    int groups = (cnt + groupSize - 1) / groupSize;

                res += groups * groupSize;
            }

    return res;
        }

    // 随便写个 main 做个简单测试
    publicstaticvoidmain(String[] args){
            Solution s = new Solution();
            System.out.println(s.numRabbits(newint[]{1, 1, 2})); // 5
            System.out.println(s.numRabbits(newint[]{0, 0, 1, 1, 1})); // 自己可以算一下
        }
    }

    稍微提一句复杂度:

    • 遍历一遍 answers 统计,O(n);
    • 遍历一遍 map 的键值对,最多也是 O(n); 总体时间复杂度就是 O(n),空间复杂度 O(n)(map 存所有不同答案的计数)。

    这个题的“坑点”,其实主要就是那个“没看到的兔子也要算上”。 你只看见一只说 2 的兔子,也得按 3 只算,因为它代表着“我这一颜色一共 3 只,剩下两只现在不在你手里而已”。

    只要想清楚“按回答分组、每组最多装 x + 1 只”的这个模型,这题就一下子顺了。

    有空你可以自己构造几个数组,手算一下再拿上面那个 main 跑一跑,对这个题的感觉就比较稳了。

    -END-

    我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

    最后给大家分享一份不错的副业资料,点击下方公众号,回复关键字: 副业 领,也可以链接我微信:hls404