找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1853

积分

0

好友

248

主题
发表于 6 天前 | 查看: 16| 回复: 0

最近在云栈社区上看到一个帖子:当事人描述自己因不同意公司的“劝退”方案,结果被安排到会议室单独办公,并且公司还专门安装了两个摄像头对准他,进行全天候监控。

外包员工监控事件社交媒体截图

评论区里的网友观点也各不相同,有人认为公司有权进行工作场所监管,有人则认为这已经构成了职场霸凌,还有不少人建议当事人立即收集证据,申请劳动仲裁。

在我看来,问题的关键或许不在于摄像头本身,而在于这种措施是否具有针对性、是否旨在制造精神压力从而迫使员工主动离职。如果整个办公室只有你一个人被如此“特殊关照”,那么其意图就相当明显了。

面对这种情况,与其情绪化对抗,不如先保持冷静,系统性地收集好所有相关证据——包括书面通知、调岗记录、监控环境等,然后咨询专业法律人士,明确自己可以争取的合法权益和补偿。

算法题:相交链表

相交链表这个问题,可以想象成两条道路:起始段各自独立,但在某个路口之后,它们汇合成同一条路,后续的路段完全共享。题目的核心就是找到这个汇合的路口。如果两条路始终没有交汇,则返回空。

翻译成编程问题:给你两个单链表的头节点 headAheadB,这两个链表可能在某个节点开始“合并”,后续节点共享;需要注意的是,这里要求的是节点的内存地址相同(对象相同),而不仅仅是节点存储的数值相同。你需要返回那个相交的节点,如果不相交则返回 None

最朴素的想法是暴力比对:遍历链表A的每一个节点,再遍历链表B的所有节点,检查是否有地址相同的节点。这种方法的时间复杂度高达 O(m*n),在链表较长时完全不可行。

另一种思路是使用一个 set 集合:先将链表A的所有节点地址存入集合,再遍历链表B,检查当前节点是否在集合中。这种方法时间复杂度为 O(m+n),但需要 O(m) 的额外空间。

题目通常要求实现 时间复杂度 O(m+n),空间复杂度 O(1) 的解法,这就需要一些巧妙的技巧了。

思路一:先计算长度,让长的链表先走

这是一个非常直观的方法:

  1. 首先分别遍历两个链表,计算出各自的长度 lenAlenB
  2. 让指向较长链表的指针先向前移动 |lenA - lenB| 步,使得两个指针距离各自链表尾部的长度相同。
  3. 然后两个指针同步每次前进一步,它们第一次指向同一个节点时,那个节点就是交点;如果同时走到 None,则说明不相交。

核心思想在于:调整两个指针的“起跑线”,使它们距离终点(尾部)的剩余路程相同,这样它们就能在交点处相遇。

Python 实现如下:

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

def get_length(head: ListNode) -> int:
    length = 0
    cur = head
    while cur:
        length += 1
        cur = cur.next
    return length

def getIntersectionNode_len(headA: ListNode, headB: ListNode) -> ListNode | None:
    if not headA or not headB:
        return None

    lenA = get_length(headA)
    lenB = get_length(headB)

    curA, curB = headA, headB

    # 让较长的链表的指针先走几步
    if lenA > lenB:
        diff = lenA - lenB
        for _ in range(diff):
            curA = curA.next
    else:
        diff = lenB - lenA
        for _ in range(diff):
            curB = curB.next

    # 同步前进,寻找第一个相同的节点
    while curA is not curB:
        curA = curA.next
        curB = curB.next

    return curA  # 可能是交点,也可能是 None

这个方法逻辑清晰,只是多写了一个求链表长度的辅助函数。

思路二:双指针“互换路径”法,代码更简洁

这是一个经典且巧妙的双指针技巧,初次见到时往往让人豁然开朗。

思路如下:

  • 指针 pAheadA 开始遍历,当它走到链表 A 的末尾(None)时,立即跳转到链表 B 的头部(headB 继续遍历。
  • 指针 pBheadB 开始遍历,当它走到链表 B 的末尾(None)时,立即跳转到链表 A 的头部(headA 继续遍历。
  • 两个指针每次都只前进一步。
  • 如果两个链表相交,那么 pApB 必定会在相交节点相遇;如果不想交,那么它们最终会同时变为 None,循环也会终止。

为什么一定会相遇?我们可以简单计算一下两个指针走过的总路程:

  • pA 的路程 = 链表 A 非公共部分 + 链表 B 非公共部分 + 公共部分(如果相交)
  • pB 的路程 = 链表 B 非公共部分 + 链表 A 非公共部分 + 公共部分(如果相交)

可以看到,两个指针走过的总长度是相等的。因此,只要存在交点,它们必定会在某一时刻同步走到那个节点上。

实现代码非常简洁:

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

def getIntersectionNode(headA: ListNode, headB: ListNode) -> ListNode | None:
    if not headA or not headB:
        return None

    pA, pB = headA, headB

    # 最多遍历两轮(m+n),要么在交点相遇,要么在 None 相遇
    while pA is not pB:
        pA = pA.next if pA else headB
        pB = pB.next if pB else headA

    return pA  # 或者 pB,此时它们相同

这种写法的优点很明显:

  • 时间复杂度:每个指针最多遍历两个链表各一次,总体 O(m+n)。
  • 空间复杂度:只使用了固定数量的指针变量,O(1)。
  • 代码简洁:非常适合在面试的白板上书写。

我们可以构造一个简单的示例来验证:

def build_demo():
    # 公共部分:8 -> 9
    common1 = ListNode(8)
    common2 = ListNode(9)
    common1.next = common2

    # 链表 A:1 -> 2 -> 3 -> 8 -> 9
    a1 = ListNode(1)
    a2 = ListNode(2)
    a3 = ListNode(3)
    a1.next = a2
    a2.next = a3
    a3.next = common1

    # 链表 B:4 -> 5 -> 8 -> 9
    b1 = ListNode(4)
    b2 = ListNode(5)
    b1.next = b2
    b2.next = common1

    return a1, b1

if __name__ == "__main__":
    headA, headB = build_demo()
    node = getIntersectionNode(headA, headB)
    if node:
        print("相交点的值:", node.val)
    else:
        print("两个链表不相交")

运行上述代码,输出结果为:

相交点的值: 8

以上就是关于“相交链表”这道经典题目的两种核心解法。关键在于理解题目要求的“节点相同”是对象地址相同,而非值相同,并掌握通过调整指针起点或互换遍历路径来消除长度差的思想。在日常刷题练习时,将两种方法都实现一遍,有助于加深理解。




上一篇:C++模板元编程基础:std::true_type与false_type的核心作用与类型萃取实战
下一篇:Python代码沙箱实战:从AST静态检查到Docker容器隔离
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-9 17:46 , Processed in 0.189378 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表