1.判断单链表是否带环?若带环,求环的长度?求环的入口点?
2.判断两个链表是否相交,若相交,求交点。(假设链表不带环)
3.判断入口点
2.判断两个链表是否相交,若相交,求交点。(假设链表不带环)
3.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】
函数如下:
typedef int DataType;
typedef struct ListNode
{
DataType data;
ListNode* next;
}ListNode;
int GetCycleLen(ListNode* plist); //求环长度(若无环则返回0)
ListNode* IsCycle(ListNode* plist); //求带环单链表快慢指针相遇点(无环返回NULL)
ListNode* GetCycleEntry(ListNode* plist); //求带环单链表入口点(无环返回NULL)
ListNode* IsCrossNoCycle(ListNode* plist1, ListNode* plist2); //判断2个无环链表是否相交
ListNode* IsCross(ListNode* plist1, ListNode* plist2); //判定2个链表是否相交(分类讨论)
1.判断是否带环
思路:快慢指针,若能相遇则带环
ListNode* IsCycle(ListNode* plist)
{
ListNode* fast = plist, *slow = plist; //注意这种定义形式
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
{
return slow;
}
}
return NULL;
}
思路:从快慢指针相遇点走一圈,用计数器计数
int GetCycleLen(ListNode* plist)
{
//先判断是否为带环单链表,不是的话返回0
ListNode* meet = IsCycle(plist);
if(meet)
{
int count = 1;
ListNode* cur = meet->next;
while(meet != cur)
{
cur = cur->next;
count++;
}
return count;
}
//若meet为空(没有环),返回0
else
{
return 0;
}
}
3.判断入口点
思路:设头结点到入口点距离L,入口点到相遇点距离X,环长度C
因为快指针是慢指针的两倍,且在相遇点相遇,所以 2(L+X) = L+X+C*n(快指针领先圈数)
解得L=n*C-X
所以从快慢指针相遇点和头结点一起走,他们的相遇点就是入口点
ListNode* GetCycleEntry(ListNode* plist)
{
ListNode* meet = IsCycle(plist);
if(meet)
{
while (meet != plist)
{
meet = meet->next;
plist = plist->next;
}
return plist;
}
else
{
return NULL;
}
}
思路:计算两个链表的长度差的绝对值gab,长的链表先移动gab步,然后两个链表一起走。如果在走到结尾之前相遇了,则该相遇点就是交点
ListNode* IsCrossNoCycle(ListNode* plist1, ListNode* plist2)
{
//plist1和plist2都是无环链表则进入循环,且plist1和plist2不为空
if(IsCycle(plist1) == NULL && IsCycle(plist2) == NULL && plist1 && plist2)
{
ListNode* cur1 = plist1, *cur2 = plist2;
//求plist1和plist2的长度
int len1 = 0;
int len2 = 0;
while (cur1)
{
len1++;
cur1 = cur1->next;
}
while (cur2)
{
len2++;
cur2 = cur2->next;
}
//移动较长的链表的指针,让他和短的链表一样长
int gap = abs(len1 - len2);
if (len1 > len2)
{
while(gap--)
{
plist1 = plist1->next;
}
}
else
{
while(gap--)
{
plist2 = plist2->next;
}
}
//一起走,如果中途相等了则有交点
while (plist1)
{
if(plist1 == plist2)
{
return plist1;
}
plist1 = plist1->next;
plist2 = plist2->next;
}
}
return NULL;
}
1) 两个都不带环;
2) 其中一个带环;
3) 两个都带环
ListNode* IsCross(ListNode* plist1, ListNode* plist2)
{
ListNode* ent1 = GetCycleEntry(plist1);
ListNode* ent2 = GetCycleEntry(plist2);
//1.两个都不带环,转化成无环相交问题
if (ent1 == NULL && ent2 == NULL)
{
return IsCrossNoCycle(plist1, plist2);
}
//2.其中一个带环,必定不相交
else if ((ent1 == NULL && ent2) || (ent2 == NULL && ent1))
{
return NULL;
}
//3.两个都带环
// 1)不相交
// 2)尾交
// 3)环交
else
{
//1)若入口点相同则尾交,去掉环,转化成无环相交问题
if(ent1 == ent2)
{
ent1->next = NULL;
return IsCrossNoCycle(plist1, plist2);
}
else
{
//2)同环,两个入口点
// 解决方案:一个入口点开始遍历一圈,看环上是否有另一个入口点
// 若找到则返回plist1的入口点
ListNode* cur = ent1->next;
while (cur != ent1)
{
if(cur == ent2)
{
return ent1;
}
cur = cur->next;
}
return NULL; //不相交
}
}
}