1 剑指Offer 52 两个链表的第一个公共节点
本质:找到两个有序数据段中的第一个相同数据
1.1 Set解决
解题:利用set的不重复性,首先把headA都塞到set中,再遍历headB找有没有已经出现在set中的节点即可。
注意! set的数据是ListNode* 不是 int。如果是int可能出现节点不同但是var相同的情况,而ListNode* 就不会。
#include<set>
using namespace std;
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
set<ListNode*> my_set;
while(headA!=NULL)
{
my_set.insert(headA);
headA = headA->next;
}
while(headB!=NULL)
{
if(my_set.find(headB) != my_set.end())
return headB;
else
headB = headB->next;
}
return NULL;
}
};
时间复杂度:O(2n)
空间复杂度:O(n)
1.2 stack解决
创建两个stack,将节点全部压入stack中然后同时出栈,等到出栈的元素不相等时,其上一个出栈的节点就是目标节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
//栈解决
#include<stack>
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
stack<ListNode*> stack_A;
stack<ListNode*> stack_B;
while(headA!=NULL)
{
stack_A.push(headA);
headA = headA->next;
}
while(headB!=NULL)
{
stack_B.push(headB);
headB = headB->next;
}
ListNode* pre = NULL;
while(stack_A.size()!=0 && stack_B.size()!=0)
{
if(stack_A.top() == stack_B.top())
{
pre = stack_A.top();
stack_A.pop();
stack_B.pop();
}
else
break;
}
return pre;
}
};
有一些细节需要注意:
- stack的pop()函数没有返回值,所以需要用stack的top()来保存栈顶元素。
- 记得用pre来保存上一个pop出来的节点。否则找到了不一样的那一个节点后找不到上一个节点了。
时间复杂度:O(3n)
空间复杂度:O(2n)
1.3 双指针法
这个思路比较难想,回归到本质,两个链表的末段相同,那么尝试把这A,B两个链表按照AB和BA的方式拼接,会发现末段依旧相同。那么双指针同时遍历直到相等即可。
/**
* 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* p1 = headA;
ListNode* p2 = headB;
if(p1 == NULL || p2 ==NULL)
return NULL;
while(p1!=p2)
{
p1 = p1->next;
p2 = p2->next;
if(p1!=p2) //@1
{
if(p1==NULL)
p1 = headB;
if(p2==NULL)
p2 = headA;
}
}
return p1;
}
};
而链表的拼接,其实可以用虚拟的拼接:当指针遍历到链表结尾后,赋值为另一个表的表头。
不要忘记当存在空链表时,直接返回NULL,否则AB == BA 返回的就是第一个节点值,出错。
@1:这里要用一个if进行判断,因为如果两者相等,其实可以直接跳出循环,没必要再进行判断了。
这里会产生一个疑问,就是p1和p2同时到达节点末尾怎么办?
其实如果两个链表长度相同,那么比较的时候在结尾之前就可以找到相等的节点了,比原问题简单多了呢
时间复杂度:O(n)
空间复杂度:O(1)
1.4 对齐双指针法
由上面双指针法的特例,我们发现如果两个链表节点数相同,这个题目将会变得非常简单,于是诞生了下面这种解法,即先对齐两个链表,然后遍历即可。
/**
* 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) {
int lenA = 0,lenB = 0;
ListNode* p1 = headA;
ListNode* p2 = headB;
while(p1!=NULL)
{
p1 = p1->next;
lenA++;
}
while(p2!=NULL)
{
p2 = p2->next;
lenB++;
}
int distance = lenA > lenB ? lenA-lenB:lenB-lenA;
if(lenA>lenB)
{
for(int i=0; i<distance; i++)
headA = headA->next;
}
if(lenB>lenA)
{
for(int i=0; i<distance; i++)
headB = headB->next;
}
ListNode* pA = headA;
ListNode* pB = headB;
while(pA != pB)
{
pA = pA->next;
pB = pB->next;
}
return pA;
}
};