資料結構: 兩個單鏈表相交的一系列問題
阿新 • • 發佈:2019-01-02
資料結構: 兩個單鏈表相交的一系列問題
這個是一個比較綜合的問題:
-
若兩個單鏈表一個為有環,一個無環. 那麼肯定不能相交.
-
若二者都沒有環, 問題就轉化為 兩個無環單鏈表是否相交,方法就是
快慢指標
,是否能找到第一個相交的節點. -
若二者都有環,那麼問題變成了兩個有換單鏈表是否相交.
第一,先找到二者是否相交.
第二,若相交則需要遍歷一遍找到相交點.
該題可以分解為三個問題:
1 判斷一個連結串列是否有環
#include "MyInclude.h" /* 問題一: 判斷一個連結串列是否有環. 如果有,返回第一個進入環的節點. 如果沒有,返回NULL */ // 快慢指標,若存在環,則一定會在環中的某一個節點相遇. // 然後再找第入口 // https://blog.csdn.net/l294265421/article/details/50478818 ListNode* getLoopNode(ListNode* head) { // 至少有三個節點 if (head == NULL||head->next==NULL||head->next->next==NULL) { return NULL; } // 初始值,slow走了一步,fast走了兩步 ListNode* slow = head->next; ListNode* fast = head->next->next; while (slow != fast) { // 若走到了NULL,則無環 if (fast->next == NULL || fast->next->next == NULL) { return NULL; } slow = slow->next; fast = fast->next->next; } // 存在環,找環的第一個節點. slow = head; while (slow != fast) { slow = slow->next; fast = fast->next; } return slow; }
2 如何判斷兩個無環連結串列相交?
/* 若兩個無環連結串列相交,返回相交節點, 否則,返回NULL */ int getLen(ListNode* head) { int len = 0; while (head != NULL) { len++; head = head->next; } return len; } ListNode* noLoop(ListNode* head1, ListNode* head2) { if (head1 == NULL || head2 == NULL) return NULL; ListNode* p1 = head1; ListNode* p2 = head2; // 統計長度 int len1 = getLen(p1); int len2 = getLen(p2); // 假設p1是長度比較長的 if (len1 < len2) { ListNode* tmp = p1; p1 = p2; p2 = tmp; } int absDiff = (len1 - len2); while (absDiff != 0) { p1 = p1->next; absDiff--; } while (p1 != NULL&&p2 != NULL&&p1 != p2) { p1 = p1->next; p2 = p2->next; } if (p1 == NULL || p2 == NULL) { return NULL; } else { return p1; } }
3 如何判斷兩個有環連結串列是否相交?
/* 判斷兩個有環連結串列是否相交 若相交,返回第一個相交點 若不相交,返回NULL */ // 求出有環連結串列的環入口節點 ListNode* getLoopEnter(ListNode* head) { ListNode* slow = head->next; ListNode* fast = head->next->next; while (slow != fast) { slow = slow->next; fast = fast->next; } slow = head; while (slow != fast) { slow = slow->next; fast = fast->next; } return slow; } ListNode* BothLoop(ListNode* head1, ListNode* head2) { // 1.先獲得兩個連結串列各自的環的入口 ListNode* loop1 = getLoopEnter(head1); ListNode* loop2 = getLoopEnter(head2); // 2.或在入口處相等,那麼返回 if (loop1 == loop2) { return loop1; } // 不相等,則遍歷一遍一個環自身,看是否能找到另一個環的入口節點,有則為相交,沒有則為不相交 ListNode* p = loop1->next; while (p != loop1) { if (p == loop2) { return p; } p = p->next; } return NULL; }
原題解答:
/*
檢測兩個單鏈表是否相交,
若相交,返回第一個相交的節點
若不相交,返回NULL
*/
/*
分析:
若一個連結串列有環,一個連結串列沒有換,則不可能相交
若兩個單鏈表都有環,
則需要檢測兩個有環連結串列是否相交
若二者都沒有,
則判斷兩個無環的單鏈表是否相交
*/
// 1.判斷一個連結串列是否有環
ListNode* hasLoop(ListNode* head) {
if (head == NULL || head->next == NULL || head->next == NULL)
return NULL;
ListNode* slow = head->next;
ListNode* fast = head->next->next;
while (slow != fast) {
// 走到NULL,說明沒有環
if (fast->next == NULL || fast->next->next == NULL) {
return NULL;
}
slow = slow->next;
fast = fast->next->next;
}
slow = head;
while (slow != fast) {
slow = slow->next;
fast = fast->next;
}
return slow;
}
// 2.兩個無環連結串列是否相交
int getLen(ListNode* head) {
int len = 0;
while (head != NULL) {
len++;
head = head->next;
}
return len;
}
ListNode* noLoop(ListNode* head1, ListNode* head2) {
if (head1 == NULL || head2 == NULL)
return NULL;
ListNode* p1 = head1;
ListNode* p2 = head2;
// 統計長度
int len1 = getLen(p1);
int len2 = getLen(p2);
// 假設p1是長度比較長的
if (len1 < len2) {
ListNode* tmp = p1;
p1 = p2;
p2 = tmp;
}
int absDiff = (len1 - len2);
while (absDiff != 0) {
p1 = p1->next;
absDiff--;
}
while (p1 != NULL&&p2 != NULL&&p1 != p2) {
p1 = p1->next;
p2 = p2->next;
}
if (p1 == NULL || p2 == NULL) {
return NULL;
} else {
return p1;
}
}
// 3.兩個有環連結串列是否相交,
ListNode* bothLoop(ListNode* Loop1, ListNode* Loop2) {
if (Loop1 == Loop2) {
return Loop1;
}
else {
ListNode* p = Loop1->next;
while (p != Loop1) {
if (p == Loop2)
return p;
p = p->next;
}
return NULL;
}
}
ListNode* getInterSectNode(ListNode* head1, ListNode* head2) {
// 1. 判斷一個連結串列是否有環
ListNode* Loop1 = hasLoop(head1);
ListNode* Loop2 = hasLoop(head2);
// 2. 若二者都是空,
if (Loop1 == NULL&&Loop2 == NULL) {
return noLoop(head1, head2);
}
if (Loop1 != NULL&&Loop2 != NULL) {
return bothLoop(Loop1, Loop2);
}
return NULL;
}