数据结构: 两个单链表相交的一系列问题
这个是一个比较综合的问题:
-
若两个单链表一个为有环,一个无环. 那么肯定不能相交.
-
若二者都没有环, 问题就转化为 两个无环单链表是否相交,方法就是
快慢指针
,是否能找到第一个相交的节点. -
若二者都有环,那么问题变成了两个有换单链表是否相交.
第一,先找到二者是否相交.
第二,若相交则需要遍历一遍找到相交点.
该题可以分解为三个问题:
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;
}