LeetCode每日一题之环形链表

前言

大家好,今天是LeetCode每日一题的第11天,给大家分享的是题目是环形链表,难度系数两颗星!废话不多说,先上题目!

1.1 题目要求

题目类型:环形链表

题目内容:给定一个链表,判断链表中是否有环;如果链表中有某个节点,可以通过连续跟踪next指针再次到达该节点,则链表中存在环

注意事项:如果链表中存在环,则返回true,否则,返回false。

1.2 解题方法

1.2.1 使用Set集合处理
1.解题思路

假设存在这样一个环形链表 1 -> 2 -> 3 -> 4 -> 1

  • 1的next指针指向2,2的next指针指向3,3的next指针指向4,4的next指针再指向1,这种情况就说明该链表中有环存在,所以返回值为true

  • 由于节点1通过跟踪next指针再次到达该节点,因此我们可以将节点1进行标记,然后结束循环

那么能使用for循环来遍历节点吗

不能,如果使用for循环,假设当前链表为环形链表,可能会一直遍历下去,形成一个死循环

  • 因此,应该使用while循环,设置一个停止条件,当再次到达该节点时,停止循环即可
2.代码实现
2-1 非环形链表测试
  • 测试代码
package com.kuang.leetcode10;

import java.util.HashSet;
import java.util.Set;

/**
 * @ClassName ListCycle
 * @Description 环形链表
 * @Author 狂奔の蜗牛rz
 * @Date 2021/8/31
 */
public class ListCycle {
    
    //主方法测试
    public static void main(String[] args) {
        //初始化链表结点的当前值和next指针指向值
        ListNode node5 = new ListNode(5, null);
        ListNode node4 = new ListNode(4, node5);
        ListNode node3 = new ListNode(3, node4);
        ListNode node2 = new ListNode(2, node3);
        ListNode node1 = new ListNode(1, node2);
        //判断hasCycle方法的返回值为true还是false
        if(hasCycle1(node1)) {
            //若返回值为true, 则打印如下信息
            System.out.println("当前链表为环形链表!");
        } else {
            //若返回值为false, 则打印如下信息
            System.out.println("当前链表不为环形链表!");
        }
    }

    /**
     * 使用Set集合处理环形链表
     * 判断链表中是否存在环
     * @param head 链表中的结点
     * @return true或false 若有环则返回true, 无环则返回false
     */
    public static boolean hasCycle1(ListNode head) {
        //创建一个HashSet集合(Set集合无序并且不允许重复) 空间复杂度为O(n)
        Set<ListNode> set = new HashSet<ListNode>();
        //使用while循环, 执行条件为head节点的值不为空
        while(head != null) {
            //判断set集合中是否能够成功添加head节点
            if(!set.add(head)) {
                //若该set集合中不能添加该元素, 说明该链表存在环, 则返回值为true
                return true;
            }
            //将当前head节点的next值赋给下一轮的head节点
            head = head.next;
        }
        //若set集合中不能添加, 说明不存在环,返回值为false
        return false;
    }

    //静态内部类ListNode
    static class ListNode{
        //当前节点值
        int val;
        //指向下一节点的next指针
        ListNode next;

        /**
         * ListNode的有参构造方法
          * @param val 当前节点值
         * @param next 指向下一节点的next指针
         */
        public ListNode(int val, ListNode next) {
           //初始化val和next值
           this.val = val;
           this.next = next;
        }
    }
}

  • 测试结果

在这里插入图片描述

结果测试结果与预期相同!

2-2 环形链表测试
  • 测试代码
package com.kuang.leetcode10;

import java.util.HashSet;
import java.util.Set;

/**
 * @ClassName ListCycle
 * @Description 环形链表
 * @Author 狂奔の蜗牛rz
 * @Date 2021/8/31
 */
public class ListCycle {

    public static void main(String[] args) {
        //初始化链表结点的当前值和next指针指向值
        ListNode node5 = new ListNode(5, null);
        ListNode node4 = new ListNode(4, node5);
        ListNode node3 = new ListNode(3, node4);
        ListNode node2 = new ListNode(2, node3);
        ListNode node1 = new ListNode(1, node2);
        //让节点5的next指针指向节点1, 形成了一个环形链表
        node5.next = node1;
        //判断hasCycle方法的返回值为true还是false
        if(hasCycle1(node1)) {
            //若返回值为true, 则打印如下信息
            System.out.println("当前链表为环形链表!");
        } else {
            //若返回值为false, 则打印如下信息
            System.out.println("当前链表不为环形链表!");
        }
    }

