leader当着我的面把数据清空了~
网友吐槽:leader让我用SQL更新,我刚拿到账号密码,leader一句“你先别动”,估计怕实习生把库当草稿本。他还特地让我站旁边“学习”。
结果我亲眼看他手速起飞,鼠标一飘,点了那个红得发亮的按钮:清空表。 我刚想喊“快回滚!”他又顺手点了 commit,动作丝滑到像在打排位。空气安静三秒,leader发出一声“啊……”,补刀一句:没备份。
我觉得这事最大价值是让我懂了两条:权限别乱给,备份要常做。至于leader的手速,我觉得适合去玩音游,别再碰数据库了。
算法题:移除 9
那天面试一个小朋友,问了他一道特别“缺德”的题,叫啥来着…哦对,《移除 9》。他一听名字以为是链表删节点,结果一看题:给你一个正整数 n,求“不含数字 9”的第 n 个正整数。人当场愣住,说这谁想得出来的题目啊。
你们先别骂出题人,这题其实挺有意思的,关键是要换个脑子看世界。
我一开始跟那小伙也是一样的思路,最土的暴力版:从 1 开始往上加,遇到带 9 的跳过,数到第 n 个停。伪代码差不多这样:
defkth_no_nine(n: int) -> int:
x = 0
count = 0
while count < n:
x += 1
if'9'in str(x):
continue
count += 1
return x
然后你一算复杂度…n 要是到 10^9,人还没数完先猝死在工位上。这个写出来,面试官一般会礼貌地笑一下,然后问你:“有没有再快一点的?”——翻译成人话就是:这个解法可以扔了。
真正有意思的地方在这:你把所有**含 9 的数干掉以后,世界就只剩 0~8 这 9 个数字了。是不是有点眼熟?9 进制啊兄弟。
所以脑洞一开,就变成一个特别优雅的结论:第 n 个不含 9 的十进制数 = n 的“9 进制”,但把每一位当成十进制数字来看。
举个小例子,别光听我嘴瓢:
n = 1,9 进制还是 1,当成十进制就是 1n = 8,9 进制 8,就是 8n = 9,9 进制是 10,当成十进制就是 10(注意这个 10 里没有 9)n = 10,9 进制是 11,十进制看就是 11,一样没 9
你要真按“暴力筛不含 9 的数”列个表,前面也是 1,2,3,...,8,10,11,... 完全对得上。所以这题本质就一句话:把 n 转成 9 进制,然后把那串“9 进制字符串”当成普通数字输出。
面试的时候我一般懒得讲那么学术,就直接边敲边说,你看这么撸一发:
defremove_9(n: int) -> int:
# 把 n 当成 9 进制数的“下标”,转成字符串再当十进制
if n == 0:
return0
res = 0
base = 1
# 相当于手动求 9 进制,不过不写成字符串,直接算成十进制数字
while n > 0:
digit = n % 9# 这一位在 0~8 之间,天然保证没有 9
res += digit * base
n //= 9
base *= 10# 注意这里是 *10,不是 *9,因为我们在拼“十进制的外壳”
return res
这里有两个容易写错的点,我当时还给那个小伙挖了坑:
一个是 base *= 10,很多人下意识写成 9,毕竟我们在搞 9 进制嘛。但你冷静想一下:我们最后要的是一个“长得像十进制”的数字,每一位都是 0~8,所以位权应该是 1,10,100… 自然是乘 10。
另一个是边界 n == 0,虽然原题一般从 1 开始,但写库函数的话,0 这种鬼东西总有人会传进来,不如顺手处理了。实际面试时写不写这句,取决于你老板严不严谨,我这种已经被线上事故教育过的人,看到没处理 0 的代码就浑身难受。
然后有同学就问了:那这个算法到底快在哪儿?你就这么说:这里的复杂度是 **O(log₉ n)**,本质上就是把 n 拆成 9 进制的位数那么多次循环。和刚才那个从 1 数到 n 的 O(n) 比,连数量级都不是一个档次的。面试官要是继续追问,你就指着那行 n //= 9,说“每次都除以 9,次数就是位数”,气势先拿下再说,哈哈。
我后来还顺手写了个对拍的小脚本,验证这个 9 进制骚操作没翻车,顺便给你们也放一眼:
defbrute(n: int) -> int:
x, cnt = 0, 0
while cnt < n:
x += 1
if'9'in str(x):
continue
cnt += 1
return x
deffast(n: int) -> int:
return remove_9(n)
for i in range(1, 2000):
if brute(i) != fast(i):
print("boom at", i)
break
else:
print("all good")
这种小对拍脚本,工作里也超级有用,尤其是你写了个“看起来很聪明”的算法的时候,一定要拿个土办法对一对,不然线上翻车的时候你都不好意思甩锅给别人。
反正这道《移除 9》,真正的难度不在代码,而是在那个“哎?不含 9 的世界刚好是 9 个数字,那不就是 9 进制嘛”的脑洞。你习惯了这种换视角的思路,后面一堆变态题就都顺眼多了。
行了,先这样,我去给同事回个需求,他刚又在问我“这个接口为啥总返回 500”…