题目:
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
图示两个链表在节点 c1
开始相交
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 输出:Intersected at '8' 解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。 从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。 在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
思路/代码
这道题目相对来说是比较简单的了,但是本人还是没有想出来第二种方法,只能说双指针YYDS,而且还需要对两个指针遍历的数量关系有一定的了解
这题就是利用了链表A和B的长度数量关系和指针遍历的特性进行解题的
方法一:哈希表
哈希集合对大家来说应该不会陌生,Set集合中有一个方法contains()可以获取集合中是否存在指定的元素,我们可以先遍历链表A,将链表中的所有元素存入Set集合中,由于题目告诉我们了,相交链表没有环,所以我们可以使用P指针直接遍历B链表,然后对B链表中每个元素使用contains()方法判断是否存在于Set集合中,存在就直接返回,当P指针为NULL的时候代表A和B链表不相交
具体代码:
public ListNode setMethod(ListNode headA,ListNode headB) {
Set<ListNode> set = new HashSet();
//将A链表元素存入集合中
while(headA != null) {
set.add(headA);
headA = headA.next;
}
//遍历B链表判断是否存在于集合中
while(headB != null) {
if(set.contains(headB)){
return headB;
}
headB = headB.next;
}
//没有找到交点
return null;
}
方法二:双指针
接下来就是十分优雅的双指针解法了、看到这个解法真的是秀了我一脸
具体思路就是定义两个指针P1和P2,让P1和P2分别同时向后移动,链表会有两种情况
-
两个链表有交点
我们可以假设链表A长度为m,链表B长度为n,链表A不相交的长度为a,链表B不相交长度为b,两个链表相交部分长度为c
-
当a等于b的时候,我们的P1和P2指针应该是可以同时移动到一个相等节点的,这个时候直接返回P1指针指向的元素即可
-
当a不等于b的时候 P1指针应该移动a + c + b才能到达相交的位置,P2指针应该移动b + c + a才能到达相交的位置
由于 a + c + b = b + c + a 所以当P1指针先到达NULL的时候让其指向B链表,当P2指针到达NULL让其指向A链表,并且继续向后移动,直到P1 = P2就可以返回P1
-
-
两个链表无交点
-
当a 不等于 b时候,P1移动m+n,P2移动n+m的时候相等,而且都等于NULL
-
当a 等于 b的时候,P1移动m,P2移动n的时候相等,而且都为NULL
-
public ListNode setMethod(ListNode headA,ListNode headB) {
//如果A或者B链表有一个为NULL就不可能有交点
if(headA == null || headB == null) {
return null;
}
//定义两个指针
ListNode p1 = headA;
ListNode p2 = headB;
while(p1 != p2) {
p1 = p1 == null ? headB : p1.next;
p2 = p2 == null ? headA : p2.next;
}
//不管如何都返回p1,因为最后不是相交节点就是NULL
return p1;
}
总结
这道题目其实更多考察的是思维上的模式,代码并不难,我们要学会对问题进行抽象,并且分析问题中的关键对象和数量关系,只有看懂了题目才能有思路,有了思路,解题就能更加得心应手!
共勉!