优雅永不过时——leetcode106 相交链表
Posted thejohn2020
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了优雅永不过时——leetcode106 相交链表相关的知识,希望对你有一定的参考价值。
又许久未发文章了,前阵子忙着期末考和杂七杂八的事情,暑假又在学车。
有些题目令你瞠目结舌的并不是困难的思维,而是优雅的做法。虽然这些题目并不难,但是你第一次做的时候总会被这种做法惊讶得拍手叫好。
今天要讲的是leetcode106题——相交链表。题目链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/。这道题的意思是这样的:有两个单链表,这两个链表可能有两个相交的地方,如果有,返回第一个相交的节点,没有则返回NULL。
这道题一个非常容易想到的做法是使用一个集合,先把第一个链表的节点一个个存到集合里面,再去扫描第二个链表,看看当前的节点是不是在集合里面。由于第二个链表是从前往后扫的,所以最先找出来的相交节点一定是第一个。
代码如下:
/**
* 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;
ListNode* cur=headA;
while(cur!=NULL){
s.insert(cur);
cur=cur->next;
}
cur=headB;
while(cur!=NULL){
if(s.count(cur))
return cur;
cur=cur->next;
}
return NULL;
}
};
如果这道题就到这了的话,那就完全没有必要讲,因为我相信大家都会这个写法。但是真正精彩的在后头:双指针。
由于解法1使用了一个集合,所以空间复杂度是 O(n)的,而时间复杂度是O(n+m)的(n是链表1的长度,m是链表2的长度),在时间这方面已经无法优化了,而空间其实是可以达到 O(1)的。
下面来说说这个解法2:
首先定义两个指针p1和p2分别指向链表1和链表2的头部,两个指针同时向后移动,如果p1为空,就把p1指向链表2的头部,如果p2为空,就把p2指向链表1的头部。直到p1==p2结束,那么p1就是答案。
为什么这样做是正确的呢?下面把所有的情况分成3种来看
情况1:两个链表根本无交点
最后两个指针都到达NULL,返回NULL。
情况2:链表1和链表2在同样的第i个节点相交
两个指针到达8的时候相等,最后返回8
情况3:链表1第i个节点和链表2第j个节点相交(i!=j)
两个指针在2这个节点相遇,最后返回2
情况1和情况2没什么好说的,接下来证明情况3的正确性:
设n是链表1的长度,m是链表2的长度,链表1距离第一个交点的距离是k,当p2走了m步的时候,p2到达链表1的头部,再走k步到达第一个交点。那么此时的p1也走了m+k步
我们计算一下p1第二次到达第一个交点的距离为n+m-(n-k),化简为m+k步。因此这个时候我们的p1和p2指针都到达了第一个交点。p1==p2,返回p1就是最终的答案。
代码如下:
/**
* 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 *a=headA,*b=headB;
while(a!=b){
a=a==NULL?headB:a->next;
b=b==NULL?headA:b->next;
}
return a;
}
};
虽然这道题的代码很短,但是这个思路的确让人回味无穷~,继续加油:)
以上是关于优雅永不过时——leetcode106 相交链表的主要内容,如果未能解决你的问题,请参考以下文章