思考:
两个链表相交,其特点为两个链表共用一个尾;但这两个链表可分为以下两个情况:
情况1:两个链表不带环
情况2:两个链表可能带环
其中情况2又可分为以下几种问题:
- 两个链表都不带环
和情况1是相同的(具体如图1)- 一个链表带环,一个不带环
结论:一定不相交- 两个链表都带环
a. 不相交
b. 相交
1)环内相交
2)环外相交
分别来看这几种情况:
情况1: 两个链表不带环
问题1:链表是否相交
问题2:求解相交点
求解相交点时,只需让两个指针同时遍历两个链表,当两个两个指针相遇时的节点即为相交节点,但这样处理的条件是:两个链表的长度相同;当链表长度不相同时,只需让较长链表指针先走两链表长度相差的步数即可
情况2:两个链表可能带环
两个链表都不带环
和情况1是相同的(具体看第一个图)一个链表带环,一个不带环
结论:一定不相交两个链表都带环
a. 不相交
b. 相交
1)环内相交
2)环外相交
情景1:两个链表都不带环
转化为情况1的问题,具体请看情况1的分析
情景2:一个链表带环,一个链表不带环
一定不相交
情景3:两个都带环
问题1: 两个链表是否相交
问题2:两个在环内相交还是环外相交
问题3:链表相交点
a. 环外相交
环外相交,入口点相同,故而可转化为不带环链表的同类题,可将入口点视为两个链表不带环部分的尾,从而求的交点(具体过程可看问题2的解决方案)
b. 环内相交 环内相交,入口点不同,相遇点可能相同可能不同,故而没有条件可以借助求得相交点,在这里相交点不得而知
具体代码:
情况1:
判断链表是否相交
int SListIsCrossNode(SListNode* list1, SListNode* list2)
{
assert(list1);
assert(list2);
while (list1 && list1->_next)
{
list1 = list1->_next;
}
while (list2 && list2->_next)
{
list2 = list2->_next;
}
//都走到尾,相同即相交
if (list1 == list2)
return 1;
else
return 0;
}
获取相交点
SListNode* GetCrossNode(SListNode* list1, SListNode* list2)
{
assert(list1);
assert(list2);
size_t n1 = 0;
size_t n2 = 0;
SListNode* cur1 = list1;
SListNode* cur2 = list2;
//求链表1的长度
while (cur1)
{
cur1 = cur1->_next;
++n1;
}
//求链表2的长度
while (cur2)
{
cur2 = cur2->_next;
++n2;
}
//选出两个链表当中较长的一个链表
size_t num = abs(n1 - n2);
SListNode* longlist = list1;
SListNode* shortlist = list2;
if (n1 < n2)
{
longlist = list2;
shortlist = list1;
}
//让较长的链表先走链表的长度差步
while (num--)
{
longlist = longlist->_next;
}
//两个链表同时走
while (longlist != shortlist)
{
longlist = longlist->_next;
shortlist = shortlist->_next;
}
return longlist;
}
情况2:两个链表可能带环
是否相交:
//两个链表是否相交(可能带环)
int SListIsCrossNode1(SListNode* list1, SListNode* list2)
{
assert(list1);
assert(list2);
//是否带环,若带环返回相遇点
SListNode* meet1 = SListIsCircle(list1);
SListNode* meet2 = SListIsCircle(list2);
//如果带环,求入口点
SListNode* entry1;
SListNode* entry2;
if (meet1 == NULL && meet2 == NULL)
{
//两个都不带环,转换为不带环的情况
//相交返回1,不相交返回0
return SListIsCrossNode(list1, list2);
}
else if (meet1==NULL || meet2==NULL)
{
//一个带环,一个不带环,则一定不相交,返回2
return 2;
}
else
{
//两个都带环,相遇点一定会在环内相遇,如果遇到则相交,遇不到则不相交
SListNode* cur = meet1;
while (cur->_next != meet1)
{
if (cur == meet2)
{
//相遇点在环内相遇,则两环相交
entry1 = SListNodeEntryNode(list1, meet1);
entry2 = SListNodeEntryNode(list2, meet2);
if (entry1 == entry2)
return 3;//入口点相同,则环外相交
else
return 4;//入口点不同,环内相交
}
cur = cur->_next;
}
//判断最后一个节点是否是相遇点
if (cur == meet2)
{
//相遇点在环内相遇,则两环相交
entry1 = SListNodeEntryNode(list1, meet1);
entry2 = SListNodeEntryNode(list2, meet2);
if (entry1 == entry2)
return 3;//入口点相同,则环外相交
else
return 4;//入口点不同,环内相交
}
//如果meet1没有遇到meet2,则两个带环链表没有相交
return -1;
}
}
获取相交点:
//获取相交点(可能带环)
SListNode* GetCrossNode1(SListNode* list1, SListNode* list2)
{
assert(list1);
assert(list2);
size_t n = 0;
SListNode* crossnode = NULL;//保存相交点
n = SListIsCrossNode1(list1, list2);
if (n == 1)
{
//两个都不带环相交,则转换为不带环的相交问题
crossnode = GetCrossNode(list1, list2);
}
else if (n == 3)
{
//两个链表带环,并且环外相交,入口点相同
//计算头到入口点的长度,让长的先走距离差,同时走
size_t len1 = 0, len2 = 0;
SListNode* cur1 = list1;
SListNode* cur2 = list2;
SListNode* meet1 = SListIsCircle(list1);//链表1的相遇点
SListNode* entry = SListNodeEntryNode(list1, meet1);//环的入口点
while (cur1 != entry)
{
//计算链表1到入口点的长度
++len1;
cur1 = cur1->_next;
}
while (cur2 != entry)
{
//计算链表2到入口点的长度
++len2;
cur2 = cur2->_next;
}
size_t num = abs(len1 - len2);
SListNode* longlist = list1;
SListNode* shortlist = list2;
//选出两个链表当中头到入口点距离较长的一个
if (len1 < len2)
{
longlist = list2;
shortlist = list1;
}
//较长链表先走长度之差
while (num--)
{
longlist = longlist->_next;
}
while (longlist != shortlist)
{
longlist = longlist->_next;
shortlist = shortlist->_next;
}
if (longlist == shortlist)
{
crossnode = longlist;
}
}
else
{
//如果环内相交,找不到入口点
return NULL;
}
return crossnode;
}