Python技术迷

面了一个45岁的程序员,他要月薪2万,我同意了,结果面试完把他送到电梯口,他说如果是14薪的话,月薪1.8万也行。

刚看到个贴子,说的是招人面试,一个45岁的程序员,开口要月薪2万,用人方点头了。结果送到电梯口,对方又补了一句:要是14薪,月薪1.8万也行。

Image

网友回帖我看了看,有人觉得这是不专业,有人说中年程序员太会算。我倒觉得,说到底还是双方立场不同。企业算的是性价比,候选人算的是安全感,就像买菜砍价,最后一句不问,心里反而不踏实。

从我的角度看,问题不在“敢不敢谈”,而在“什么时候谈”。提前谈清楚是职业,临走再补一句,确实容易让人不舒服。

总的来说,职场就是明码标价的交换,彼此多点坦诚,少点情绪,其实对谁都好。

面试题:树节点

昨天晚上十一点多,我在公司楼下等外卖,群里那个刚转后端的小李又来问我: “东哥,树节点那个算法题我总是写崩,递归一深我就晕,你给我讲讲呗,用 Python 的。”

那就顺着这个事儿聊一聊树和树节点这个事儿,别太教科书,就按咱平时写代码的思路说。

你可以先把“树”想成家谱,或者公司组织架构。每个人就是一个节点,有可能有下属(孩子节点),有可能没有。

在算法题里最常见的是“二叉树”节点,代码一般长这样(Python):

classTreeNode:
def__init__(self, val=0, left=None, right=None):
        self.val = val      # 节点存的值
        self.left = left    # 左孩子
        self.right = right  # 右孩子

面试官一说“给你一个二叉树的根节点 root”,十有八九就是这个结构。

拿一个最典型的小问题开刀:数一数一共有多少个节点

题目大概是这样: “给定一棵二叉树的根节点 root,返回这棵树一共有多少个节点。”

你别一上来就想复杂的,先想人是怎么数的:

  • 没树?那就是 0 个节点
  • 有一个根?那至少 1 个
  • 然后再去数左边那棵子树有多少个,右边那棵有多少个,加一起就完事

这个思路翻译成递归,就是最标准的写法:

defcount_nodes(root: TreeNode) -> int:
if root isNone:  # 空树没有节点
return0
# 当前节点算 1,左子树一个数,右子树一个数
return1 + count_nodes(root.left) + count_nodes(root.right)

你可以这么理解: 这个函数接的任务就是“帮我把这棵树的节点数算出来”。 它自己只做三件事: 1)先把自己算上(那 1) 2)把左子树交给同一个函数去数 3)把右子树也交给同一个函数去数

这就是递归:大任务拆成结构一模一样的小任务。

这个也巨常见,题目一般是: “求二叉树的最大深度(从根到最深的叶子节点,最多经过多少个节点)。”

人脑逻辑还是老三步:

  • 空树深度是 0
  • 只有一个根节点,深度是 1
  • 其他情况,就是:1(根自己) + 左右子树里更深的那一边

代码也很自然:

defmax_depth(root: TreeNode) -> int:
if root isNone:
return0
    left_depth = max_depth(root.left)
    right_depth = max_depth(root.right)
return1 + max(left_depth, right_depth)

你看,这跟刚才数节点的结构是不是很像? 只是 + 的时候,前面那个题是“左 + 右 + 1”,这里是“max(左, 右) + 1”, 一个要总数,一个要最大值,本质都在“把子树交给递归”这一招上。

再换种写法:用队列来一层一层走(BFS)

有些人天生对递归不太敏感,那可以用“排队”的方式看这棵树,一层一层扫过去,这就是广度优先搜索(BFS)。

比如还是算最大深度,用 BFS 写就是这样:

from collections import deque

defmax_depth_bfs(root: TreeNode) -> int:
if root isNone:
return0

    queue = deque([root])
    depth = 0

while queue:
# 当前这一层有多少个节点
        level_size = len(queue)
# 把这一层的节点全处理掉
for _ in range(level_size):
            node = queue.popleft()
if node.left:
                queue.append(node.left)
if node.right:
                queue.append(node.right)
# 一层处理完就加一层深度
        depth += 1

return depth

你可以脑补一个场景: 第一层就一个根节点排队; 处理完它,把它的孩子都加到队尾; 队列里现在是一整层的节点; 每次“把队列当前这一批全处理完”,就说明一层走完了,深度 +1。

这个写法的好处是,递归栈多深你不用操心,循环结束就是答案。

顺带说一个容易踩的坑:空节点要早点拦住

不管是递归还是 BFS,树题里最容易出 bug 的地方就两个:

  1. 忘记处理 root is None 的情况
  2. 访问 node.left / node.right 的时候没判断 node 自己是不是 None

像刚才这几个函数,开头那句 if root is None: 基本是写树题的肌肉记忆。 很多同学代码跑一半崩了,大多数都是空指针这类小问题,不是算法本身有多难。

最后提一句,树这种数据结构,别把它当成“算法题专用怪物”。 你平时查配置、目录结构、权限继承、甚至一些数据库索引,本质上都在和“树”和“节点”打交道。

-END-

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

🔥虎哥私藏精品🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB