Python技术迷

被裁后的第3个月,面试官问我空窗期在干嘛。 我说:"跑外卖。"他愣住了。我接着说:"送了1278单,超时率0.3%,差评0条

刚看到个贴子,说有网友被裁第3个月去面试,问空窗期干嘛,他很老实说去送外卖,然后现场气氛突然安静了。

Image

我觉得这事吧,最扎心的不是送外卖,而是“体面”这俩字。很多人嘴上说职业不分贵贱,真轮到自己或者员工去跑外卖,心里那道坎一下就出来了。

可换个角度想,失业了不躺平,顶着压力出去跑单,每天风里来雨里去,这执行力、抗压能力、时间管理,哪样不是职场刚需?

我倒觉得,送外卖完全可以讲,但话术要升级一下:把“跑外卖”讲成“为了维持收入选择高强度的服务类工作,同时复盘职业规划”,再顺手亮个数据,效果就不一样了。

算法题:两数相加

昨天晚上十一点多,我在公司楼下等外卖,手机快没电,人也快没电,小李突然在群里丢过来一句: “东哥,那个两数相加的链表题,你是怎么写的?我老写成大数加法用 int,直接爆了……”

我当时整个人懵了一下,又好气又好笑,这题你们是不是面试被问烂了那个。

比如 342 + 965,你肯定是这么算的: 2 + 5 = 7,记 7; 4 + 6 = 10,写 0 进 1; 3 + 9 + 1(进位) = 13,写 3 再进 1,最后得到 1307。

注意一个细节,你是从个位开始往前算的,不是从最高位。这题其实就完全是把这个动作,搬到链表上去。

面试题里的规则大概是这样: 两个非空链表,每个节点存一位数,低位在前,高位在后。 比如数字 342 存成:2 -> 4 -> 3965 就是:5 -> 6 -> 9然后要你返回一个新的链表,表示它们的和,也是低位在前那种。

小李原来的写法是,直接把俩数从链表里读出来,拼成字符串,再转成 int 去相加,我就问他:

“你想象一下,面试官给你俩 1000 位的小数,你一个 int 顶得住?long long 也不行啊兄弟。”

所以这个题的关键其实就一句话:别把它当“数字加法”,就当“链表逐位相加”。每一位自己算,顺手带上一个进位变量,和你纸上算是一个套路。

脑子里先有个画面: 你一只手指着链表 l1 的当前节点,一只手指着 l2 的当前节点,兜里揣着一个 carry(进位),默认是 0。

每一步就干三件小事:

  1. 把两个节点的值拿出来,没有节点就按 0 算。
  2. sum = x + y + carry
  3. 当前位是 sum % 10,新的进位是 sum // 10

然后指针往后挪。一直走到两个链表都空了,还要看看 carry 里是不是还有 1,要的话再补一个节点。就这么碎碎念地一直加下去。

我一般会先写个最小的链表节点定义,Python 里就这样:

classListNode:
def__init__(self, val=0, next=None):
        self.val = val
        self.next = next

然后是核心的加法函数,这个才是面试官盯着看的地方:

defaddTwoNumbers(l1: ListNode, l2: ListNode) -> ListNode:
# 哨兵节点,方便返回结果
    dummy = ListNode(0)
    cur = dummy

    carry = 0# 进位,开始是0

# 只要还有一个链表没走完,或者还有进位,就继续算
while l1 isnotNoneor l2 isnotNoneor carry != 0:
        x = l1.val if l1 isnotNoneelse0
        y = l2.val if l2 isnotNoneelse0

        s = x + y + carry
        carry = s // 10
        digit = s % 10

# 当前位的结果挂到新链表后面
        cur.next = ListNode(digit)
        cur = cur.next

# 往后挪动指针
if l1 isnotNone:
            l1 = l1.next
if l2 isnotNone:
            l2 = l2.next

return dummy.next

这个写法的几个小点,面试的时候容易被问:

  1. 为啥要用 dummy 这个哨兵节点? 因为你一开始还不知道结果链表的头在哪,直接造一个“假的头”,后面都往它后面接,最后 dummy.next 就是真头。这样就不用写“如果是第一个节点就怎么怎么”的那些 if,代码干净很多。

  2. while 里为啥要把 carry != 0 也算进去? 就是为了解决这种情况: 5 -> None 5 -> None 5 + 5 = 10,结果得是 0 -> 1,如果你只看链表是否为空,进位 1 就丢了。

  3. 时间、空间复杂度怎么说? 别紧张,张嘴就一句: “每个节点只访问一次,所以时间复杂度 O(n),n 是两个链表的最长长度;额外空间就是结果链表本身,算题目要求的,额外辅助空间是 O(1)。” 面试官一般就点点头过去了。

再顺手提醒几个常见“迷之翻车点”,我看好几个新人都栽过:

一个是不同长度的情况,比如:9 -> 9 -> 91你不能假设两个链表一样长,所以取值的时候一定要写成 x = l1.val if l1 else 0 这种,不然直接空指针见祖宗。

还有一个是原地修改 vs 新建链表。 有的人喜欢在 l1 上直接改,这个思路也行,但是面试题一般默认你别动入参,新建一个结果链表最保险,不容易搞出一些看不见的副作用,特别是后面还有别的逻辑复用这个链表的时候。