Python技术迷

因为狐臭被公司开除了~

刚刷到这个帖子,我第一反应就是:啊?因为狐臭,快转正了直接被公司劝退,这操作也太粗暴了点。

人家说自己工作挺认真,业绩也不差,眼看着就要转正,结果人事突然通知明天不用来了。理由不是能力不行,不是工作出错,而是身上味道影响同事。说真的,这事听着就挺扎心。

Image

狐臭这个东西吧,确实可能会影响办公环境,但公司要真觉得有问题,正常也该先沟通,给人一点处理空间。上来就“你别来了”,这谁顶得住啊。换谁都得怀疑人生:我是不是以后都找不到工作了?

但也别一下把自己判死刑。该看医生看医生,该处理处理,别硬扛。公司这么处理也挺冷的,打工人已经够难了,还要因为这种事被一脚踢开,真是看得人心里堵。

今日算法题

链表遍历卡住,CPU 不高,日志也不刷,接口就这么吊着。

这种问题我第一眼不会先怀疑 Python 慢,也不会先改超时参数。链表一旦走不出来,八成是里面有环。尤其是那种从缓存里拼节点、从文件反序列化节点、或者面试题里手工构造链表的场景,next 指针指回去了,普通 while 直接变成死循环。

环形链表这题,最笨的写法是拿一个 set 存访问过的节点:

defhas_cycle_by_seen(head):
    seen = set()
    cur = head

while cur:
if cur in seen:
returnTrue
        seen.add(cur)
        cur = cur.next

returnFalse

这代码能跑,但我不太喜欢。不是因为它错,而是它多吃了一份空间。链表长一点,set 跟着涨。线上排障时我一般不愿意为了判断一个指针问题,再引入一堆额外对象。

更稳的写法是快慢指针。

慢指针一次走一步,快指针一次走两步。没有环,快指针迟早撞到 None。有环,快指针会在环里追上慢指针。这个判断很像操场跑圈,跑得快的人只要一直跑,迟早会套圈。

代码我一般会这么写:

classNode:
def__init__(self, value):
        self.value = value
        self.next = None


defhas_cycle(head):
    slow = head
    fast = head

while fast isnotNoneand fast.next isnotNone:
        slow = slow.next
        fast = fast.next.next

if slow is fast:
returnTrue

returnFalse

这里有两个地方别手滑。

第一个是 while 条件必须同时判断 fast 和 fast.next。因为快指针要走两步,如果只判断 fast,到尾节点时再取 fast.next.next,直接炸。

第二个是比较节点要用 is,不是比较 value。链表里两个节点的值一样太正常了,比如:

a = Node(7)
b = Node(7)

这俩值一样,但不是同一个节点。环判断看的是指针是不是回到了老地方,不是值有没有重复。

随手构造一个带环的链表测一下:

n1 = Node("order-1")
n2 = Node("order-2")
n3 = Node("order-3")
n4 = Node("order-4")

n1.next = n2
n2.next = n3
n3.next = n4
n4.next = n2

print(has_cycle(n1))  # True

再测一个正常链表:

x1 = Node("log-1")
x2 = Node("log-2")
x3 = Node("log-3")

x1.next = x2
x2.next = x3

print(has_cycle(x1))  # False

这题还有一个进阶版本:不光判断有没有环,还要找环从哪里开始。

快慢指针第一次相遇后,把一个指针重新放回头节点,然后两个指针都一次走一步。它们再次相遇的位置,就是环入口。

deffind_cycle_entry(head):
    slow = head
    fast = head

while fast and fast.next:
        slow = slow.next
        fast = fast.next.next

if slow is fast:
            cursor = head
while cursor isnot slow:
                cursor = cursor.next
                slow = slow.next
return cursor

returnNone

这个写法看着有点绕,但比到处塞计数器靠谱。面试里能写出判断环不稀奇,能把入口节点也写干净,基本就说明指针移动这块没乱。

环形链表这题,真正容易错的不是算法,而是边界:空链表、只有一个节点、两个节点成环、节点值重复。把这几个样例跑完,代码基本就稳了。