Python技术迷

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

看着是涨薪,其实是在算全家迁徙成本。

成都70w,已经定居,还有小孩,这个盘子其实挺稳了。深圳110w乍一看多40w,确实香,谁看了不心动。但问题是,深圳这地方,钱进来快,出去也挺丝滑。

Image

要是深圳这offer只是工资高一点,岗位没明显上升,未来也不确定,那我真觉得别急着冲。成都70w已经不是普通舒适区了,是挺硬的基本盘。

但要是深圳给的是更大的平台、更高的天花板,后面能打开职业路径,那可以认真算。别只算110减70,得算搬家以后,生活还剩多少,人还累不累。这个账,HR肯定不会帮你算。

算法题:计数器

数组一长,最先暴露问题的不是循环,而是你到底有没有把“出现次数”记清楚。

计数器这类题,看着很水,实际写错的人不少。尤其是那种“找出现次数最多的元素”“判断两个字符串是否由同一批字符组成”“统计缺失数字”的题,第一反应别上复杂算法,先把账记明白。

比如给一组用户行为日志,找访问次数最多的页面:

defmost_visit_page(pages):
    counter = {}

for page in pages:
        counter[page] = counter.get(page, 0) + 1

    hot_page = None
    hot_count = 0

for page, count in counter.items():
if count > hot_count:
            hot_page = page
            hot_count = count

return hot_page, hot_count


pages = [
"/home", "/pay", "/home",
"/login", "/pay", "/pay"
]

print(most_visit_page(pages))

输出:

('/pay', 3)

这段代码没什么花活,就是把每个页面出现几次存下来。这里我一般不急着用 max() 写一行,因为排查时看不清中间状态。线上真出问题,能打印出 counter 比优雅更重要。

如果题目是判断两个字符串是不是“字符频率一致”,比如异位词,计数器也能直接拿下:

defsame_letters(left, right):
if len(left) != len(right):
returnFalse

    bucket = {}

for ch in left:
        bucket[ch] = bucket.get(ch, 0) + 1

for ch in right:
if ch notin bucket:
returnFalse

        bucket[ch] -= 1

if bucket[ch] < 0:
returnFalse

returnTrue


print(same_letters("listen", "silent"))
print(same_letters("hello", "bello"))

这里有个细节,我不喜欢先把两个字符串都统计完再比较。第二个字符串边扫边扣,扣到负数马上返回。数据量小看不出来,数据量一大,这种提前退出就很舒服。

当然,Python 里有现成的 Counter,不是不能用。比赛、面试里可以直接用:

from collections import Counter

defsame_letters_by_counter(left, right):
return Counter(left) == Counter(right)

但我建议刚练算法时,先手写一遍字典计数。你得知道 Counter 背后其实就是哈希表加次数,不然遇到变形题还是懵。

计数器最常见的坑有两个。

第一个是漏掉初始化。很多人写成:

counter[x] += 1

第一次遇到 x 就炸了。所以要么用 get,要么用 defaultdict。

from collections import defaultdict

defcount_items(items):
    counter = defaultdict(int)

for item in items:
        counter[item] += 1

return dict(counter)

第二个坑是只会加,不会减。比如窗口类题目,右边进来要加,左边出去要减,减到 0 还要删掉。不删也能跑,但后面判断种类数量时容易埋雷。

defadd_one(counter, key):
    counter[key] = counter.get(key, 0) + 1


defremove_one(counter, key):
if key notin counter:
return

    counter[key] -= 1

if counter[key] == 0:
del counter[key]

计数器题的核心不在“计数”两个字,而在状态维护。

出现一次,加一。

离开窗口,减一。

减到零,清掉。

如果一道题你发现自己在反复扫描数组,或者每次都 list.count(),这地方我第一眼就不太信。大概率可以用一个字典把重复计算砍掉。

写算法也是这样,别一上来追求多高级。先把账记对,再谈优化。