目录
一、链表结构
struct ListNode
{
int val;
ListNode* next;
};
二、判断一个链表是否带环
1.方法一:快慢指针
定义一个快指针和慢指针都指向链表的head,快指针一次走两步,慢指针一次走一步,这样两个指针之间的举例会每次缩小一,如果该链表带环的话,那么快慢指针必然会相遇,并且相遇是在环中。
bool hasCycle(ListNode *head)
{
if(head == nullptr)//空链表直接返回false
return false;
ListNode* fast = head;//快指针
ListNode* slow = head;//慢指针
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
return true;
}
return false;
}
2.方法二:使用STL库中的map表进行映射
我们可以用map建立<ListNode*,int>的映射关系,int为节点出现的次数,将每个节点都放入map中,放入时并将int设置为1,如果链表带环,必然会在遇到之前插入过map的节点,那么如果检测到value为1,那就说明带环。
bool hasCycle(ListNode *head)
{
if(head == nullptr)
return false;
map<ListNode*,int> m;//定义映射map
ListNode* cur = head;
while(cur)
{
if(m[cur] == 0)
m[cur] = 1;
else
return true;
cur = cur->next;
}
return false;
}
三、求环的长度
要求环的长度我们先找到相遇的点,相遇的点必然在环内部,那么定义一个计数器,每走一步计数器++,当再次回到这个节点的时候,计数器就记录了这个环的长度。
ListNode *getmeet(ListNode *head)//找到相遇节点
{
if(head == nullptr)
return NULL;
ListNode* fast = head;
ListNode* slow = head;
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
return slow;
}
return NULL;
}
int cycleLenth(ListNode *head)
{
ListNode* meet = getmeet(head);
if(meet == nullptr)
return 0;
int count = 1;
ListNode* cur = meet->next;
while(cur!=meet)
{
count++;
cur = cur->next;
}
return count;
}
四、找环的入口节点
当快慢指针相遇时说明有环,并且这个相遇点一定在环内部,此时记录这个相遇节点,然后让一个指针从这个带环链表的头开始走,那么这两个指针相遇的点就是环的入口点。
具体为什么呢?这里有个公式推导的过程,我们通过这张图了解一下:
通过公式的推导我们发现L=kc-n(这里的k是倍数,有可能快指针在环里转了k圈),也就是说相遇节点到入环点的距离等于链表的头到入环点的距离。
所以我们写代码的时候只需要找到相遇节点,再让一个指针从头开始走即可。
ListNode *getmeet(ListNode *head)//找到相遇节点
{
if(head == nullptr)
return NULL;
ListNode* fast = head;
ListNode* slow = head;
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
return slow;
}
return NULL;
}
ListNode *detectCycle(ListNode *head)//查找入环点
{
ListNode* meet = getmeet(head);
if(meet == nullptr)
return NULL;
ListNode* cur = head;
while(meet && cur)
{
if(cur == meet)
return meet;
meet = meet->next;
cur = cur->next;
}
return NULL;
}