LeetCode题解:Java中判断单向链表有环、如果有环,求环的长度和入环节点

有一个单向链表,链表当中有可能出现“环”。
力扣题:141 .Linked List Cycle 环形链表

判断出这个链表是否有环?

方法一:

传统的方法:

  1. 遍历单链表的每一个节点,每遍历一个新的节点,就从头检查新节点之前的所有节点,用新节点和此节点之前所有节点依次作比较。
  2. 如果发现新节点之前的所有节点当中存在相同节点ID,则说明该节点被遍历过两次,链表有环;
  3. 如果之前的所有节点当中不存在相同的节点,就继续遍历下一个新节点,继续重复刚才的操作。
  • 时间复杂度:O(n²) 空间复杂度:O(1)

方法二:

  1. 首先创建一个以节点ID为键的HashSet集合,用来存储曾经遍历过的节点。
  2. 从头节点开始,依次遍历单链表的每一个节点。每遍历到一个新节点,就用新节点和HashSet集合当中存储的节点作比较
  3. 如果发现HashSet当中存在相同节点ID,则说明链表有环
  4. 如果HashSet当中不存在相同的节点ID,就把这个新节点ID存入HashSet
  5. 之后进入下一节点,继续重复刚才的操作。
  • 时间复杂度:O(n) 空间复杂度:O(n)
public class Solution {
    
    
    public ListNode detectCycle(ListNode head) {
    
    
        HashSet<ListNode> hashSet = new HashSet<>();
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;
        ListNode cur = dummyHead;
        while(null != cur.next){
    
    
            if(hashSet.contains(cur.next)){
    
    
                return cur.next;
            }
            cur = cur.next;
            hashSet.add(cur);
        }
        return null;
    }
}

方法三:

  1. 首先创建两个指针1和2(在java里就是两个对象引用),同时指向这个链表的头节点。
  2. 然后开始一个大循环,在循环体中,让指针1每次向下移动一个节点,让指针2每次向下移动两个节点,然后比较两个指针指向的节点是否相同。
  3. 如果相同,则判断出链表有环,如果不同,则继续下一次循环。
  • 时间复杂度:O(n) 空间复杂度:O(1)
public class Solution {
    
    
    public ListNode detectCycle(ListNode head) {
    
    
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;
        ListNode cur1 = dummyHead;
        ListNode cur2 = dummyHead;
        while(true){
    
    
            if(null == cur2.next || null == cur2.next.next){
    
    
                return null;
            }
            cur1 = cur1.next;
            cur2 = cur2.next.next;
            if(cur1 == cur2){
    
    
                break;
            }
        }
        cur1 = dummyHead;
        while(cur1 != cur2){
    
    
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1;
    }
}

如果链表有环,如何求出环的长度?

  • 当两个指针首次相遇,则证明有环。让两个指针从相遇点继续前进,并统计前进的循环次数,直到两个指针再次相遇,则前进次数就是环长。

  • 原因:指针fast每次走两步,指针slow每次走一步,当两者相遇时,fast比slow多走了一圈。

  • 若带环,则fast与slow两次相遇之间,slow走过的节点数即为环的长度

如果链表有环,如何求出入环节点?

力扣题:142. 环形链表 II
相遇之后,让fast重回链表头,fast与slow同时一次走一步,当fast==slow时,则它们到了入口点

  • 只要把一个指针放回到头节点位置,另一个指针保持在首次相遇位置,连个指针都是每次走一步,最终相遇点就是入环点
    在这里插入图片描述
package some_problem;/**
 * Copyright (C), 2019-2020
 * author  candy_chen
 * date   2020/7/16 19:37
 * version 1.0
 * Description: 判断单向链表是否有环
 */

/**
 *
 */
public class isCycle {
    
    
    /**
     * 判断是否有环
     * @param head  链表头节点
     * @return
     */

    public static boolean isCycle(Node head){
    
    
        Node slow = head;
        Node fast = head;
        while (fast!=null && fast.next!=null){
    
    
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast){
    
      //一旦两环相遇 则说明链表有环
                return true;
            }
        }
        return false;
    }
    /**
     * 如果链表有环,如何求出环的长度
     */

    public static int getCycleLength(Node head){
    
    
        if (head ==null){
    
    
            return 0;
        }
        Node fast = head;
        Node slow = head;
        while (fast != null && fast.next != null){
    
    
            fast = fast.next.next;
            slow = slow.next;
            while (fast == slow){
    
    
                int len = 1;
                fast = fast.next.next;
                slow = slow.next;
                while (fast!= slow){
    
    
                    len ++;
                    fast = fast.next.next;
                    slow = slow.next;
                }
                return len;

            }
        }
        return 0;

    }

    /**
     *如果链表有环,求出入环节点
     */

    public static int getCycle(Node head){
    
    
        if (head ==null){
    
    
            return 0;
        }
        Node fast = head;
        Node slow = head;
        while (fast != null && fast.next != null){
    
    
            fast = fast.next.next;
            slow = slow.next;
            while (fast == slow){
    
      //第一轮相遇
                fast = head;	//构建第二轮相遇  fast在第一轮相遇后返回起点开始走 slow不动从相遇点走
                while (fast != slow){
    
    
                    fast = fast.next;
                    slow = slow.next;
                }
                return slow.data;

            }
        }
        return 0;

    }

    /**
     * 链表节点
     */
    private static class Node{
    
    
        int data;
        Node next;
        Node(int data){
    
    
            this.data = data;
        }
    }

    public static void main(String[] args) {
    
    
        Node node1 = new Node(5);
        Node node2 = new Node(3);
        Node node3 = new Node(7);
        Node node4 = new Node(2);
        Node node5 = new Node(6);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        node5.next = node3;

        System.out.println(isCycle(node1));
        System.out.println(getCycleLength(node1));
        System.out.println(getCycle(node1));
    }
}

说明:作者根据网络资料进行搜索学习,理解整理 若有侵权联系作者

猜你喜欢

转载自blog.csdn.net/qq_35655602/article/details/107397393