Write a program to find the node at which the intersection of two singly linked lists begins.
For example, the following two linked lists:
A: a1 → a2 ↘ c1 → c2 → c3 ↗ B: b1 → b2 → b3
begin to intersect at node c1.
Notes:
- If the two linked lists have no intersection at all, return
null
. - The linked lists must retain their original structure after the function returns.
- You may assume there are no cycles anywhere in the entire linked structure.
- Your code should preferably run in O(n) time and use only O(1) memory.
要说在LeetCode上刷题最让我受益匪浅的地方,大概就是链表相关的问题了吧。以前对于链表的认识很浅,基本只够应付“说说链表和数组的优缺点”这种问题,真正应用到的机会很少。来了LeetCode才发现,链表还可以这么玩,而且玩的这么溜。
针对这道题,我想了很久都想不到O(n)时间复杂度的做法(我是真的菜),于是就撸了一个暴力枚举的做法:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* tempA;
ListNode* tempB;
while(headA)
{
tempA=headA;
tempB=headB;
while(tempB)
{
if(tempA==tempB)return tempA;
tempB=tempB->next;
}
headA=headA->next;
}
return NULL;
}
};
毕竟跟指针相关的代码很容易出错,为了确认这样写的逻辑有没有问题,我就试探性地交了一发,居然直接AC了...这要是在ACM,怎么也是TLE吧。
感觉打ACM的人是不是真的不应该做LeetCode,做多了AC多了,你真的就没有对罚时的恐惧了,对时间复杂度也视而不见了...
当然这道题还没有结束,上面的代码明显不是最优的。想了一下其实用哈希基本就可以降复杂度了,于是乎再来一发:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set<ListNode*> s;
while(headA)
{
s.insert(headA);
headA=headA->next;
}
while(headB)
{
if(s.find(headB)!=s.end())return headB;
headB=headB->next;
}
return NULL;
}
};
哈哈哈过了,用时也还算低,但是直觉告诉我这不是出题人的本意,肯定还有更巧妙的做法。果不其然,围观了一下大佬的做法,简直变身小迷弟给大佬疯狂打call。这是大佬的代码:/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if (!headA||!headB) return NULL;
ListNode *a=headA,*b=headB;
while(a!=b)
{
a=a?a->next:headB;
b=b?b->next:headA;
}
return a;
}
};
两个字:优美
题目保证不出现环,然后大佬就把两个链首尾相连构造成环。有两个理解上的要点:
- 假设A长度m,B长度n,两个指针都会在m+n次循环后同时抵达空指针,使得a!=b不成立退出循环,防止了死循环的出现,返回NULL。
- 假设倒数第k个点是交集,由于交点之后的点是共用的,所以两个指针都会在m+n-k次循环后抵达相同节点,使得a!=b不成立退出循环,返回交点指针。
如果大家理解上有困难,可以画个图模拟一下。这种想法光是理解对我们来说都挺费劲的,大佬还能用短短几行代码就将其实现,实在是让人佩服。
这道题也让我认识到,自己对指针、链表的认识还需要加强。