LeetCode题解:Java中判断单向链表有环、如果有环,求环的长度和入环节点
有一个单向链表,链表当中有可能出现“环”。
力扣题:141 .Linked List Cycle 环形链表
判断出这个链表是否有环?
方法一:
传统的方法:
- 遍历单链表的每一个节点,每遍历一个新的节点,就从头检查新节点之前的所有节点,用新节点和此节点之前所有节点依次作比较。
- 如果发现新节点之前的所有节点当中存在相同节点ID,则说明该节点被遍历过两次,链表有环;
- 如果之前的所有节点当中不存在相同的节点,就继续遍历下一个新节点,继续重复刚才的操作。
- 时间复杂度:O(n²) 空间复杂度:O(1)
方法二:
- 首先创建一个以节点ID为键的HashSet集合,用来存储曾经遍历过的节点。
- 从头节点开始,依次遍历单链表的每一个节点。每遍历到一个新节点,就用新节点和HashSet集合当中存储的节点作比较
- 如果发现HashSet当中存在相同节点ID,则说明链表有环
- 如果HashSet当中不存在相同的节点ID,就把这个新节点ID存入HashSet
- 之后进入下一节点,继续重复刚才的操作。
- 时间复杂度: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和2(在java里就是两个对象引用),同时指向这个链表的头节点。
- 然后开始一个大循环,在循环体中,让指针1每次向下移动一个节点,让指针2每次向下移动两个节点,然后比较两个指针指向的节点是否相同。
- 如果相同,则判断出链表有环,如果不同,则继续下一次循环。
- 时间复杂度: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));
}
}
说明:作者根据网络资料进行搜索学习,理解整理 若有侵权联系作者