今天遇到一件挺让人感慨的事。公司面试了一位45岁的技术架构,履历和能力没得说,技术面全票通过,但最后在薪资这关,发生了意想不到的转折。

对方开价25K,结合其腾讯T线背景和带团队的经验,这个要价其实很实在。领导那边好不容易沟通下来,批了这个预算。本以为板上钉钉,结果送他到电梯口时,他许是看出了我片刻的犹豫,主动改口说,月薪1.8万也能接受,只要底薪别压得太低就行。
那一刻的心情很复杂。一个技术和项目经验能“吊打”我们整个部门的人,却开始主动向下调整自己的预期。有网友总结,35岁之后找工作,最先失去的可能不是技术价值,而是那份议价的“体面”。这话听着刺耳,但在某些场景下,却显得格外真实。招聘方在评估时,年龄与成本的天平,时常会微妙地倾斜。
聊完这些,我们不妨切回技术本身。这次面试中,考了一道经典的算法题:原子的数量(LeetCode 726)。这道题很多人的第一反应是用正则或者各种字符串切片,但写着写着就容易乱,尤其是面对 K4(ON(SO3)2)2 这种嵌套复杂的化学式。
手算可能还能理清,但一旦要写成代码,括号嵌套、数字倍率、原子名大小写,这三者纠缠在一起,代码结构就容易变得混乱。解这道题,关键不是先想“怎么解析字符串”,而是先想清楚两件事:谁负责记录当前层级的原子数量,以及谁负责把括号后的倍率乘回去。
本质上,这不是一道数学题,而是一个小型的语法解析题。最顺手的解法就是使用 栈 + 哈希表。
核心思路
- 遇到左括号
(:意味着进入新的一层。将当前层的计数哈希表压入栈中,然后创建一个新的空哈希表,用于记录新层内的原子数量。
- 遇到右括号
):意味着当前层结束。紧接着读取右括号后面的数字作为倍率(若无数字则默认为1)。将当前层哈希表中所有原子的数量乘以这个倍率,然后合并回栈顶的上一层哈希表中。
- 遇到原子(如
Mg, He, O):读取完整的原子名(大写字母开头,后跟零个或多个小写字母)。接着读取紧随其后的数字(若无则默认为1),并将数量累加到当前层的哈希表中。
解析过程中的难点在于原子名和数字的读取,以及括号嵌套倍率的正确处理。代码实现上,追求清晰远比追求奇技淫巧重要。一个指针顺序扫描的写法就足够清晰:
from collections import defaultdict
class Solution:
def countOfAtoms(self, formula: str) -> str:
stack = []
cur = defaultdict(int)
i, n = 0, len(formula)
while i < n:
ch = formula[i]
if ch == '(':
stack.append(cur)
cur = defaultdict(int)
i += 1
elif ch == ')':
i += 1
num = 0
while i < n and formula[i].isdigit():
num = num * 10 + int(formula[i])
i += 1
num = num or 1
top = stack.pop()
for atom, cnt in cur.items():
top[atom] += cnt * num
cur = top
else:
start = i
i += 1
while i < n and formula[i].islower():
i += 1
atom = formula[start:i]
num = 0
while i < n and formula[i].isdigit():
num = num * 10 + int(formula[i])
i += 1
cur[atom] += num or 1
ans = []
for atom in sorted(cur.keys()):
ans.append(atom)
if cur[atom] > 1:
ans.append(str(cur[atom]))
return ''.join(ans)
这段代码里,有两个细节最容易出错:
- 右括号
) 后面的倍率读取:很多人一看到右括号就急着合并上一层,容易漏掉 )2、)12 这种情况。这个数字必须完整读出来,因为它要乘以其所在的整个括号层内的所有原子计数。
- 原子名的读取:像
Mg、He 这种,不能只读一个大写字母就结束。必须用一个循环,把后面连续的(如果存在)小写字母一起“吃掉”,直到遇到非小写字母字符为止。
为了更直观地理解这个过程,我们用 Mg(OH)2 走一遍流程:
- 读到
Mg,当前层记录为 {'Mg': 1}。
- 遇到
(,将当前表 {'Mg': 1} 压栈,并新建一个空表 {}。
- 在新层里,先后读到
O 和 H,记录为 {'O': 1, 'H': 1}。
- 遇到
),读取后面的倍率 2。
- 将当前层原子数量乘以倍率,得到
{'O': 2, 'H': 2}。
- 将这一层合并回栈顶的上一层,得到
{'Mg': 1, 'O': 2, 'H': 2}。
- 最后按字典序排序输出:
H2MgO2。
这道题本身并不难,难点在于如何把解析过程组织得清晰、健壮。如果你一开始就用一堆散乱的 if-else 去拼凑,最后很可能栽在嵌套括号的倍率或多位数字的处理上。栈在这里的作用不是炫技,它就是一个最自然的“现场暂存区”,用来管理不同层级的上下文状态。
所以,面对这类算法题,或者任何需要解析嵌套结构的场景,先别急着去抠最终输出的字符串格式。把“进入/退出层级”和“应用倍率”这两个核心动作的数据结构想清楚,代码的脉络自然就顺畅了。多练习这类题目,能有效锻炼你对栈和哈希表这两种基础数据结构的运用能力,这正是技术面试中常考的重点。在像云栈社区这样的开发者平台上,大家也常常交流这类题目的解法和调试心得,这对于巩固基础、应对面试非常有帮助。
|