Python技术迷

大专就不配月薪3万么,我月薪是28000,总包在50左右,而他薪资预估在18000左右,还是211高材生,于是开始对我充满了敌意。

刚看到个贴子,说一位大专博主月薪2.8w,总包50左右,被一个预估1.8w的211同事各种阴阳,意思是“大专也配拿这么高?”还对人充满敌意。

Image

网友回复我也瞄了瞄,有说211眼红的,有质疑工资造假的,还有感叹“学历不值钱了”的。

我觉得这事吧,关键真不在“大专”和“211”这俩标签上,而是在市场愿不愿意为你那点本事付钱。学历是门票,薪资是身价,两张票混着算,只会越想越不平衡。很多人读书时比的是分数,工作后比的是价值,路径不一样,但赛道是同一条。

换个角度想,如果一个人最大的本事,就是盯着别人的工资生气,那说明他缺的不是名校,而是心态和能力

算法题:最大层内元素和

昨天晚上十一点多,我在公司楼下拿着奶茶吹风,我们组那个小李一边啃着炸鸡一边跟我说:“东哥,我刷到一道树的题,叫啥最大层内元素和,脑子一下卡壳了,你给我讲讲呗?”我一听名字就笑了,这玩意说难不难,说简单也能劝退一批人,主要是对“层”这个概念不够有感觉。

先把题目翻译成人话哈,就是这样一个意思: 给你一棵二叉树,树是有很多层的嘛,根节点是第1层,它的左右孩子是第2层,再往下是第3层……现在每个节点上有个整数值(可以是负数),让你算:哪一层所有节点的值加起来最大?最后返回“层号”,不是返回那个和本身。

随便举个迷你例子,你脑补一下:

  • 第1层:节点值 [1],和 = 1
  • 第2层:节点值 [7, 0],和 = 7
  • 第3层:节点值 [7, -8],和 = -1

那最大的是第2层,答案就是 2。就这么点事,对吧,听着不吓人。

那怎么干呢?很多同学一上来就想着“递归,递归一把梭”,但是递归如果没想清楚要统计啥,很容易写成那种“看着很优雅,实际上啥都没做对”的那种算发…算法。这里其实有个特别自然的思路:既然问“每一层”的和,那我就一层一层地去走不就完事了?

一层一层地走这个动作,在二叉树里有个固定叫法:层序遍历。实现层序遍历最经典的工具就是队列,你可以脑补成排队打饭的窗口:先来的先处理,处理的时候再把它的孩子丢到队尾,等下次处理。

整个过程大概是这样:

  1. 一开始队列里只有 root 根节点,当前层 level = 1。
  2. 每次先记住这一层有多少个节点 size = len(queue),然后把这 size 个依次弹出来,顺便把它们的值加到 cur_sum 里。
  3. 弹每个节点的时候,把它的 left、right 不为空的孩子,塞回队列。这样下次循环的时候,队列里装的就是“下一层”的所有节点。
  4. 每一层遍历完,就拿 cur_sum 和历史最大和 best_sum 比一比,大就更新一下,同时记下是第几层。
  5. level 每轮 +1,最后返回 best_level。

思路就这样,真的没啥花活。用 Python 写一下,丢给小李看的是这个版本:

from typing import Optional
from collections import deque

classTreeNode:
def__init__(self, val: int = 0, left: 'Optional[TreeNode]' = None, right: 'Optional[TreeNode]' = None):
        self.val = val
        self.left = left
        self.right = right

classSolution:
defmaxLevelSum(self, root: Optional[TreeNode]) -> int:
ifnot root:
# 没有节点,看题目要求,一般不会给空树
return0

        queue = deque([root])
        level = 1# 当前正在遍历的层
        best_level = 1# 目前为止和最大的层
        best_sum = root.val  # 目前为止最大的层和

while queue:
            size = len(queue)
            cur_sum = 0

# 把当前层的节点都处理掉
for _ in range(size):
                node = queue.popleft()
                cur_sum += node.val

if node.left:
                    queue.append(node.left)
if node.right:
                    queue.append(node.right)

# 当前层算完,和出来了,和历史最大比一下
if cur_sum > best_sum:
                best_sum = cur_sum
                best_level = level

            level += 1

return best_level

你看这个代码,其实就围绕“队列 + 当前层节点个数”这俩点在转。里面有几个小细节,你注意下就行:

一个是最佳答案的初始化,我这里直接用 root.val,当成“第一层的和”。这样后面比较时逻辑很清楚:只要后面的层比它大,就更新。顺带还能自然地处理“全是负数”那种树,不会出现默认 0 把真正的负数层压过去的情况,这一点很多人第一次写会被坑。

第二个是 queue 的使用方式: 每一轮 while,size = len(queue) 这一步是“锁当前层”;for 循环只循环 size 次,保证你这一轮只处理这一层的节点,孩子全部进队列,但不会在本轮被处理,这样“层和层之间”就天生分隔开了。

顺便说一句时间复杂度这种东西,面试官爱问。这个算法每个节点就进队列、出队列各一次,做常数次加法、判断,整体就是 O(n),空间开了一个队列,最坏节点都在同一层,也是 O(n)。说到这里基本就够用了。

当然,有人会问:“这题能不能用 DFS 啊?我就想写递归。”也是可以的,你可以搞一个字典 level_sum[level],递归的时候把当前节点值加到对应层上,最后再在这个字典里找最大值对应的层。思路是通的,只是实现上比 BFS 多点心智负担,要考虑递归层数、栈深度啥的,我一般让新人先把 BFS 写明白,再去玩 DFS 花式写法。

我们当时在楼下聊完,小李回去敲了一遍,运行过了之后给我发消息说:“原来就这啊,我刚开始还以为是什么高深动态规划。”其实很多树题都这样,你只要对“遍历方式”这几个套路熟:前序、中序、后序、层序,再把队列、栈这几个基本数据结构用顺手,剩下就是一点点业务包装。

行了,不说了,我外卖到了,先吃口饭再刷两道题,不然今天又要拖延了…

-END-

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

🔥虎哥私藏精品🔥

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