    /**
     * 使用Set集合处理环形链表
     * 判断链表中是否存在环
     * @param head 链表中的结点
     * @return true或false 若有环则返回true, 无环则返回false
     */
    public static boolean hasCycle1(ListNode head) {
        //创建一个HashSet集合(Set集合无序并且不允许重复) 空间复杂度为O(n)
        Set<ListNode> set = new HashSet<ListNode>();
        //使用while循环, 执行条件为head节点的值不为空
        while(head != null) {
            //判断set集合中是否能够成功添加head节点
            if(!set.add(head)) {
                //若该set集合中不能添加该元素, 说明该链表存在环, 则返回值为true
                return true;
            }
            //将当前head节点的next值赋给下一轮的head节点
            head = head.next;
        }
        //若set集合中不能添加, 说明不存在环,返回值为false
        return false;
    }

    //静态内部类ListNode
    static class ListNode{
        //当前节点值
        int val;
        //指向下一节点的next指针
        ListNode next;

        /**
         * ListNode的有参构造方法
          * @param val 当前节点值
         * @param next 指向下一节点的next指针
         */
        public ListNode(int val, ListNode next) {
           //初始化val和next值
           this.val = val;
           this.next = next;
        }
    }
}

  • 测试结果

在这里插入图片描述

结果测试结果与预期相同!

3.时间和空间复杂度分析

时间复杂度分析

最多将Set集合中所有的节点遍历一遍,所以其时间复杂度O(n)

空间复杂度分析

创建了新的Set集合来存储节点,所以其空间复杂度也为O(n)

1.2.2 使用双指针法
1.解题思路

使用双指针法, 即分别设置一个快指针和慢指针,

初始状态:即慢指针在前, 快指针在后

 1 -> 2 -> 3 -> 4 -> 5
 |----|-------------->
 slow-> quick->
  • 假设该链表的节点3位置存在一个环, 即节点5的next指针指向3, 某一时刻slow指针和quick指针会都进入到环中, 直至两个指针重合
1 -> 2 -> 3 -> 4 -> 5
|----|----|---->--->|
quick low
|<--------|
  • 假设该链表不存在环,那么quick指针会一直移动到最后, 直至其值为null
2.代码实现
2-1 非环形链表
  • 测试代码
package com.kuang.leetcode10;

import java.util.HashSet;
import java.util.Set;

/**
 * @ClassName ListCycle
 * @Description 环形链表
 * @Author 狂奔の蜗牛rz
 * @Date 2021/8/31
 */
public class ListCycle {

    public static void main(String[] args) {
        //初始化链表结点的当前值和next指针指向值
        ListNode node5 = new ListNode(5, null);
        ListNode node4 = new ListNode(4, node5);
        ListNode node3 = new ListNode(3, node4);
        ListNode node2 = new ListNode(2, node3);
        ListNode node1 = new ListNode(1, node2);
        //判断hasCycle方法的返回值为true还是false
        if(hasCycle2(node1)) {
            //若返回值为true, 则打印如下信息
            System.out.println("当前链表为环形链表!");
        } else {
            //若返回值为false, 则打印如下信息
            System.out.println("当前链表不为环形链表!");
        }
    }

    /**
     * 使用双指针法处理环形链表
     * 判断链表中是否存在环
     * @param head 链表中的结点
     * @return true或false 若有环则返回true, 无环则返回false
     */
    public static boolean hasCycle2(ListNode head) {
        //判断head(当前节点)值是否为空
        if (head == null || head.next == null) {
            //若head值为空, 说明该链表不存在环, 则返回值为false
            return false;
        }
        //设置慢指针slow, 其初值为head头结点
        ListNode slow = head;
        //设置快指针quick, 其初值为head(头结点)的next节点值
        ListNode quick = head.next;
        //使用while循环, 执行条件为slow慢指针值不等于quick快指针
        while(slow != quick) {
            //判断quick快指针值是否为空或者quick指针的next值是否为空
            if(quick == null || quick.next == null) {
                //若值为空, 说明该链表不存在环, 则返回值为false
                return false;
            }
            //将slow指针的next值赋给下一轮循环的slow指针
            slow = slow.next;
            //将quick指针的next值指向的next值赋给下一轮循环的quick指针
            quick = quick.next.next;
        }
        //若慢指针值等于快指针值, 说明该链表存在环,返回值为true
        return true;
    }

