目录
1、什么是环:
环:是链表里面出现指向自己或者指向这个结点前面的元素,从而使链表形成一个闭环的状态。
2、如何判断链表是否含有环
方法1(最优解):快慢指针:
思路:设置两个指针p、q,开始都在头结点,但是p走一步,q走两步。这样子,如果是没有环的链表,p,q是不可能相遇的
但是如果链表里面含有环,则他们一定会相遇
代码实现:
bool hasCycle(struct ListNode* head ) //bool函数,返回true或者false
{
if(head == NULL )//代码为空,直接返回
{
return false;
}
struct ListNode*p = head;
struct ListNode*q = head;
while(p != NULL && p->next !=NULL)//注意遍历的条件,避免段错误
{
p = p->next->next;
q = q->next;
if(q == p)//如果相遇则有环
{
return true;
}
}
return false;//没有相遇
}
方法2:取极限思维(一般情况可取)
思路:用一个指针去遍历链表,同时设置一个计数器,每遍历一个元素则加一次,如果当计数器到达一个非常大的数字,则一般可以判断这个链表含有环。
代码实现:
bool hasCycle(struct ListNode* head ) {
if(head == NULL )
{
return false;
}
struct ListNode*p = head;
int cut = 0 ;
while(p)
{
cut++;
p = p->next;
if(cut == 100000000)
{
return true;
}
}
return false;
}
方法3:哈希表(不是很推荐)
思路:每次记录遍历的结点的数,进行比较有没有回到这个结点,但是如果有相同的元素,则不好判断。
3、求环的入口
有了上面的知识,现在我们可以考虑一个新的问题,将环的入口求出来如上图,环的入口为3,那么我们怎么通过代码求出来呢?
思路:同样,我们需要判断该链表是否为带环链表,使用快慢指针的方法。当两个指针相遇,即证明有环,此时:将p返回头节点,q留在相遇的结点,p,q再分别一步一步的进行遍历,当p,q再次相遇,则该节点必定为环的入口。
代码实现:
struct ListNode* EntryNodeOfLoop(struct ListNode* pHead )
{
struct ListNode*p = pHead;
struct ListNode*q = pHead;
if(pHead == NULL)
{
return NULL;
}
while(p != NULL&&p->next!=NULL)
{
p = p->next->next;
q = q->next;
if(q == p)//有环
{
p = pHead;
while(q != p )//以相遇为条件进行循环遍历
{
p = p->next;
q = q->next;
}
return p;
}
}
return NULL;
}