160. 相交链表
题目来源
题目分析
给你两个单链表的头节点
headA
和headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回null
。
题目难度
- 难度:简单
题目标签
- 标签:链表、双指针
题目限制
- 两个链表中不存在环。
headA
和headB
的长度分别为m
和n
。0 <= m, n <= 3 * 10^4
1 <= Node.val <= 10^5
解题思路
思路:双指针法
-
问题定义:
- 要求找到两个单链表相交的起始节点。通过遍历两个链表,判断它们的相同节点。
-
核心算法:
- 双指针法:使用两个指针分别遍历两个链表,当一个指针到达链表尾部时,跳转到另一个链表的头部继续遍历。这样两个指针最终会在相交点相遇或同时到达
null
。
- 双指针法:使用两个指针分别遍历两个链表,当一个指针到达链表尾部时,跳转到另一个链表的头部继续遍历。这样两个指针最终会在相交点相遇或同时到达
-
关键步骤:
- 初始化两个指针
pA
和pB
,分别指向headA
和headB
。 - 遍历两个链表,当
pA
到达链表尾部时跳转到headB
,pB
到达链表尾部时跳转到headA
。 - 如果两个指针相遇,返回该节点;如果两个指针同时到达
null
,则返回null
。
- 初始化两个指针
核心算法步骤
-
初始化指针:
- 将两个指针分别初始化为
headA
和headB
。
- 将两个指针分别初始化为
-
遍历链表:
- 当两个指针不相等时,继续遍历。如果其中一个指针到达
null
,则将其跳转到另一个链表的头部。
- 当两个指针不相等时,继续遍历。如果其中一个指针到达
-
返回结果:
- 当两个指针相遇时,返回该节点;如果两个指针都为
null
,则返回null
。
- 当两个指针相遇时,返回该节点;如果两个指针都为
代码实现
以下是双指针法的 Java 代码实现:
/**
* 160. 相交链表
* @param headA 链表1
* @param headB 链表2
* @return 相交的起始节点
* @apiNote 双指针法,时间复杂度O(m+n),空间复杂度O(1)
*/
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode pA = headA;
ListNode pB = headB;
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
代码解读
- 双指针遍历:
pA
和pB
分别遍历headA
和headB
,当一个指针到达null
时,切换到另一个链表的头部。 - 判断相交节点:当
pA
和pB
相遇时,返回相交节点;否则返回null
。
性能分析
- 时间复杂度:
O(m + n)
,其中m
和n
分别是两个链表的长度。每个指针最多遍历两个链表的总长度。 - 空间复杂度:
O(1)
,只使用了常量级的额外空间。
测试用例
你可以使用以下测试用例来验证代码的正确性:
// 测试用例1
ListNode headA1 = new ListNode(4);
ListNode headB1 = new ListNode(5);
ListNode common1 = new ListNode(8);
headA1.next = new ListNode(1, common1);
headB1.next = new ListNode(6, new ListNode(1, common1));
common1.next = new ListNode(4, new ListNode(5));
ListNode result1 = getIntersectionNode(headA1, headB1);
System.out.println(result1.val); // 输出: 8
// 测试用例2
ListNode headA2 = new ListNode(2, new ListNode(6, new ListNode(4)));
ListNode headB2 = new ListNode(1, new ListNode(5));
ListNode result2 = getIntersectionNode(headA2, headB2);
System.out.println(result2); // 输出: null
扩展讨论
其他实现
- 长度对齐法:通过计算两个链表的长度,让较长的链表先遍历一定距离以对齐两个链表的长度,然后同时遍历两个链表,找到相交节点。
- 哈希表法:使用哈希表记录
headA
中的节点,再遍历headB
,找到第一个在哈希表中的节点即为相交节点。
// 哈希
Map<ListNode,Integer> map=new HashMap<>();
ListNode p=headA;
while(p!=null){
map.merge(p,1,Integer::sum);
p=p.next;
}
p=headB;
while(p!=null){
map.merge(p,1,Integer::sum);
if(map.get(p)>1){
return p;
}
p=p.next;
}
return null;
总结
这道题目考察了链表的相交问题,双指针法通过简单有效的遍历,能够在 O(m + n)
的时间复杂度内找到相交节点或确认两个链表不相交。