    //静态内部类ListNode
    static class ListNode{
        //当前节点值
        int val;
        //指向下一节点的next指针
        ListNode next;

        /**
         * ListNode的有参构造方法
          * @param val 当前节点值
         * @param next 指向下一节点的next指针
         */
        public ListNode(int val, ListNode next) {
           //初始化val和next值
           this.val = val;
           this.next = next;
        }
    }
}

  • 测试结果

在这里插入图片描述

结果测试结果与预期相同!

2-2 环形链表测试
  • 测试代码
package com.kuang.leetcode10;

import java.util.HashSet;
import java.util.Set;

/**
 * @ClassName ListCycle
 * @Description 环形链表
 * @Author 狂奔の蜗牛rz
 * @Date 2021/8/31
 */
public class ListCycle {

    public static void main(String[] args) {
        //初始化链表结点的当前值和next指针指向值
        ListNode node5 = new ListNode(5, null);
        ListNode node4 = new ListNode(4, node5);
        ListNode node3 = new ListNode(3, node4);
        ListNode node2 = new ListNode(2, node3);
        ListNode node1 = new ListNode(1, node2);
        //让节点5的next指针指向节点1, 形成了一个环形链表
        node5.next = node1;
        //判断hasCycle方法的返回值为true还是false
        if(hasCycle2(node1)) {
            //若返回值为true, 则打印如下信息
            System.out.println("当前链表为环形链表!");
        } else {
            //若返回值为false, 则打印如下信息
            System.out.println("当前链表不为环形链表!");
        }
    }

    /**
     * 使用双指针法处理环形链表
     * 判断链表中是否存在环
     * @param head 链表中的结点
     * @return true或false 若有环则返回true, 无环则返回false
     */
    public static boolean hasCycle2(ListNode head) {
        //判断head(当前节点)值是否为空
        if (head == null || head.next == null) {
            //若head值为空, 说明该链表不存在环, 则返回值为false
            return false;
        }
        //设置慢指针slow, 其初值为head头结点
        ListNode slow = head;
        //设置快指针quick, 其初值为head(头结点)的next节点值
        ListNode quick = head.next;
        //使用while循环, 执行条件为slow慢指针值不等于quick快指针
        while(slow != quick) {
            //判断quick快指针值是否为空或者quick指针的next值是否为空
            if(quick == null || quick.next == null) {
                //若值为空, 说明该链表不存在环, 则返回值为false
                return false;
            }
            //将slow指针的next值赋给下一轮循环的slow指针
            slow = slow.next;
            //将quick指针的next值指向的next值赋给下一轮循环的quick指针
            quick = quick.next.next;
        }
        //若慢指针值等于快指针值, 说明该链表存在环,返回值为true
        return true;
    }

    //静态内部类ListNode
    static class ListNode{
        //当前节点值
        int val;
        //指向下一节点的next指针
        ListNode next;

        /**
         * ListNode的有参构造方法
          * @param val 当前节点值
         * @param next 指向下一节点的next指针
         */
        public ListNode(int val, ListNode next) {
           //初始化val和next值
           this.val = val;
           this.next = next;
        }
    }
}

  • 测试结果

在这里插入图片描述

结果测试结果与预期相同!

3.时间和空间复杂度分析

时间复杂度分析

使用双指针法,最多将链表遍历一遍,所以其时间复杂度O(n)

空间复杂度分析

并没有创建新的数组空间来存储节点,所以其空间复杂度O(1)


好了,今天LeetCode每日一题—环形链表到这里就结束了,欢迎大家学习和讨论,点赞和收藏!


参考视频链接:https://www.bilibili.com/video/BV1Ey4y1x7J3 (国内算法宝典-LeetCode算法50讲)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

狂奔の蜗牛rz

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

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

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

打赏作者

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

抵扣说明:

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

余额充值