前言
本文章采用的例题为leetcode上的题目。代码实现采用C++,但是几乎没有涉及太多的C++语法。话不多说,直接上链接!!!
例题1:环型链表
题目链接:141.环型链表
题目:
给你一个链表的头节点
head
,判断链表中是否有环。如果链表中有某个节点,可以通过连续跟踪
next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。如果链表中存在环 ,则返回
true
。 否则,返回false
。
案例:
解法:
这道题需要运用双指针来解决会更简单一点。
具体做法:定义两个指针,同时从链表的头节点出发,一个指针一次走一步,一个指针一次走2步。(原理后面介绍,下面直接上代码)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* slow = head;
ListNode* fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
return true;
}
return false;
}
};
结果;
原理分析:
假设:当slow到入环点,fast肯定在环r内。并且它们之间的位置关系如图所示。
那么,fast到slow的距离为 d = R – x。
而slow走一步,fast走两步那么它们之间的距离差就为1
即slow和fast每走一次,fast和slow的距离为:d = R – x – 1
那就相当于slow和fast在环里总会相遇。
例题2:环型链表Ⅱ
题目链接:142.环型链表Ⅱ
题目:
给定一个链表的头节点
head
,返回链表开始入环的第一个节点。 如果链表无环,则返回null
。如果链表中有某个节点,可以通过连续跟踪
next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果pos
是-1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改链表
案例:
解法:
这道题我知道有两种解法!
第一种解法(了解一下就好):
利用上一个例题的结论,在环里找到相遇点,在相遇点把这个环拆开。把带环问题演变成寻找两个链表的第一个相交节点,这就非常的容易了,但是不符合题意,这样会改变链表的结构。(但是可以了解一下,下图为拆分效果!!!)
第二种解法(推荐):
这里先套用结论,从相遇点到入环点的距离等于head到入环点的距离!!!也就是说,从head和相遇点同时走就会碰到入环点!!!这道题运用的就是这个结论。(下面直接上代码,推导在后面!!!)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow = head;
ListNode *fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(fast == slow)
break;
}
//判断是否有环
if(fast == NULL || fast->next == NULL)
{
return NULL;
}
while(slow != head)
{
head = head->next;
slow = slow->next;
}
return slow;
}
};
结果:
解法二的推导:
head为链表的起始点、入环点为e、相遇点为m。
假设:环的周长为R、head到入环点e的距离为L、入环点e到相遇点m的距离为X、相遇点m到入环点e的距离为d = R – x。
在判断是否有环时:
- fast走的路径长度为:L + X + nR
- slow走的路径长度为:L+X
注意:为什么fast走的路径长度为 L + X + nR?
因为:当slow到入环点e时,fast一定在环内,并且fast要追slow,那么fast一定会再经过入环点e,所以fast走的路径为L + X + nR、且n的取值至少为1。
又因为fast = 2slow
- 2*(L+X) = L+X+nR
- L+X = nR
- L = nR – X
- L = (n - 1)R + d (其中(n-1)R代表从相遇点m绕环转(n-1)圈,最后回到m点)
证明完毕!!(关系如下图所示!!)
链表的问题涉及的范围很广,带环的问题只是一个分支。通过这两道例题可以让我们对这类问题有一定的了解和熟悉。但是,链表的题目灵活多变,这就需要我们平时多练习!!