【LeetCode】141-环形链表

105 阅读3分钟

141. 环形链表

题目描述

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

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

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

示例

circularlinkedlist

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

circularlinkedlist_test2

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

circularlinkedlist_test3

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

题解

题目很简单,给你一个链表,判断链表中是否存在环形,在题目中有提到使用 pos 来表示链表尾连接到链表中的位置,这个变量仅仅只是用来系统判题的时候使用的,和我们解题没关系,所以可以忽略。

方法一

通过观察上面的示例中的链表示意图可以看出,如果存在环形链表的话那么其中的某一个节点一定会有另外两个节点指向它,也就是说如果对链表进行遍历的话,那么在遍历一定的次数之后肯定会回到这个节点,那既然这样的话,我们就可以使用一个 List 用来保存每次遍历的节点,然后在遍历的过程中,每遍历到一个节点就判断一下 List 中是否存在该节点,如果存在该节点,那么就形成了环形链表,如果没有的话则将当前节点放入到 List 中,如果最后遍历完成之后还是没有碰到某个节点出现了两次,那么就表示链表不存在环形链表。

代码如下:

public boolean hasCycle(ListNode head) {
  List<ListNode> list = new ArrayList<>();
  while (head != null) {
    if (list.contains(head)) {
      return true;
    }
    list.add(head);
    head = head.next;
  }
  return false;
}

代码解析

时间复杂度:O(N)

空间复杂度:O(N)

方法二

通过方法一就已经可以解决该问题,但是在空间复杂度上还可以进行优化,我们知道如果一个链表中存在环形链表的话,那么在遍历的过程中就会成死循环,会一直在环形链表中循环下去,这样的话我们还有一个方法就是使用两个指针,一快一慢,快指针每次移动两个节点,慢指针每次移动一个节点,如果链表中存在环形链表的话,那么在不停的循环中肯定会存在一种情况,就是快指针在前面不断的移动中会超过慢指针整整一圈,最后在某个位置追上慢指针。就和在操场跑步一样,跑的快的那个人会超跑的慢的人整整一圈,称之为套圈。

代码如下:

public boolean hasCycle(ListNode head) {
  if (head == null || head.next == null) {
    return false;
  }
  ListNode slow = head;
  ListNode fast = head.next;
  while (slow != fast) {
    if (fast == null || fast.next == null) {
      return false;
    }
    slow = slow.next;
    fast = fast.next.next;
  }
  return true;
}

代码解析

定义快慢两个指针,两个指针不相等时就遍历,如果快指针等于空或者快指针的下一个节点等于空,那么就表示链表没有环形,遍历结束了直接返回 false,如果有环形,那么两个指针就肯定会在某一个节点相遇,也就是两个指针相等。

时间复杂度:O(N)

空间复杂度:O(1),不需要额外的空间。