目录
前言
小伙伴们大家好啊!今天为大家带来一篇有关链表带环问题的文章,众所周知,链表带环问题在单链表的应用中相对来说,是比较复杂的一类问题,所以今天小编就带大家一起看看吧!
一,问题分析
这次我们通过两个环形链表的题目,来了解一下,带环链表的特点。
1.1环形链表 I
如上图所示,题目中说明,我们需要判断一个链表是否带环,然后带环的链表的要求是:链表中一定有一个节点,可以通过连续跟踪 next 指针再次到达。
也就是说,如果带环的话,那么某个节点的指针一直通过 next 往后移动,那么一定会再次到达那个节点。
1.2 环形链表II
那么类似的第二道题是如上描述的,和第一道题类似,同样都需要判断是否带环,但是这个题附加的条件是如果带环,则需要返回开始入环的。
所以,我们看到,其实对于带环问题,简单的方面主要就是判断是否带环,如果带环的话,则返回入环的第一个节点。
二,是否带环分析
那么根据上面两个题目的要求,我们知道,对于链表带环,首先就是需要判断是否带环也就是第一个题目。
2.1推荐使用“二指针”
为什么不使用“一指针”
我们知道,因为单链表涉及到的很多问题一个指针是不能解决的,所以这里同样的,我们也用两个指针,但是这里用两个指针的主要原因是:
一个指针不能实现。因为我们不知道链表有几个节点,假设该链表带环,那么我们就需要有一个东西记住了该节点第一次出现的位置,然后第二次出现的时候,我们将这两个节点的位置比较了一下,发现是一样的,这就说明了,确实是带环的。
但是如果只用一个指针的话,那么我们就需要每次就记录一下访问过的节点的地址,然后有一个指针从头节点一直移动,我们需要判断的就是该指针如果第二次出现在同样的地址的话,说明是带环的,但是这是不现实的,因为我们不知道有多少个节点(就算是计算也是比较繁琐的),即使我们知道了,难道我们需要将这些节点的每个地址都记录下来吗?
显然这是不现实的,所以这里我们需要用两个指针。
2.2思路分析
那么既然用两个指针实现的话,思路就相对来说比较简单了。
如上图所示,两个指针 fast 和slow一开始都是指向头节点的。然后这里我们一般的做法是快指针 fast 一次移动两个位置,然后 slow 一次移动一个位置,这样去移动是有一定的规律的(这个在后面的文章中我们会有证明)。那么不这样移动可以实现判断带环问题吗?可以的,但不是一定能判断,而上面那种做法是一定可以实现的,所以一般我们用上面那种做法。
那么我们确定了快指针 fast 一次移动两个位置,而慢指针 slow 一次只移动一个位置。如下图所示:那么对于当前链表,移动三次之后,两指针处于同一个位置,此时就如开头说得那样,如果两个指针的地址再次一样的话,说明是带环的,否则不可能两指针地址再次相同。
好的,那么废话不多说,直接上代码。
2.3代码实现
bool hasCycle(struct ListNode *head) {
struct ListNode*fast=head;
struct ListNode*slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
return true;
}
}
return false;
}
其实相对来说,代码实现是比较简单的,主要的是我们需要理解,如果链表带环的话,什么条件下,说明链表带环了,怎么样去实现?那么如果小伙伴们看完上面的解析之后,还觉得不是很理解,可以关注后续文章:为什么fast一次走两步,slow一次走一步?
三,带环 - 入环节点
那么经过上面我们的分析之后,相信大家对于是否带环也有了自己的想法。那么我们接下来解决一下带环的链表,其入环的节点的问题。也就是上面环形链表的第二题,求出第一次入环的第一个节点。
2.1思路分析
因为这个题是在第一题的基础上实现的,所以我们不做过多解释,直接进入主题。
这里我们通过实践发现了一个规律:如果链表带环的话,我们首先用双指针让其相遇,在第一次相遇的地方设置一个指针 meet ,然后将链表的头指针 head 和 meet 指针一起移动,那么这两个指针一定会在入环的地方相遇。此时返回 meet 指针即可。
tips:对于该规律,以及上面的为什么fast一次走两步,而slow一走一步。后续小编会为大家带来一篇详解。
在这两个题目中,大家只需要能够理解这样做可以做出来题目就可以了。
2.2代码实现
那么因为思路我们在第一题中讲述的很详细,这里我们不做过多解释。
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=slow;
while(head!=meet)
{
head=head->next;
meet=meet->next;
}
return meet;
}
}
return NULL;
}
好的,那么本文到此就结束啦,如果大家还有问题的话,还请指正鸭!