算法面试题:如何判断链表有环

题目

有一个单向链表,链中可能有“环”,如何用程序判断呢?

方法1(最低效)
最常想到的是:首先从头结点开始遍历整个链表,每遍历一个,就和之前遍历过的比较,,,
这相当于每次遍历了两遍,无疑是扩大了链表“ 遍历 ”带来的劣势,其时间复杂度为O(n^2),由于没有额外的存储空间,故空间复杂度为O(1)。

这也太低效了吧。

方法2(次之)
首先创建一个以节点ID为key的hashSet集合,用来存储曾经遍历过的节点。
这种方式相当于“ 用空间换时间 ”,其使用额外的存储空间来代替重复遍历链表带来的时间上的“低效”。
时间复杂度O(n),但空间复杂度也是O(n)。

有没有办法让时间复杂度不变同时空间复杂度降低呢?

双指针法
正如我前文所说,双指针法在诸如链表等的算法中简直是游刃有余。

首先创建两个指针p1和p2,让他们同时指向这个链表的头结点。然后开始一个大循环,在循环体中,让p1每次向后移动一个节点,而p2每次移动两个节点,然后比较两个指针所指向节点是否相等,如果相等,则链表有环!

bool isExistLoop(ListNode* pHead)  {  
    ListNode* fast;//慢指针,每次前进一个节点
    ListNode* slow;//快指针,每次前进2个节点 
    slow = fast = pHead ;  //两个指针均指向链表头节点
    //当没有到达链表结尾,则继续前进
    //快指针一次两个节点,慢指针一次一个节点,要求不能在尾节点处相遇,故而有下面while语句中的条件
    while (slow != NULL && fast -> next != NULL)  {  
        slow = slow -> next ; //慢指针前进一个节点
        fast = fast -> next -> next ; //快指针前进两个节点
        if (slow == fast)  //若两个指针相遇,且均不为NULL则存在环
            return true ;  
    }  
    //到达末尾仍然没有相遇,则不存在环
    return false ;  
}  

思路解读:
(1)定义两个指针分别为 slow,fast,并且将指针均指向链表头节点。
(2)规定,slow 指针每次前进 1 个节点,fast 指针每次前进两个节点。
(3)当 slow 与 fast 相等,且二者均不为空,则链表存在环。
若链表中存在环,则快慢指针必然能在环中相遇。这就好比在环形跑道中进行龟兔赛跑。由于兔子速度大于乌龟速度,则必然会出现兔子与乌龟再次相遇情况。因此,当出现快慢指针相等,且二者不为NULL时(即二者不在尾节点处相遇),则表明链表存在环。

相似扩展

如何求出入环节点?

上面我们判断了是否有环,那么,在有环处一定两指针是相遇的。
因为快慢指针的性质,我们知道,快指针比慢指针快一步:这样,当两者再次相遇时,p2比p1整整多走了一圈。
也就是说,从链表头到入环节点的位置,恰等于从首次相遇点回到入环点的距离!
在这里插入图片描述

我们把p1放在相遇位置,把p2放在头结点位置,这次,让他们以相同步率移动,则再次相遇的节点,就是要求的“入环节点”!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恪愚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值