Leetcode(七)——给定一个链表 ,判断链表中是否有环。 返回环的入口。

判断链表中是否有环

题目描述 

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

分析

如果链表中有环,那我们是不是就可以了解到它的尾指针并不指向空,因此也不能进行遍历会发生死循环。那我们应该怎样去解决这个问题呢?

首先我们可以定义快慢指针(fast,slow)。让快指针一次走两步,慢指针一次走一步。如果有环的话他们两个必定相遇,没环则不会相遇。所以,如果快慢指针相遇就能证明链表有环。

你会不会想问为什么快指针一次走两步,慢指针一次走一步,为什么会在环里相遇呢?

fast会先进环,slow会后进环。假设slow进环时,slow和fast之间的距离为N。slow进环以后,fast开始追击slow,slow每走一步,fast走两步,它们之间的距离缩小一步。它们之间的距离一步一步的缩小。也有可能slow一进环他们两个就相遇了

N

N-1

N-2

N-3

......

2

1

0  即相遇

所以,slow走一步,fast走两步。只要存在环,他们两个必定相遇。

代码演示

 struct ListNode {
   int val;
   struct ListNode *next;
 };
 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 (slow == fast)
         {
             return true;
         }
     }
     //不相遇
     return false;
 }

扩展问题

 快指针一次走三步、四步、五步......N步行吗? 

 fast会先进环,slow会后进环。假设slow进环时,slow和fast之间的距离为N。slow进环以后,fast开始追击slow,slow每走一步,fast走三步,它们之间的距离缩小2

综上,如果快指针一次走三步,五步等等,可能会相遇,也有可能不会相遇。

返回环的入口点

我们根据上题判断链表是否有环的为基础来进行分析。fast走的路程是slow的两倍。

分析

总结:

一个指针从链表头开始走,一个指针从相遇点开始走,他们会在入口点相遇。 

刚好我们要返回入口点。那我们就可以让头指针(head)从链表头开始走,让慢指针(slow)从相遇点开始走,他们两个相遇,便是入口点。

代码演示

struct ListNode {
     int val;
     struct ListNode* next;
 };
 struct ListNode* detectCycle(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)
         {
             struct ListNode* meet = slow;//相遇点
             //判断入口点
             while (head != meet)
             {
                 head = head->next;//head从来链表头一次走一步
                 meet = meet->next;//相遇点,让meet一次走一步
             }
             return meet;//返回入口点
         }
     }
     return NULL;//不相遇,返回NULL
 }
 

                                                                                                                          

                                                                             

  • 18
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值