给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。 否则,返回 false
。
示例 1:
代码语言:javascript复制输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
代码语言:javascript复制输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
代码语言:javascript复制输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
提示:
- 链表中节点的数目范围是
[0, 104]
-105 <= Node.val <= 105
pos
为-1
或者链表中的一个 有效索引 。
1,什么是指针? 对于学习C 的伙伴们对指针估计已经很熟悉了,但是在其他语言中却没有直接的指针可以使用,那么其他语言就不能和指针一起玩了嘛?其实不是的,指针是一个宽泛的概念,其实在你用循环遍历容器的时候,那个在循环中不断自增的 i 其实就可以被称为指针了;其实指针就是指一个指向容器中某一个值的东西而已,就好像下面这个代码:
C Python3 int i = 1; vector<int> nums = {1,2,3}; cout << nums[i] << endl; 其中我们将 i 作为下标访问了列表的第2个元素(数组下标从0开始),这时候我们也可以把 i 称为指向列表第2个元素的一个指针。
2,什么是快慢指针? 我们在遍历容器的时候,有一般都会习惯性的定义一个指针 i ,每经历一轮循环 i 都会向后移一位指向下一个元素(通过自增实现);而快慢指针就是定义两个指针 fast 和 slow ,一开始都处于容器的头部,他们唯一的区别就是每经历一轮循环,快指针向后移动的位数比慢指针多(比如快指针走两步,慢指针走一步)。
3,快慢指针有啥用? 快慢指针可以干的事情很多,可以用于返回不支持随机访问容器(不可以直接通过下标访问元素的容器)的中间元素,比如返回链表的中间节点(876. 链表的中间结点)(题解:快慢指针解决链表的中间节点),亦或是像本题一样解决环问题等等。
4,本题思路 这道题是检测链表中是否有环的存在,如果有则返回true,没有则返回false;有的朋友就直接使用集合去计数了,如果某个节点在访问时已经被记录访问过就返回true,否则当指针走到链表尾部时返回false。但这种写法无疑对内存的占用不会小,那有没有其他的办法呢?这里就可以用到快慢指针了。像下图的链表:
上面的链表是一个简单的环形链表,我们可以试着用两根手指来代替两个指针,开始两个指针都在头部,开始循环后快指针走两步,慢指针走一步;稍加模拟之后就会发现,快指针虽然比慢指针快,但因为环的存在,快指针比慢指针先进入环,然后快指针会到慢指针的后面,最终在-4节点相遇;当没有环的情况下,快指针永远比慢指针快,所以他们出发之后便不可能再相遇,所以,这题的思路就是,快慢指针如果在链表的遍历过程中相遇,则证明链表中存在环。
5,代码实现
代码语言:javascript复制class Solution {
public:
bool hasCycle(ListNode *head) {
//快慢指针定义
ListNode* fast = head;
ListNode* slow = head;
//fast指针不为空指针且fast指针下一个节点也不为空指针
while(fast != NULL && fast -> next != NULL){
//快指针两步,慢指针一步
fast = fast -> next -> next;
slow = slow -> next;
//相遇时返回true
if(fast == slow) return true;
}
//遍历结束还未相遇,返回false
return false;
}
};