链表的结构
typedef int DataType;
typedef struct ListNode
{
DataType data;
struct ListNode* next;
}ListNode;
一、链表带环的判断、若带环,求环的长度、环的入口
1)带环的判断
ListNode* Ring(ListNode *list)
{
ListNode *Slow = list;
ListNode *Fast = list;
if (list == NULL || list->next == NULL)
{
return NULL;
}
Slow = Slow->next;
Fast = Fast->next->next;
while (Slow != Fast)
{
if (Fast->next == NULL || Fast->next->next == NULL)
{
return NULL;
}
Slow = Slow->next;
Fast = Fast->next->next;
}
//返回相遇点
return Slow;
}
思路:
通过快慢指针的移动来判断是否带环。慢指针每次+1,快指针每次+2。如果带环的话,快慢指针就一定会有相遇的一个节点,返回相遇的节点,为下面判断环的长度和入口做准备。如果快指针每次+3、+4……就不一定能够相遇,有可能存在刚好跳过相遇的点。
2)环的长度
int RingLength(ListNode *First)
{
ListNode *Slow = First;
ListNode *Fast = First;
size_t count = 1;
Fast = Fast->next->next;
Slow = Slow->next;
while (Fast != Slow)
{
count++;
Fast = Fast->next->next;
Slow = Slow->next;
}
return count;
}
思路:
让快指针和慢指针在环内从相遇点再走一圈。下一次相遇的时候,一定是慢指针走了一圈,快指针走了两圈。用一个计数记录下相遇的次数,这个就是环的长度。
3)环的入口
ListNode *RingEntry(ListNode *begin, ListNode *ring)
{
if (begin == NULL || ring == NULL)
{
return NULL;
}
while (begin != ring)
{
begin = begin->next;
ring = ring->next;
}
return begin;
}
思路:
begin指针从单链表的开头向前,ring指针从相遇点向前。两个指针每次+1,两个指针相遇的节点就是入口点。
二、判断两个链表是否相交,若相交,求交点。(假设链表不带环)
ListNode *ListCross(ListNode *List1, ListNode *List2)
{
ListNode *LongList = List1;
ListNode *ShortList = List2;
ListNode *Tmp = NULL;
size_t long_length = 0;
size_t short_length = 0;
size_t tmp_length = 0;
if (List1 == NULL || List2 == NULL)
{
return NULL;
}
//判断是不是相交
while (List1)
{
++long_length;
List1 = List1->next;
}
while (List2)
{
++short_length;
List2 = List2->next;
}
if (List1 != List2)
{
return NULL;
}
//求交点
if (short_length > long_length)
{
Tmp = LongList;
LongList = ShortList;
ShortList = Tmp;
tmp_length = long_length;
long_length = short_length;
short_length = tmp_length;
}
tmp_length = long_length - short_length;
while (tmp_length--)
{
LongList = LongList->next;
}
while (LongList != ShortList)
{
LongList = LongList->next;
ShortList = ShortList->next;
}
return LongList;
}
思路:
1、两链表相交的模型:
2、判断是是否相交:
分别遍历两个链表,如果尾节点的地址相同,就证明两链表相交
3、求交点
两个链表分别遍历一次的时候同时记录下节点个数,将长链表的头指针先移动相差的节点数,再同时向前移动节点,两指针地址相同时,节点就是相交点。
三、判断两个链表是否相交,若相交,求交点。(假设至少一个链表带环)
ListNode *ListCrossRing(ListNode *List1, ListNode *List2)
{
ListNode *MeetNode1 = Ring(List1);
ListNode *MeetNode2 = Ring(List2);
//默认长的是List1
ListNode *LongList = List1;
ListNode *ShortList = List2;
size_t LongLenth = 0;
size_t ShortLenth = 0;
size_t Tmp = 0;
//有一个链表不带环,不可能相交
if (Ring(List1) == NULL || Ring(List2) == NULL)
{
return NULL;
}
//两个链表都带环
//两链表不相交
MeetNode1 = MeetNode1->next;
while (MeetNode1 != MeetNode1 && MeetNode1 != MeetNode2)
{
MeetNode1 = MeetNode1->next;
}
if (MeetNode1 != MeetNode2)
{
return NULL;
}
//两链表相交
//只有一个切入点
MeetNode1 = Ring(List1);
MeetNode2 = Ring(List2);
while (List1 != MeetNode1)
{
List1 = List1->next;
MeetNode1 = MeetNode1->next;
}
while (List2 != MeetNode2)
{
List2 = List2->next;
MeetNode2 = MeetNode2->next;
}
//两链表不在环上相交,判断条件:两入口点一样
if (MeetNode1 == MeetNode2)
{
while (LongList != MeetNode1)
{
LongList = LongList->next;
LongLenth++;
}
while (ShortList != MeetNode2)
{
ShortList = ShortList->next;
ShortLenth++;
}
if (LongLenth < ShortLenth)
{
Tmp = LongLenth;
LongLenth = ShortLenth;
ShortLenth = Tmp;
LongList = List2;
ShortList = List1;
}
Tmp = LongLenth - ShortLenth;
while (Tmp--)
{
LongList = LongList->next;
}
while (LongList != ShortList)
{
LongList = LongList->next;
ShortList = ShortList->next;
}
return LongList;
}
//两链表在环上相交,两入口点不一样,返回其中一个作为交点
else
{
return MeetNode1;
}
}
思路:
1、带环相交情况分析
2、情况一
对两个链表直接判环,有一个没有环,就符合情况一,没有相交
3、情况二
也是判环,用每个链表返回的相遇点比较,地址一样,表示是同一个环,否则,没有相交,不共用一个环
4、情况三
判断两者是不是在同一个环上面相交,可以用是不是具有同一个切入点判断。判断方法与上述第二题相同
5、情况四
if……else……剩下的就是这个情况了,即没有情况三的同一个切入点不满足