問題描述(鏈表不帶環):
一個比較經典的問題,判斷兩個鏈表是否相交,如果相交找出他們的交點。
思路:
1、碰到這個問題,第一印象是採用hash來判斷,將兩個鏈表的節點進行hash,然後判斷出節點,這種想法當然是可以的。
2、當然採用暴力的方法也是可以的,遍歷兩個鏈表,在遍歷的過程中進行比較,看節點是否相同。
3、第三種思路是比較奇特的,在編程之美上看到的。先遍歷第一個鏈表到他的尾部,然後將尾部的next指針指向第二個鏈表(尾部指針的next本來指向的是null)。這樣兩個鏈表就合成了一個鏈表,判斷原來的兩個鏈表是否相交也就轉變成了判斷新的鏈表是否有環的問題了:即判斷單鏈表是否有環?
這樣進行轉換後就可以從鏈表頭部進行判斷了,其實並不用。通過簡單的瞭解我們就很容易知道,如果新鏈表是有環的,那麼原來第二個鏈表的頭部一定在環上。因此我們就可以從第二個鏈表的頭部進行遍歷的,從而減少了時間複雜度(減少的時間複雜度是第一個鏈表的長度)。
下圖是一個簡單的演示:
這種方法可以判斷兩個鏈表是否相交,但不太容易找出他們的交點。
4、仔細研究兩個鏈表,如果他們相交的話,那麼他們最後的一個節點一定是相同的,否則是不相交的。因此判斷兩個鏈表是否相交就很簡單了,分別遍歷到兩個鏈表的尾部,然後判斷他們是否相同,如果相同,則相交;否則不相交。示意圖如下:
判斷出兩個鏈表相交後就是判斷他們的交點了。假設第一個鏈表長度爲len1,第二個問len2,然後找出長度較長的,讓長度較長的鏈表指針向後移動|len1 - len2| (len1-len2的絕對值),然後在開始遍歷兩個鏈表,判斷節點是否相同即可。
下面給出一個簡單的實現:
typedef struct node_t
{
int data;//data
struct node_t *next; //next
}node;
node* find_node(node *head1, node *head2)
{
if(NULL == head1 || NULL == head2)
{
return NULL;//如果有爲空的鏈表,肯定是不相交的
}
node *p1, *p2;
p1 = head1;
p2 = head2;
int len1 = 0;
int len2 =0;
int diff = 0;
while(NULL != p1->next)
{
p1 = p1->next;
len1++;
}
while(NULL != p2->next)
{
p2 = p2->next;
len2++;
}
if(p1 != p2) //如果最後一個節點不相同,返回NULL
{
return NULL;
}
diff = abs(len1 - len2);
if(len1 > len2)
{
p1 = head1;
p2 = head2;
}
else
{
p1 = head2;
p2 = head1;
}
for(int i=0; i<diff; i++)
{
p1 = p1->next;
}
while(p1 != p2)
{
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
問題描述(假設鏈表帶環)
相當於求倆個鏈表的第一個公共結點,要麼找到,要麼爲空,那麼考慮到有鏈表可能有帶環的情況,情況共分爲下面三種:
(1)、兩個鏈表都帶環
分別獲取兩個鏈表環的入口點
判斷入口點是否相同
如果入口點相同,臨時修改鏈表爲 Y 形狀,處理完畢後恢復
如果入口點不相同,將一個環遍歷一週看是否能遇到另外一個環的入口點(防止單獨成環)
(2)、兩個鏈表都不帶環
都無環,當做 Y 形狀處理
(3)、一個帶環一個不帶環
這種情況肯定不相交,畫幾個圖就想明白了。
關於(1),可以看下面這個圖:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
// 找出鏈表的第一個公共結點
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
// 第一個公共結點
ListNode* commonNode = NULL;
if(pHead1 == NULL || pHead2 == NULL )
return NULL;
// (1) 判斷是否都有環
ListNode* circleNode1; // 鏈表1 環上的結點(快慢指針相遇點),如果無環則爲空
ListNode* circleNode2; // 鏈表2 環上的結點(快慢指針相遇點),如果無環則爲空
circleNode1 = GetMeetNode(pHead1);
circleNode2 = GetMeetNode(pHead2);
// (2) 如果都有環 or 如果都無環 or 只有一個有環
// 1. 都有環
if (circleNode1 && circleNode2){
// 鏈表1 環的入口點
ListNode* circleEnterNode1 = GetCircleEnterNode(pHead1, circleNode1);
// 鏈表2 環的入口點
ListNode* circleEnterNode2 = GetCircleEnterNode(pHead2, circleNode2);
// 如果入口點相同,臨時修改鏈表爲 Y 形狀,處理完畢後恢復
if(circleEnterNode1 == circleEnterNode2){
// 保存
ListNode* enterNodeNext = circleEnterNode1->next;
// 設置爲 Y 形,即無環
circleEnterNode1->next = NULL;
// 調用處理無環情況的函數
commonNode = GetFirstCommonNodeNoCircle(pHead1, pHead2);
// 恢復
circleEnterNode1->next = enterNodeNext;
}// 如果入口點不同,將一個環遍歷一週看是否能遇到另外一個環的入口點
else{
// 獲取其中一個環的長度
int circleLength = GetCircleLength(circleNode2);
// 遍歷一週看是否能遇到另外一個環的入口點,如果遇到則找到,否則未找到返回環
ListNode* next = circleEnterNode2->next;
// 遍歷一圈
while(circleLength--){
// 找到公共結點
if(next == circleEnterNode1){
commonNode = circleEnterNode1;
break;
}
next = next->next;
}
// 未找到公共結點
if(circleLength <= 0)
return NULL;
}
} // 2. 都無環
else if (circleNode1 == NULL && circleNode2 == NULL){
commonNode = GetFirstCommonNodeNoCircle(pHead1, pHead2);
}
// (3).其中一個無環, 肯定無公共結點
return commonNode;
}
private:
// 獲取帶環鏈表環上的一個結點(快慢指針相遇點),如果不帶環則返回空
ListNode* GetMeetNode(ListNode* pHead){
if(pHead == NULL)
return NULL;
ListNode* pFast = pHead;
ListNode* pSlow = pHead;
// 快慢指針法
while(pFast && pFast->next){
pFast = pFast->next->next;
pSlow = pSlow->next;
if(pFast == pSlow){
return pFast;
}
}
return NULL;
}
// 根據快慢指針相遇點,過去環的入口點
ListNode* GetCircleEnterNode(ListNode* pHead, ListNode* meetNode){
if(pHead == NULL || meetNode == NULL)
return NULL;
while(pHead != meetNode){
pHead = pHead->next;
meetNode = meetNode->next;
}
return pHead;
}
// 判斷兩個鏈表是否相交
bool isCross(ListNode* pHead1, ListNode* pHead2){
if(pHead1 == NULL || pHead2 == NULL){
return false;
}
while(pHead1->next)
pHead1 = pHead1->next;
while(pHead2->next)
pHead2 = pHead2->next;
if(pHead1 == pHead2)
return true;
return false;
}
// 獲取鏈表長度
int getListLength(ListNode* pHead){
int length = 0;
while(pHead){
length++;
pHead = pHead->next;
}
return length;
}
// 處理 Y 形狀
ListNode* GetFirstCommonNodeNoCircle(ListNode* pHead1, ListNode* pHead2){
if(pHead1 == NULL || pHead2 == NULL)
return NULL;
// 如果不相交,直接返回NULL
if (!isCross(pHead1, pHead2))
return NULL;
int step = 0;
int lengthList1 = getListLength(pHead1);
int lengthList2 = getListLength(pHead2);
step = lengthList1 - lengthList2;
if(step > 0){
while(step--)
pHead1 = pHead1->next;
}
else if(step<0){
while(step++)
pHead2 = pHead2->next;
}
while(pHead1 != pHead2){
pHead1 = pHead1->next;
pHead2 = pHead2->next;
}
return pHead1;
}
// 獲取帶環鏈表環的長度
int GetCircleLength(ListNode* meetNode){
int length = 0;
if(meetNode == NULL)
return length;
ListNode* next = meetNode->next;
while(next != meetNode){
length++;
next = next->next;
}
return length;
}
};