如何判断两个无环单链表是否交叉
单链表相交指的是两个链表存在完全重合的部分
方法一:hash法
由于结点的地址可以作为结点的唯一标识,因此可以通过判断两个链表中的结点是否有相同的地址来判断链表是否相交。首先遍历链表head1,把遍历到的所有结点的地址存放到hash表中;接着遍历链表head2,每遍历到一个结点,就判断这个结点的地址在hash表中是否存在,如果存在,那么说明两个链表相交并且当前遍历到的结点就是它们的相交点
由于需要分别遍历两个链表,因此算法的时间复杂度为O(n1+n2),n1与n2分别为两个链表的长度。此外,由于需要申请额外的存储空间来存储链表head1中结点的地址,因此空间复杂度为O(n1)
方法二:首尾相接法
将这两个链表首尾相连(例如,将head1尾结点链接到head2的头指针),然后检测这个链表是否存在环,如果存在,则两个链表相交,检测出来的环入口即为相交的第一个点
方法三:尾结点法
如果两个链表相交,那么两个链表从相交点到链表结束都是相同的结点,必然是Y字形,所以判断两个链表的最后一个结点是否相同即可。即先遍历一个链表,直到尾部,再遍历另外一个链表,如果也可以走到相同的尾结点,则两个链表相交,这时记下两个链表的长度n1、n2,再遍历一次,长链表结点先出发前进|n1-n2|步,之后两个链表同时前进,每次一步,相遇的第一个点即为两个链表相交的第一个点
struct ListNode {
int val;
ListNode *next;
};
ListNode* IsIntersect(ListNode* head1, ListNode* head2)
{
if (head1 == NULL || head1->next == NULL || head2 == NULL || head2->next == NULL)
{
return NULL;
}
ListNode* temp1 = head1->next;
ListNode* temp2 = head2->next;
int n1 = 0;
int n2 = 0;
//遍历head1,找到尾结点,同时记录head1的长度
while (temp1->next)
{
temp1 = temp1->next;
++n1;
}
//遍历head2,找到尾结点,同时记录head2的长度
while (temp2->next)
{
temp2 = temp2->next;
++n2;
}
//head1与head2有相同的尾结点
if (temp1 == temp2)
{
//长链表先走|n1-n2|步
if (n1 > n2)
{
while (n1 - n2 > 0)
{
head1 = head1->next;
--n1;
}
}
else if (n2 > n1)
{
while (n2 - n1 > 0)
{
head2 = head2->next;
--n2;
}
}
//两个链表同时前进,找出相同的结点
while (head1 != head2)
{
head1 = head1->next;
head2 = head2->next;
}
return head1;
}
else
{
return NULL;
}
}
设重叠的结点个数为L,则总共对链表进行的遍历次数为2(n1+n2)-L,因此算法的时间复杂度为O(n1+n2);由于只使用了常数个额外指针变量,因此空间复杂度为O(1)
如果单链表有环,如何判断两个链表是否相交
- 如果一个单链表有环,另一个没环,那么它们肯定不相交
- 如果两个单链表都有环并且相交,那么这两个链表一定共享这个环。判断两个有环的链表是否相交的方法为:首先采用前面介绍的方法找到链表head1中环的入口点p1,然后遍历链表head2,判断链表中是否包含结点p1,如果包含,则这两个链表相交,否则不相交。找相交点的方法为:把结点p1看作两个链表的尾结点,这样就可以把问题转换为求两个无环链表相交点的问题