python-leetcode 22.相交链表

题目:给两个单链表的头节点heada和headb,请找出并返回两个单链表相交的起始节点。如果两个链表不存在相较节点,返回null。

两个链表在C1开始相交。

intersectval:相交的起始节点的值,如果不存在相交节点,这一值为0

listA:第一个链表

listB:第二个链表

skipA:在listA从头节点开始,跳到交叉节点的节点数

skipB:在listB从头节点开始跳到交叉节点的节点数

方法一:哈希集合

判断链表是否相交,可以使用哈希集合存储链表节点。

首先遍历链表headA,并将链表headA的每个节点加入哈希集合中,然后遍历链表headB,对于遍历到的每个节点,判断该节点是否在哈希集合中:

1.如果当前节点不在哈希集合中,则继续遍历下一个节点

2.如果当前节点在哈希集合中,则后面的节点都在哈希集合中,即从当前节点开始的所有节点都在两个链表的相交部分,因此在链表headB中遍历到的第一个哈希集合的节点就是两个链表相交的节点,返回该节点。

如果链表headB中的所有节点都不在哈希集合中,则两个链表不相交,返回null

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        visited=set() #创建集合
        temp=headA #指向headA的头节点
        while temp:  # 循环遍历 headA 的所有节点
            visited.add(temp)  #将temp加入集合,表示该节点已经访问过
            temp=temp.next #移动到下一个节点,直到结束
        temp=headB #重置指针,让其指向B
        while temp: #遍历B
            if temp in visited: # 如果当前节点在集合中,说明它是交点
                return temp
            temp=temp.next
        return None    # 如果没有交点,返回 None

        

时间复杂度:O(M+N)M和N是链表的长度

空间复杂度:O(M),链表A 的长度,需要使用哈希表存储A的全部节点

方法二:双指针

只有当链表A和链表B都不为空时,两个链表才可能相交,因此首先判断链表headA和headB是否为空,如果其中至少一个链表为空,则两个链表一定不相交,返回null

当链条A和链条B都不为空时,创建两个指针PA和PB,初始时分别指向两个链条的头节点,然后将两个指针依次遍历两个链条的每个节点,具体做法如下:

每步操作需要同时更新指针PA和PB

如果指针PA不为空,则将指针PA移到下一个节点,如果指针PB不为空,则将指针PB移到下一个节点

如果指针pA 为空,则将指针 pA 移到链表 headB 的头节点,同样,如果指针PB为空,则将指针PB移到链条headA的头节点。

当指针PA和PB指向同一个节点或者都为空时,返回它们指向的节点或者null

证明(源自官网)

下面提供双指针方法的正确性证明。考虑两种情况,第一种情况是两个链表相交,第二种情况是两个链表不相交。

情况一:两个链表相交

链表 headA 和 headB 的长度分别是 m 和 n。假设链表 headA 的不相交部分有 a 个节点,链表 headB 的不相交部分有 b 个节点,两个链表相交的部分有 c 个节点,则有 a+c=m,b+c=n。

如果 a=b,则两个指针会同时到达两个链表相交的节点,此时返回相交的节点;

如果 a!=b,则指针 pA 会遍历完链表 headA,指针 pB 会遍历完链表 headB,两个指针不会同时到达链表的尾节点,然后指针 pA 移到链表 headB 的头节点,指针 pB 移到链表 headA 的头节点,然后两个指针继续移动,在指针 pA 移动了 a+c+b 次、指针 pB 移动了 b+c+a 次之后,两个指针会同时到达两个链表相交的节点,该节点也是两个指针第一次同时指向的节点,此时返回相交的节点。

情况二:两个链表不相交

链表 headA 和 headB 的长度分别是 m 和 n。考虑当 m=n 和 m!=n 时,两个指针分别会如何移动:

如果 m=n,则两个指针会同时到达两个链表的尾节点,然后同时变成空值 null,此时返回 null;

如果 m!=n,则由于两个链表没有公共节点,两个指针也不会同时到达两个链表的尾节点,因此两个指针都会遍历完两个链表,在指针 pA 移动了 m+n 次、指针 pB 移动了 n+m 次之后,两个指针会同时变成空值 null,此时返回 null。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        if not headA or not headB: #如果 headA或headB 是 None,说明链表为空,不可能有交点
            return None
        pA,pB=headA,headB ## 初始化两个指针,分别指向 headA 和 headB
        while pA !=pB:  #当 pA 和 pB 不相等时继续遍历
            pA=headB if pA is None else pA.next #  pA 走完 A 之后跳转到 B
            pB=headA if pB is None else pB.next # pB 走完 B 之后跳转到 A
        return pA  # 返回交点

        

时间复杂度:O(M+N)遍历各链表一遍

空间复杂度:O(1) 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值