劍指offer36--連結串列的第一個公共結點
阿新 • • 發佈:2021-02-20
題目描述
輸入兩個連結串列,找出它們的第一個公共結點。(注意因為傳入資料是連結串列,所以錯誤測試資料的提示是用其他方式顯示的,保證傳入資料是正確的)
思路
思路1:雜湊
map有一個特點,當使用陣列插入方式時,遇到相同的鍵值,會把原來的值給覆蓋,但是可以通過構造一個pair來檢測是否插入成功。
實現:
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
if (!pHead1|| !pHead2) return nullptr;
map<ListNode*,int> m;
ListNode* p=pHead1;//先遍歷第一條連結串列將所有結點加入到map中
while (p)
{
m[p]=p->val;
p=p->next;
}
pair<map<ListNode*, int>::iterator, bool> insert_pair; //構造一個pair
p=pHead2;//再遍歷第二條連結串列
while(p)
{
insert_pair = m.insert(map<ListNode*, int>::value_type (p, p->val));
if (!insert_pair.second)//如果存在公共結點,這個就不會插入成功,對應這個Pair的第二個引數就是false,那麼這個結點就是第一個公共結點
return p;
p=p->next;
}
return nullptr;
}
};
分析:
空間複雜度和時間複雜度都是兩條連結串列的長度和,即都是O(m+n)
思路2:雙指標
用兩個指標分別遍歷2條連結串列兩次:
(假設長度不相等,因為相等的情況太好做了)
第一次都各自走到頭,短的先走到頭,然後重新將其指向長的那條繼續走;長的後走到頭,將其指向短的繼續走。這時候兩個指標走的路是一樣長了,要麼走到公共結點,要麼兩個同時為null
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
if (!pHead1||!pHead2) return nullptr;
ListNode* p1=pHead1;
ListNode* p2=pHead2;
while (p1!=p2)
{
p1=p1->next;
p2=p2->next;
if (p1!=p2)
{
//短的先開啟了第二輪遍歷
if (!p1) p1=pHead2;
if (!p2) p2=pHead1;
}
}
return p1;
}
};
分析:
時間複雜度:最壞的情況是公共結點在末尾,這樣兩條連結串列都要完整遍歷兩次,即O(2m+2n),這樣比較起來雜湊的時間複雜度反而要低,因為只要各遍歷一次。
空間複雜度為O(1)
思路3:雙指標(2)
先遍歷一次兩條連結串列求得其長度差x;
第二次遍歷長的連結串列先跑x步,短的連結串列再開始跑,當兩者相等時就是第一個公共結點。
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
if (!pHead1||!pHead2) return nullptr;
//先遍歷求得兩個連結串列的長度
int a1=0,a2=0;
ListNode* p1=pHead1;
ListNode* p2=pHead2;
while (p1)
{
a1++;
p1=p1->next;
}
while (p2)
{
a2++;
p2=p2->next;
}
p1=pHead1;
p2=pHead2;
int a=abs(a1-a2);
//長的先走
if (a1>a2)
{
while (a)
{
p1=p1->next;
a--;
}
}
else{
while (a)
{
p2=p2->next;
a--;
}
}
//再一起走
while (p1!=p2)
{
p1=p1->next;
p2=p2->next;
}
return p1;
}
};
時間複雜度和空間複雜度跟思路2其實是一樣的,並且更容易想到,但是程式碼沒有思路2的簡潔