technology-note/算法/leetcode/常规题/Q142 环型链表2.md
2022-03-16 11:08:59 +08:00

112 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: "20210402"
date: "2021/04/02 17:15"
title: "Q142 环形链表 II(Linked List Cycle II)"
tags: ["java", "leetcode"]
categories:
- "算法"
- "leetcode刷题"
---
### 解析思路
  leetcode 中等难度,题目描述[点击这里](https://leetcode-cn.com/problems/linked-list-cycle-ii/)。
  看到题目描述首先蹦出来的解法就是遍历链表记录走过的节点当某个节点第二次走到的时候说明该节点就是环的起点当节点没有下个节点说明没有环。本方法很简单不做详细说明用一个HashSet记录访问过的节点即可。
  有没有更好的版本呢?当然是有的。通常使用**快慢指针**的办法来解决这类链表环的问题。思路如下:
1. 定义两个指针slow(慢指针每次走1个)fast(快指针,每次走两个),都从head出发。
2. 每进行一次移动fast和slow间隔的节点就会+1,由此可以推断当slow进入环后一定会和fast相遇
<!-- more -->
3. 假设链表非环的长度为a,环的长度为b,fast走过的距离为f,slow走过的距离为s,当fast在环中循环n次后fast,slow首次相遇可得到如下表达式
f=2s(f的速度是s的两倍)
f=s+nb(一定比s走的距离多n圈才会相遇)
由上面两个得到s=nb,由于s走的距离肯定包含了a,可以得到s在环中走的距离为nb-a,只需要继续走a的距离就能刚好走到环的起点。
4. 所以再加一个指针c从head开始每次移动一格当c和slow相遇时说明到了环的起点
java如下
```java
package com.fanxb.common;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* 环形链表 II
* 题目地址:https://leetcode-cn.com/problems/linked-list-cycle-ii/
* 解题思路:
* 解法1只要找到是否某个节点访问了两次即可用set记录已访问过的节点当访问的节点在set中说明找到了环形链表头当节点没有下一个节点说明不存在环
* 更优化的解法2
* 使用快慢指针定义两个指针slow(慢指针每次走1个)fast(快指针,每次走两个),都从head出发。
* 每进行一次移动fast和slow间隔的节点就会+1,由此可以推断当slow进入环后一定会和fast相遇
* 假设链表非环的长度为a,环的长度为b,fast走过的距离为f,slow走过的距离为s,当fast在环中循环n次后fast,slow首次相遇可得到如下表达式
* f=2s(f的速度是s的两倍)
* f=s+nb(一定比s走的距离多n圈才会相遇)
* 由上面两个得到s=nb,由于s走的距离肯定包含了a,可以得到s在环中走的距离为nb-a,只需要继续走a的距离就能刚好走到环的起点。
* 所以再加一个指针c从head开始每次移动一格当c和slow相遇时说明到了环的起点
*
* @author fanxb
* Date: 2020/6/9 15:10
*/
public class Q142 {
public static class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
public static ListNode solution(ListNode head) {
Set<ListNode> set = new HashSet<>();
while (head != null) {
if (set.contains(head)) {
return head;
} else {
set.add(head);
head = head.next;
}
}
return null;
}
public static ListNode betterSolution(ListNode head) {
ListNode slow = head, fast = head;
do {
if (fast == null || fast.next == null) {
return null;
}
slow = slow.next;
fast = fast.next.next;
} while (slow != fast);
//此时快慢指针相遇,再加一个指针c
ListNode c = head;
while (c != slow) {
slow = slow.next;
c = c.next;
}
return c;
}
}
```
本体存在一些变体也是一样的解法,
比如找到开始节点后的第n个节点可在找到头后再遍历n次
比如找到开始节点前的第n个节点一样的找到头后再遍历一遍环得到环长度再计算出需要遍历的次数得到距离开始节点前的第n个节点
<span id="blogIdSpan" style="display:none">20210402</span>