数据结构 【带环链表2】
说到带环链表,有一道题目是这样说的,如果一个链表存在环,那么就返回进入环的第一个节点,如果链表没有环,那么就返回空。这里给出两种解题思路:
第一种解法:小结论解法
分析:这道题目可以拆分成两个部分,第一:检查链表是否带环。第二:返回带环链表的第一个进环节点。对于第一个部分,可以使用快慢指针的方式进行检测,这里再理一下思路:
· 关于第二点,这里先给出一个结论:假设存在两个指针meet和start,meet从fast和slow相遇的位置出发,start从链表的起点出发,那么meet和start一定会在链表刚进环的节点相遇。
这里给出证明过程:假设有一个带环链表如下图所示:
证明结果续:
所以算法的设计思路为:当meet和start从相应的条件开始移动时,它们相遇的节点就是链表环的入口节点。代码实现如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode* fast,*slow;
fast = slow = head;
while(fast && fast->next)//检测是否带环
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)//相遇说明有环
{
struct ListNode* meet = fast;
struct ListNode* start = head;
while(meet != start)//循环停止即为入口节点
{
meet = meet->next;
start =start->next;
}
return meet;
}
}
return NULL;
}
第二种解法:转换法(将环断开,转换成求链表的第一个交点问题)
随即问题得到转化,可以先计算headA和headB的长度,计算出长度之前的差值,让长的链表先走差值步,然后两个链表同时往后走,在第一次节点地址相同的位置时,就是两个链表的第一个交点。代码实现如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode* fast,*slow;
fast = slow = head;
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(slow == fast)
{
struct ListNode* headB = fast->next;
fast->next = NULL;
struct ListNode* tailA = head,*tailB = headB;
int lenA = 1,lenB = 1;
while(tailA->next)
{
tailA = tailA->next;
lenA++;
}
while(tailB->next)
{
tailB = tailB->next;
lenB++;
}
int gap = abs(lenA-lenB);
struct ListNode* longNode = head,*shortNode = headB;
if(lenA < lenB)
{
longNode = headB;
shortNode = head;
}
while(gap--)
{
longNode = longNode->next;
}
while(longNode != shortNode)
{
longNode = longNode->next;
shortNode = shortNode->next;
}
return longNode;
}
}
return NULL;
}