判斷兩個單向鏈表是否相交,并找出他們的交點。
這個問題我們分三種情況討論:
一. 兩個鏈表都不存在環
問題思路:
- 遍歷兩個鏈表,在遍歷的過程中進行比較,看節點是否相同。
-
先遍歷第一個鏈表到他的尾部,然后將尾部的next指針指向第二個鏈表。這樣兩個鏈表就合成了一個鏈表,判斷原來的兩個鏈表是否相交也就轉變成了判斷新的鏈表是否有環的問題了。 如果新的鏈表有環則第二個鏈表從頭節點遍歷一定存在環。
圖片.png - 如果兩個沒有環的鏈表相交,則這兩個鏈表的尾節點一定相等。因此這種方式判斷兩個鏈表是否相交,就分別遍歷兩個鏈表到尾部,判斷是否相等,如果相等就是相交,否則不相交。
圖片.png
第三那種情況的代碼實現思路:
假設第一個鏈表長度為len1,第二個問len2,然后找出長度較長的,讓長度較長的鏈表指針向后移動|len1 - len2| (len1-len2的絕對值),然后在開始遍歷兩個鏈表,判斷節點是否相同即可。代碼如下
//鏈表節點的定義
typedef struct Node {
int data;
struct Node *pNext;
} NODE, *PNODE;
PNODE *findNode(PNODE head1, PNODE head2) {
if (head1 == NULL || head2 == NULL) {
//如果鏈表為空則很定不相交
return NULL;
}
PNODE p1, p2;
p1 = head1;
p2 = head2;
int len1 = 0; //第一個鏈表的長度
int len2 = 0; //第二個鏈表的長度
int diff = 0;
while (NULL != p1->pNext) {
p1 = p1->pNext;
len1++;
}
while (NULL != p2->pNext) {
p2 = p2->pNext;
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->pNext;
}
while (p1 != p2) {
p1 = p1->pNext;
p2 = p2->pNext;
}
return p1;
}
二. 如果一個鏈表中有環,另一個鏈表沒有環。
這種情況則兩個鏈表一定不相交。因為一旦相交則另一個鏈表,一定存在環。
圖片.png
三. 鏈表中有環
- 判斷鏈表是否存在環,辦法為:設置兩個指針(fast, slow),初始值都指向頭,slow每次前進一步,fast每次前進二步,如果鏈表存在環,則fast必定先進入環,而slow后進入環,兩個指針必定相遇。(當然,fast先行頭到尾部為NULL,則為無環鏈表)程序如下:
bool IsExitsLoop(slist *head){
slist *slow = head, *fast = head;
while ( fast && fast->next ) {
slow = slow->next;
fast = fast->next->next;
if ( slow == fast ) break;
}
return !(fast == NULL || fast->next == NULL);
}
二、找到環的入口點當fast若與slow相遇時,slow肯定沒有走遍歷完鏈表,而fast已經在環內循環了n圈(1<=n)。假設slow走了s步,則fast走了2s步(fast步數還等于s 加上在環上多轉的n圈),設環長為r,則:2s = s + nrs= nr設整個鏈表長L,入口環與相遇點距離為x,起點到環入口點的距離為a。a + x = nra + x = (n – 1)r +r = (n-1)r + L - aa = (n-1)r + (L – a – x)(L – a – x)為相遇點到環入口點的距離,由此可知,從鏈表頭到環入口點等于(n-1)循環內環+相遇點到環入口點,于是我們從鏈表頭、與相遇點分別設一個指針,每次各走一步,兩個指針必定相遇,且相遇第一點為環入口點。程序描述如下:
slist* FindLoopPort(slist *head){
slist *slow = head, *fast = head;
while ( fast && fast->next ) {
slow = slow->next;
fast = fast->next->next;
if ( slow == fast ) break;
} if (fast == NULL || fast->next == NULL)
return NULL; slow = head;
while (slow != fast) {
slow = slow->next;
fast = fast->next;
}
return slow;
}