標籤:
連結
牛客OJ:兩個鏈表的第一個公用結點
九度OJ:http://ac.jobdu.com/problem.php?pid=1505
GitHub代碼: 037-兩個鏈表的第一個公用結點
CSDN題解:劍指Offer–037-兩個鏈表的第一個公用結點
| 牛客OJ |
九度OJ |
CSDN題解 |
GitHub代碼 |
| 037-兩個鏈表的第一個公用結點 |
1505-兩個鏈表的第一個公用結點 |
劍指Offer–037-兩個鏈表的第一個公用結點 |
037-兩個鏈表的第一個公用結點 |
題意
題目描述
輸入兩個鏈表,找出它們的第一個公用結點。
暴力方法
最簡單直接的方法就是,對於第一個鏈表的每個節點,我們依次判斷其是不是第二條鏈表的公用結點
#include <iostream>using namespace std;// 調試開關#define __tmain main#ifdef __tmain#define debug cout#else#define debug 0 && cout#endif // __tmain#ifdef __tmainstruct ListNode{public : int val; struct ListNode *next;};#endifclass Solution{public: ListNode* FindFirstCommonNode(ListNode *leftHead, ListNode *rightHead) { ListNode *left = NULL; ListNode *right = NULL; // 迴圈第一個鏈表的每個結點 for(left = leftHead; left != NULL; left = left->next) { debug <<endl <<left->val <<" : "; // 依次判斷其在不在第二條鏈表中 for(right = rightHead; right != NULL; right = right->next) { debug <<right->val <<", "; if(left == right) { break; } } if(left == right) { break; } } return left; }};int __tmain( ){ ListNode common[2]; common[0].val = 6; common[0].next = &common[1]; common[1].val = 7; common[1].next = NULL; ListNode left[3]; left[0].val = 1; left[0].next = &left[1]; left[1].val = 2; left[1].next = &left[2]; left[2].val = 3; left[2].next = &common[0]; ListNode right[2]; right[0].val = 4; right[0].next = &right[1]; right[1].val = 5; right[1].next = &common[0]; Solution solu; ListNode *node = solu.FindFirstCommonNode(left, right); while(node != NULL) { debug <<node->val <<" "; node = node->next; } debug <<endl; return 0;}
靠右對齊兩個鏈表
如果兩個鏈表有公用節點,則它們的形狀必然是一個Y字形。
長鏈表先走,實現靠右對齊
先假設這兩個鏈表的長度相等,則我們可以同步遍曆這兩個鏈表,找到公用節點。現在有兩個鏈表,我們可以先分別求齊長度得其差n,然後遍曆長的那個鏈表n個節點,然後同步遍曆這兩個鏈表即可。
class Solution{public: ListNode* FindFirstCommonNode(ListNode *leftHead, ListNode *rightHead) { ListNode *left = leftHead; ListNode *right = rightHead; int leftLength = 0; int rightLength = 0; /// 首先計算兩個鏈表的長度 leftLength = GetListLength(left); rightLength = GetListLength(right); /// 對齊兩個鏈表 int length = 0; if(leftLength < rightLength) { // 右鏈表長 length = rightLength - leftLength; while(right != NULL && length > 0) { right = right->next; length--; } } else { // 左鏈表長 length = leftLength - rightLength; while(left != NULL && length > 0) { left = left->next; length--; } } // 兩個指標同步移動即可找到共同的結點 while(left != NULL && right != NULL) { if(left == right) { break; } left = left->next; right = right->next; } return ((left == right) ? left : NULL); } int GetListLength(ListNode *head) { ListNode *node = head; int length = 0; while(node != NULL) { length++; node = node->next; } return length; }};
將兩個鏈表拼接起來實現靠右對齊
前面那個思路中,長的鏈表先走一個長度差,從而保證兩個鏈表是靠右對齊的,但是這種方式,我們需要多次掃面鏈表,因為鏈表必須通過掃描才能擷取其長度
但是有沒有不需要多次掃描就可將兩個鏈表對齊的方法呢。
我們可以將兩個鏈表拼接在一起來實現其
left : left->right
>
right : right->left
這樣一來我們的鏈表就成了長度為M+N的兩個鏈表,而公用部分都在最後,而且是對齊的
class Solution{public: ListNode* FindFirstCommonNode(ListNode *leftHead, ListNode *rightHead) { ListNode *left= leftHead; ListNode *right = rightHead; debug <<(left == NULL ? -1 : left->val) <<", "; debug <<(right == NULL ? -1 : right->val) <<endl; while(left != right) { left = (left == NULL ? rightHead : left->next); right = (right == NULL ? leftHead : right->next); debug <<(left == NULL ? -1 : left->val) <<", "; debug <<(right == NULL ? -1 : right->val) <<endl; } return left; }};
棧的後進先出實現靠右對齊
由於棧是後進先出的,因此我們將兩個鏈表分別入棧,那麼棧頂的元素一定是最後一個元素。從而兩個鏈表是靠右對齊的,兩個棧同步彈出,當兩個棧頂的節點不相同時候,即是開始合并的前一個節點
class Solution{public: ListNode* FindFirstCommonNode(ListNode *leftHead, ListNode *rightHead) { ListNode *left = leftHead; ListNode *right = rightHead; stack<ListNode *> leftStack; stack<ListNode *> rightStack; /// 結點依次入棧 while(left != NULL) { //debug <<left->val <<endl; leftStack.push(left); left = left->next; } while(right != NULL) { //debug <<right->val <<endl; rightStack.push(right); right = right->next; } /// 開始同步彈出元素 while(leftStack.empty( ) != true && rightStack.empty( ) != true) { left = leftStack.top( ); right = rightStack.top( ); debug <<left->val <<", " <<right->val <<endl; /// 不相同的元素就是合并的前一個結點 if(left != right) { break; } leftStack.pop( ); rightStack.pop( ); } /// 不相同元素的下一個結點就是共同結點 if(left != right) { return left->next; } else { return NULL; } }};
利用高效的資料結構實現尋找結點
當然還有其他思路,我們可以使用一個尋找效率高的資料結構,比如hash或者map
>
先將第一個鏈表插入,然後因此判斷第二個鏈表的節點在不在map中,即可尋找到第一個共同結點
利用unordered_map,這個模板類是基於hash表實現的,速度與map、hashmap相比較快
class Solution{public: ListNode* FindFirstCommonNode(ListNode *leftHead, ListNode *rightHead) { unordered_map<ListNode*, bool> umap; ListNode* left = leftHead; while (left != NULL) { umap.insert(make_pair(left, 1 )); left = left->next; } ListNode* right = rightHead; while (right) { if (umap.count(right)>0) { return right; } right = right->next; } return NULL; }};
劍指Offer--037-兩個鏈表的第一個公用結點