第一种情况:两个链表均不含有环
思路:
1、直接法
采用暴力的方法,遍历两个链表,判断第一个链表的每个结点是否在第二个链表中,时间复杂度为O(len1*len2),耗时很大。
2、hash计数法
以链表节点地址为值,遍历第一个链表,使用Hash保存所有节点地址值,结束条件为到最后一个节点(无环)或Hash中该地址值已经存在(有环)。
再遍历第二个链表,判断节点地址值是否已经存在于上面创建的Hash表中。
这个方面可以解决题目中的所有情况,时间复杂度为O(m+n),m和n分别是两个链表中节点数量。由于节点地址指针就是一个整型,假设链表都是在堆中动态创建的,可以使用堆的起始地址作为偏移量,以地址减去这个偏移量作为Hash函数
3、先遍历第一个链表到他的尾部,然后将尾部的next指针指向第二个链表(尾部指针的next本来指向的是null)。这样两个链表就合成了一个链表,判断原来的两个链表是否相交也就转变成了判断新的链表是否有环的问题了:即判断单链表是否有环?
这样进行转换后就可以从链表头部进行判断了,其实并不用。通过简单的了解我们就很容易知道,如果新链表是有环的,那么原来第二个链表的头部一定在环上。因此我们就可以从第二个链表的头部进行遍历的,从而减少了时间复杂度(减少的时间复杂度是第一个链表的长度)。
判断出两个链表相交后就是判断他们的交点了。假设第一个链表长度为len1,第二个问len2,然后找出长度较长的,让长度较长的链表指针向后移动|len1 - len2| (len1-len2的绝对值),然后在开始遍历两个链表,判断节点是否相同即可。
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;
typedef struct Node
{
int data;
struct Node *next;
}Node;
typedef struct Node *list;
void In_List(list *head)
{
*head = (Node*)malloc(sizeof(Node));
(*head)->next = NULL;
}
void create_list(list *head1, list *head2)
{
Node*p1 = (Node*)malloc(sizeof(Node));//单链表1
Node*p2 = (Node*)malloc(sizeof(Node));
Node*p3 = (Node*)malloc(sizeof(Node));
Node*p4 = (Node*)malloc(sizeof(Node));//单链表2
Node*p5 = (Node*)malloc(sizeof(Node));
Node*p6 = (Node*)malloc(sizeof(Node));
Node*p7 = (Node*)malloc(sizeof(Node));//公共
Node*p8 = (Node*)malloc(sizeof(Node));
Node*p9 = (Node*)malloc(sizeof(Node));
p1 = *head1;
p4 = *head2;
p1->data = 1;
p2->data = 2;
p3->data = 3;
p4->data = 4;
p5->data = 5;
p6->data = 6;
p7->data = 7;
p8->data = 8;
p9->data = 9;
p1->next = p2;
p2->next = p3;
p3->next = p7;
p7->next = p8;
p8->next = p9;
p9->next = NULL;
p4->next = p5;
p5->next = p6;
p6->next = p7;
}
Node *Find_Node(list *head1, list *head2)
{
if (head1 == NULL || head2 == NULL)
{
return NULL;//若其中一个链表为空则不相交
}
Node *p1 = *head1;
Node *p2 = *head2;
int len1 = 0;
int len2 = 0;
int diff = 0;//绝对值
while (p1->next != NULL)//求链表1的长度
{
p1 = p1->next;
len1++;
}
while (p2->next != NULL)//求链表2的长度
{
p2 = p2->next;
len2++;
}
if (p1 != p2)//如果最后一个结点不相同,则没有相交结点
{
return NULL;
}
diff = abs(len1 - len2);//len1-len2的绝对值
//长的是p1,短的是p2
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;
}
int main()
{
list mylist1;
list mylist2;
In_List(&mylist1);
In_List(&mylist2);
create_list(&mylist1, &mylist2);
Node *p = Find_Node(&mylist1, &mylist2);
if (p == NULL)
cout << "是两个不相交的链表" << endl;
else
cout << "链表相交,相交节点为:" << p->data << endl;
system("pause");
return 0;
}
运行结果:
第二种情况:两个链表中有环
如果链表有环且相交,那么这两个链表都是有环的。
找到第一个链表的环点,然后将环断开(当然不要忘记了保存它的下一个节点),然后再来遍历第二个链表,如果发现第二个链表从有环变成了无环,那么他们就是相交的嘛,否则就是不相交的了。
当两个有环的链表相交时,有以下两种情况:
代码实现:
void create_list(list *head1, list *head2)
{
Node*p1 = (Node*)malloc(sizeof(Node));//单链表1
Node*p2 = (Node*)malloc(sizeof(Node));
Node*p3 = (Node*)malloc(sizeof(Node));
Node*p4 = (Node*)malloc(sizeof(Node));//单链表2
Node*p5 = (Node*)malloc(sizeof(Node));
Node*p6 = (Node*)malloc(sizeof(Node));
Node*p7 = (Node*)malloc(sizeof(Node));//公共
Node*p8 = (Node*)malloc(sizeof(Node));
Node*p9 = (Node*)malloc(sizeof(Node));
p1 = *head1;
p9 = *head2;
p1->data = 1;
p2->data = 2;
p3->data = 3;
p4->data = 4;
p5->data = 5;
p6->data = 6;
p7->data = 7;
p8->data = 8;
p9->data = 9;
p1->next = p2;
p2->next = p3;
p3->next = p4;
p4->next = p5;
p5->next = p6;
p6->next = p7;
p7->next = p8;
p8->next = p2;
p9->next = p2;
}
//求入口结点
Node *FindEntrance(list *head)
{
Node *slow = *head;
Node *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 (fast != slow)
{
slow = slow->next;
fast = fast->next;
}
return fast;
}
//求环的长度
int GetPathDistance(list *head)
{
Node *slow = *head;
Node *fast = *head;
while (fast&&fast->next)
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast)
break;
}
if (fast == NULL || fast->next == NULL)
return 0;
Node *meet = fast;
slow = slow->next;
fast = fast->next->next;
int distance = 0;
while (slow != fast)
{
distance++;
slow = slow->next;
fast = fast->next->next;
}
return distance + 1;
}
//求链表总长度
int GetAllPathLen(list *head)
{
int d1 = 0;//从头到入口的距离
int d2 = 0;//带环的距离
int d = 0;//总距离
Node*p1 = *head;
while (p1->next != (FindEntrance(head)))
{
d1++;
p1 = p1->next;
}
d2 = GetPathDistance(head);
d = d1 + d2;
return d;
}
//是否带环
bool IsExitLoop(list *head)//是否带环
{
Node *slow = *head;
Node *fast = *head;
while (fast&&fast->next)
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast)
break;
}
if (fast == NULL || fast->next == NULL)
return false;
return true;
}
//带环链表是否相交
Node* Find_Node(list *head1, list *head2)
{
Node *p1 = FindEntrance(head1);
p1 = p1->next;
Node *p2 = p1;
p1->next = NULL;
if (IsExitLoop(head2) == 0)
{
p1->next = p2->next;
int len1 = GetAllPathLen(head1);
int len2 = GetAllPathLen(head2);
int diff = abs(len1 - len2);//len1-len2的绝对值
//长的是p1,短的是p2
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;
}
else
return NULL;
}
运行结果:
在这种情况下,两个链表的交点在环点之前,可以将环点切断,这样就变成了两个无环的链表求相交点。可使用以上方法。
另一种情况为:
在这种情况下,不存在所谓的相交点。