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

给你一个链表的头节点 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
解释: 链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos 为 -1 或者链表中的一个 有效索引

**进阶:**你能用 O(1)(即,常量)内存解决此问题吗?

题目来源:
作者:LeetCode
链接:https://leetcode.cn/leetbook/read/top-interview-questions-easy/xnwzei/
来源:力扣(LeetCode)

解决方法:

一:快慢指针解决

判断链表是否有环应该是老生常谈的一个话题了,最简单的一种方式就是快慢指针,慢指针针每次走一步,快指针每次走两步,如果相遇就说明有环,如果有一个为空说明没有环。代码比较简单

public boolean hasCycle(ListNode head) {
    if (head == null)
        return false;
    //快慢两个指针
    ListNode slow = head;
    ListNode fast = head;
    while (fast != null && fast.next != null) {
        //慢指针每次走一步
        slow = slow.next;
        //快指针每次走两步
        fast = fast.next.next;
        //如果相遇,说明有环,直接返回true
        if (slow == fast)
            return true;
    }
    //否则就是没环
    return false;
}

转换为PHP代码为:

function hasCycle($head) {
    //方法一:快慢指针解决
    if($head == null)
        return false;   
    //快慢两个指针
    $slow = $head;
    $fast = $head;

    while($fast != null && $fast->next != null){
        //慢指针每次走一步
        $slow = $slow->next;
        //快指针每次走两步
        $fast = $fast->next->next;
        //如果相遇,说明有环,直接返回true
        if($slow === $fast)
            return true;
    } 
    //否则没环
    return false;
}

到这里问题好像并没有结束,为什么快慢指针就一定能判断是否有环。我们可以这样来思考一下,假如有环,那么快慢指针最终都会走到环上,假如环的长度是m,快慢指针最近的间距是n,如下图中所示
在这里插入图片描述
快指针每次走两步,慢指针每次走一步,所以每走一次快慢指针的间距就要缩小一步,在图一中当走n次的时候就会相遇,在图二中当走m-n次的时候就会相遇。

二:存放到集合中
这题还可以把节点存放到集合set中,每次存放的时候判断当前节点是否存在,如果存在,说明有环,直接返回true,比较容易理解

public boolean hasCycle(ListNode head) {
    Set<ListNode> set = new HashSet<>();
    while (head != null) {
        //如果重复出现说明有环
        if (set.contains(head))
            return true;
        //否则就把当前节点加入到集合中
        set.add(head);
        head = head.next;
    }
    return false;
}

转换为PHP代码为:

function hasCycle($head) {
    //方法二:存放到集合中
    $array = [];
    while($head != null){
        //如果没有重复出现说明有环
        if(in_array($head,$array))
            return true;
        //否则就把当前节点加入到数组中
        array_push($array,$head);
        $head = $head->next;
    }
    return false;
}

三:逐个删除

一个链表从头节点开始一个个删除,所谓删除就是让他的next指针指向他自己。如果没有环,从头结点一个个删除,最后肯定会删完,如下图所示
在这里插入图片描述
如果是环形的,那么有两种情况,一种是o型的,一种是6型的。原理都是一样,我们就看一下o型的
在这里插入图片描述
如上图所示,如果删到最后,肯定会出现head=head.next

public boolean hasCycle(ListNode head) {
    //如果head为空,或者他的next指向为空,直接返回false
    if (head == null || head.next == null)
        return false;
    //如果出现head.next = head表示有环
    if (head.next == head)
        return true;
    ListNode nextNode = head.next;
    //当前节点的next指向他自己,相当于把它删除了
    head.next = head;
    //然后递归,查看下一个节点
    return hasCycle(nextNode);
}

转换为PHP代码为:

//逐个删除
public function hasCycle($head){
    //如果head为空,或者他的next指向为空,直接返回false
    if($head == null || $head->next == null)
        return false;
    //如果出现head.next = head表示有环
    if($head->next == $head)
        return true;
    $nextNode = $head->next;
    //当前节点的next指向它自己,相当于把它删除了
    $head->next = $head;
    //然后递归,查看下一个节点
    return hasCycle($nextNode);
}

四:先反转在比较

如果有环,那么链表反转之后,原来的头结点和反转之后的头结点一定是同一个

public ListNode reverseList(ListNode head) {
    //新链表
    ListNode newHead = null;
    while (head != null) {
        //先保存访问的节点的下一个节点,保存起来
        //留着下一步访问的
        ListNode temp = head.next;
        //每次访问的原链表节点都会成为新链表的头结点,
        //其实就是把新链表挂到访问的原链表节点的
        //后面就行了
        head.next = newHead;
        //更新新链表
        newHead = head;
        //重新赋值,继续访问
        head = temp;
    }
    //返回新链表
    return newHead;
}

public boolean hasCycle(ListNode head) {
    ListNode rev = reverseList(head);
    if (head != null && head.next != null && rev == head) {
        return true;
    }
    return false;
}

转换为PHP代码为:

public function reverseList($head){
    //新链表
    $newHead = null;
    while($head != null){
        //先保存访问的节点的下一个节点,保存起来
        //留着下一步访问的
        $temp = $head->next;
        //每次访问的原链表节点都会成为新链表的头结点,
        //其实就是把新链表挂到访问的原链表节点的
        //后面就行了
        $head->next = $newHead;
        //更新新链表
        $newHead = $head;
        //重新赋值,继续访问
        $head = $temp;
    }
    //返回新链表
    return $newHead;
}
    
public function hasCycle($head){
    $rev = $this->reverseList($head);
    if($head != null && $head->next != null && $rev == $head){
        return true;
    }
    return false;
}

方法来源:
作者:数据结构和算法
链接:https://leetcode.cn/leetbook/read/top-interview-questions-easy/xnwzei/?discussion=GfAWpk
来源:力扣(LeetCode)

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值