今日主要总结一下,剑指 Offer 52. 两个链表的第一个公共节点
题目:剑指 Offer 52. 两个链表的第一个公共节点
题目描述:
输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。
数据范围: n≤1000
要求:空间复杂度 O(1),时间复杂度 O(n)
示例 1:
输入:两个相交链表的两个头结点指针
输出:相交链表交点处结点指针
注意:
如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
一、开始的错误解法
这道题开始我就想着要找两个链表第一个val相同的结点就是相交结点,就让我想到了,LeetCode的第一题,两数之和,把一个链表的val存到hashtable,之后遍历另一个链表val,只要找到一样的val,就return
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_map<int, int> hashtable;
ListNode *curA = headA;
ListNode *curB = headB;
while(curA != NULL){
hashtable[curA->val] = 1;
curA = curA->next;
}
while(curB != NULL){
if(hashtable.find(curB->val)!=hashtable.end()){
return curB;
}
}
return NULL;
}
};
本题重难点
简单来说,就是求两个链表交点节点的指针。 这里要注意,交点不是数值相等,而是指针相等!!!
二、正确解法
方法一:集合set法
这个就是我最开始想法的正确解法,应该存入节点指针,而不是存入val
1.遍历链表headA,并存入set中
2.遍历headB,边遍历边在set中查找是否有相同元素
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
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;
}
};
方法二:让两个链表头指针移至同一起点
先统计两个链表的长度,如果两个链表的长度不一样,就让链表长的先走,直到两个链表长度一样,这个时候两个链表再同时每次往后移一步,看节点是否一样,如果有相等的,说明这个相等的节点就是两链表的交点,否则如果走完了还没有找到相等的节点,说明他们没有交点,直接返回null即可,来画个图看一下。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *curA = headA;
ListNode *curB = headB;
int lenA = 0,lenB = 0;
while(curA != NULL){
++lenA;
curA = curA->next;
}
while(curB != NULL){
++lenB;
curB = curB->next;
}
curA = headA;
curB = headB;
if(lenA < lenB){
swap(lenA , lenB);
swap(curA , curB);
}
int gap = lenA - lenB;
while(gap --){
curA = curA -> next;
}
while(curA != NULL){
if(curA == curB){
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
方法三:双指针法
这个双指针还有一个名字叫做浪漫相遇:
你变成我,走过我走过的路
我变成你,走过你走过的路
然后我们便相遇了…
就是我们考虑到两个链表在相交之前长度很可能不同,这个时候同时跑就会一直有差距一直不会相遇,所以我们有了方法一,开始先移至同一起跑线。同样的,双指针是指假设两个链表公共节点之前(包括公共节点)长度分别为m和n,公共节点之后的公共长度为t,使用两个指针 node1,node2 分别指向两个链表 headA,headB 的头结点,然后同时分别逐结点遍历,当 node1 到达链表 headA 的末尾时,重新定位到链表 headB 的头结点;当 node2 到达链表 headB 的末尾时,重新定位到链表 headA 的头结点。这样最后两个在相遇之前,就跑了同样的距离,第一次少跑的重新定位后的第二次就会多跑,相遇时两个指针都跑了(m+n+t),如果没有公共节点就会在一起移至最后一个节点 return NULL
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *curA = headA;
ListNode *curB = headB;
while(curA != curB){
if(curA == NULL)
curA = headB;
else
curA = curA -> next;
if(curB == NULL)
curB = headA;
else
curB = curB -> next;
}
return curA;
}
};
总结
二、算法思想
寻找链表的公共节点,就是求两个链表交点节点的指针。 这里要注意,交点不是数值相等,而是指针相等!!!