程序员老鬼

目前70w,接到深圳110w offer,目前坐标成都,自定居成都,有小孩,划得来去吗? 真诚发问

10w听着确实香,但一看坐标成都、有娃、还想定居成都,就不一样了。

70w在成都,已经不是“混口饭吃”了,是能把日子过得挺稳的那种。孩子、房子、老人、生活节奏,全都在这边铺开了。深圳110w,税后多出来那点,先别急着激动,房租、通勤、育儿成本、夫妻异地或者全家搬迁,随便一项都能把人劝冷静。

Image

而且深圳的110w,很多时候买的不是工资,是强度。你去了以后,可能不是“多赚40w”,而是换一套更卷的系统重新开局。小孩转学、家里重新适应,成年人倒还好,扛一扛就过去了,孩子和家庭成本没法只用钱算。

要是单身,我可能会说冲一下。现在这个情况,真得看深圳这个offer能不能稳定三五年,不然就是拿全家折腾,换一段高压体验卡。

70w成都,真的已经挺能打了。

面试题:计数器

计数器这题,别一上来就写数组

这题最容易写歪的地方,不是不会加一,而是默认“计数对象一定是数字,而且范围不大”。

真到题里,计数对象可能是用户 ID、单词、订单状态,也可能是一堆字符串命令。你拿数组硬顶,样例能过,换个 user_98231 这种 key,直接开始拆东墙补西墙。

我一般先看两个点:计数对象是不是连续整数,计数结果会不会减到 0。

如果对象是连续小整数,比如 1 到 100000,那数组没问题。但只要 key 是字符串,或者数字范围很散,老老实实用 HashMap。这玩意儿在这类题里不是“高级写法”,就是正路。

题目可以简化成这样:

ADD apple
ADD apple
ADD orange
DEL apple
GET apple
GET banana

输出:

1
0

这里的坑在 DEL。很多人减完以后还把 apple=0 留在 map 里,后面如果题目要求统计当前有多少种元素,或者遍历所有有效计数,就会出脏数据。

我会这么写:

import java.io.*;
import java.util.*;

publicclassMain{
staticclassCounter{
privatefinal Map<String, Integer> box = new HashMap<>();

voidadd(String key){
            box.put(key, box.getOrDefault(key, 0) + 1);
        }

voidremoveOne(String key){
            Integer old = box.get(key);
if (old == null) return;

if (old == 1) {
                box.remove(key);
            } else {
                box.put(key, old - 1);
            }
        }

intget(String key){
return box.getOrDefault(key, 0);
        }
    }

publicstaticvoidmain(String[] args)throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line;

        Counter counter = new Counter();
        StringBuilder ans = new StringBuilder();

while ((line = br.readLine()) != null && !line.isEmpty()) {
            String[] arr = line.split(" ");
            String op = arr[0];
            String key = arr[1];

if ("ADD".equals(op)) {
                counter.add(key);
            } elseif ("DEL".equals(op)) {
                counter.removeOne(key);
            } elseif ("GET".equals(op)) {
                ans.append(counter.get(key)).append('\n');
            }
        }

        System.out.print(ans);
    }
}

这段代码没写得花,故意的。

算法题里的计数器,核心就三件事:不存在时怎么初始化,删除时怎么处理 0,查询时怎么返回默认值。

getOrDefault 解决的是初始化和查询默认值。remove 解决的是脏计数。别小看这个 remove,很多计数类题后面一扩展,比如“当前还有多少种水果”“找出现次数最多的元素”,如果 0 还躺在里面,结果就会很别扭。

如果题目只要求统计一段文本里每个字符出现次数,也还是同一个思路,只是 key 从字符串变成字符:

Map<Character, Integer> cnt = new HashMap<>();

for (char c : s.toCharArray()) {
    cnt.put(c, cnt.getOrDefault(c, 0) + 1);
}

但这里我不建议一开始就背模板。先判断 key 的形态,再决定数组还是 Map。

数组快,但挑数据。

Map 慢一点,但稳。

计数器这类题,看着像送分题,真正扣分的地方基本都在边界:没出现过的 key、减到 0 的 key、重复查询的 key。把这几个口子堵住,代码就干净了。