一开始想到的暴力解法忽略了两链表不等长时,都从第一个元素开始遍历会失败
解法
1.暴力解法
指针遍历链表,但是要记得若两链表不等长则不能只遍历一遍,则需要两个while形成两层遍历,则一链表为基,从起始遍历另一链表,相互比较。即对链表A中的每一个结点ai ,遍历整个链表 B 并检查链表 B 中是否存在结点和ai相同。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA==NULL||headB==NULL)//无交点
return NULL;
while(headA)
{ ListNode *p=headB;//注意其位置,每次循环都需重置
if(headA==p) return headA;//注意返回为结点
while(p->next)
{
p=p->next;
if(headA==p) return headA;
}
headA=headA->next;
}
return NULL;
}
};
这种解法时间复杂度O(mn),太慢了,不建议使用。
解法2 哈希法
遍历链表 A 并将每个结点的地址/引用存储在哈希表中。然后检查链表 B 中的每一个结点 bi是否在哈希表中。若在则bi为相交结点。用到set(关联容器 键值类型相同,键唯一,则集合中不会有多个相同的键,对于此题可用)
此解法与暴力解法类似,只不过将结点存储在哈希表中,只需遍历一遍A即可
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA==NULL||headB==NULL)//无交点
return NULL;
set<ListNode *> s;
while(headA)
{
s.insert(headA);//装入set
headA=headA->next;
}
while(headB)
{
if(s.find(headB)!=s.end())//set的find函数返回一个迭代器,该迭代器指向键与head值相同的元素,若无则返回s.end()
return headB;
headB=headB->next;
}
return NULL;
}
};
时间复杂度 : O(m+n)O(m+n)。
空间复杂度 : O(m)O(m) 或 O(n)O(n)。
解法三 双指针
-
创建两个指针 pA 和 pB,分别初始化为链表 A 和 B 的头结点。然后让它们向后逐结点遍历。
-
当 pA 到达链表的尾部时,将它重定位到链表 B 的头结点 (你没看错,就是链表 B); 类似的,当 pB 到达链表的尾部时,将它重定位到链表 A 的头结点。
-
若在某一时刻 pA 和 pB 相遇,则 pA/pB 为相交结点。
-
想弄清楚为什么这样可行, 可以考虑以下两个链表: A={1,3,5,7,9,11} 和 B={2,4,9,11},相交于结点 9。 由于 B.length (=4) < A.length (=6),pB比 pA 少经过 22 个结点,会先到达尾部。将 pB 重定向到 A 的头结点,pA 重定向到 B 的头结点后,pB 要比 pA 多走 2 个结点。因此,它们会同时到达交点。
如果两个链表存在相交,它们末尾的结点必然相同。因此当 pA/pB到达链表结尾时,记录下链表 A/B 对应的元素。若最后元素不相同,则两个链表不相交。
这样用双指针就使两指针走相同的路程,弥补了链表长度的不同
即a + all +b = b + all + a (all是链表相交个数,a是A不相交个数,b是B不相交个数)
class Solution {
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?pa->next:headB;
pb=pb?pb->next:headA;
}
return pa;
}
};
复杂度分析
时间复杂度 : O(m+n)O(m+n)。
空间复杂度 : O(1)O(1)。
三种方法第三种最优。