1.题目
剑指 Offer 52. 两个链表的第一个公共节点
输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
在节点 c1 开始相交。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
示例 2:
输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
示例 3:
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。
注意:
如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
2.自我思路及实现
1.栈
每个链表各创建一个栈,将节点压入栈中,
如果不相交,栈顶节点不同,
如果相交,找出第一个不同的节点的next节点
时间:A+B,遍历A, 遍历B, 寻找相同节点最大为二者之间最小者
空间:A+ B
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == headB)
return headA;
Stack<ListNode> stackA = new Stack<>();
Stack<ListNode> StackB = new Stack<>();
ListNode tempA = headA;
ListNode tempB = headB;
while(headA != null)
{
stackA.push(headA);
headA = headA.next;
}
while(headB != null)
{
StackB.push(headB);
headB = headB.next;
}
ListNode res = null;
while(!stackA.isEmpty() && !StackB.isEmpty())
{
if(stackA.peek() != StackB.peek())
{
return res;
}
else
{
res = stackA.pop();
StackB.pop();
}
}
return res;
}
}
2.双指针
先记录两个链表的长度,使用指针分别指向这两个链表的开头
移动较长链表的指针至和较短链表剩余的节点数量相同,再一起移动并判断两个指针所指向的节点是否相同,若直到结尾都不同,则返回空节点
时间:A+B
空间:1
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null)
return null;
int numA = 0;
int numB = 0;
ListNode tempA = headA, curA = headA;
ListNode tempB = headB, curB = headB;
while(tempA != null)
{
tempA = tempA.next;
numA++;
}
while(tempB != null)
{
tempB = tempB.next;
numB++;
}
if(numA >= numB)
{
for(int i = numB; i < numA; i++)
{
curA = curA.next;
}
while(curA != null)
{
if(curA == curB)
return curA;
curA = curA.next;
curB = curB.next;
}
}
else
{
for(int i = numA; i < numB; i++)
{
curB = curB.next;
}
while(curB != null)
{
if(curA == curB)
return curA;
curA = curA.next;
curB = curB.next;
}
}
return null;
}
}
3.总结思路及实现
3.拼接链表+双指针(最简)
如何消除较长链表多的那一部分从而使得两个指针指向的链表的剩余部分数量相同?
使用拼接链表,将长+短 和短+长看作两个新的链表,两个指针从前向后遍历这两个新的链表,当遍历到长+短的短的时候,此时已经是两个指针指向长短链表的剩余部分相同.
若两个链表长度相同,那么指针开始移动时就已经达成条件了
时间:A+B
空间:1
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode pA = headA;
ListNode pB = headB;
while(pA != pB)
{
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
}
4.相关知识
- 链表要首先想到双指针解法