1. 常规思路

既然题目已经明知是两个无环的单链表,那么此题难度就降低了。常规思路就是用一个哈希集合来存储第一个链表遍历后的所有节点;接着遍历第二个链表,与哈希集合中的节点进行比较

  • 如果第二个链表中节点不存在于哈希集合中,那么移动到下一个节点再比较;
  • 否则,节点存在于哈希集合中,返回第二个链表的该节点(他就是我们要找的第一个公共节点)。

PS:如果比较到最后一个节点发现,第二个链表中的节点都不存在于哈希集合中,那么说明这两个链表不相交!直接返回None即可。

2. 图解示例

根据示例一,可以得到如下的黄色链表A和蓝色链表B:
图片说明
然后进行遍历比较操作:
图片说明
同样不相交的链表示例二如下:
图片说明

2. 核心代码

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

#
# 
# @param pHead1 ListNode类 
# @param pHead2 ListNode类 
# @return ListNode类
#第一种解法:哈希
class Solution:
    def FindFirstCommonNode(self , pHead1 , pHead2 ):
        # write code here
        #首先判断两个链表是否为空
        if pHead1 is None or pHead2 is None:
            return None
        #定义链表1 的集合
        set_A = set()
        node1, node2 = pHead1, pHead2 #定义两个节点
        #遍历链表 1 ,把每个节点加入集合中
        while node1:
            set_A.add(node1)
            node1 = node1.next
        #遍历链表2 看当前节点是否在 集合中;如果存在,当前节点就是要找的第一个公共节点;否则继续比较下一个节点。
        #这里还要注意,如果遍历完链表 B,发现所有节点都不在集合中,则说明两个链表不相交,返回None。
        while node2:
            if node2 in set_A:
                return node2
            node2 = node2.next
        return None

3. 复杂度分析

  • 时间复杂度:
    (需要遍历链表1,这里m代表链表1 的长度;同理n 就是遍历链表2的时间)
  • 空间复杂度:
    (因为创建的哈希集合要存储链表1的所有节点,所以为m)

--------------------------------------------我是解法二的分割线--------------------------------------------------

4. 解法二:双指针思路

4.1 思路+图解

看到这里有两个链表,那么双指针又可以出来秀一把了。具体步骤如下:

  • 创建两个指针A和B分别指向两链表的初始节点,然后让指针A遍历链表1的所有节点;同理指针B也遍历链表2的所有节点;
  • 当指针A和指针B都遍历完各自的节点后,开始互换赛道,让指针A去走链表2;而指针B则走链表1
  • 依然是遍历,但是这次两指针就会在某一个点相遇!这个相遇点就是我们要找的第一个公共节点。

PPS:也许有朋友要疑问:如果他们没有相交点呢?放心,因为不相交,所以他们最后都会走到一个None的节点,此时返回该节点即可。

下面是图解示例一,当两链表相交的时候👇:
图片说明
持续这个过程,直到指针A和指针B走完各自的节点:
图片说明
图片说明
现在指针A换到链表2的赛道上,指针B换到链表1的赛道,只要节点不相等继续往下一个节点移动:
图片说明
图片说明
最后他们就会在node 6 这里相遇:
图片说明
同样示例二的不相交过程如下:
图片说明

4.2 核心代码

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
# @param pHead1 ListNode类 
# @param pHead2 ListNode类 
# @return ListNode类
#第二种解法:双指针
class Solution:
    def FindFirstCommonNode(self , pHead1 , pHead2 ):
        # write code here
        #首先判断两个链表是否为空
        if pHead1 is None or pHead2 is None:
            return None
        #使用双指针思路进行判断
        p1, p2 = pHead1, pHead2 #创建两个指针,分别指向两个链表的头节点
        #当指针指向的两个节点不相等:
        while p1 != p2:
            if p1:
                p1 = p1.next           #同时更新两个指针
            else:
                p1 = pHead2
            #直到p1走完了自己的节点,p2也走完了自己的节点,此时p1换到p2的这条道上;同理p2也走到p1的道上
            #两者还是每次都移动到下一个节点,最终p1会跟p2在某一个节点相遇,相遇的这个节点就是他们的公共节点!
            if p2:
                p2 = p2.next
            else:
                p2 = pHead1
        return p1                  #由于没有交点的时候,两个链表最后都会出现都是None值,所以此时返回的p1 = None          

4.3 复杂度分析:

  • 时间复杂度:
    (其中m 和n 是分别是链表1和链表2的长度。两个指针同时遍历两个链表,每个指针遍历两个链表各一次)
  • 空间复杂度:
    (两指针使用常数大小的额外空间)