3.<tag-链表和快慢针>lt.876-链表的中间结点 + lt.141-环形链表 + lt.142-环形链表 II 2 + 针对环形链表的面试扩展 1
lt.876-链表的中间结点
[案例需求]
[思路分析]
[代码示例一, 朴素写法]
class Solution {
public ListNode middleNode(ListNode head) {
//遍历结点, 得到链表的个数 count, 再次遍历链表 返回地 count/2 + 1 个结点;
int count = 0;
ListNode temp = head;
while(temp != null){
count++;
temp = temp.next;
}
//遍历count/2 + 1 次
temp = head;
for(int i = 1; i < count / 2 + 1; i++){
temp = temp.next;
}
return temp;
}
}
[代码示例二, 快慢针法]
class Solution {
public ListNode middleNode(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
}
lt.141-环形链表
[案例需求]
[思路分析一, 快慢针]
- 环形链表快慢针哈哈, 固定搭配了.
- 同向快慢针一般是fast走两步, slow移动一步, 在探测环时, 由于slow与fast在环内, 随着快指针遍历完一圈后, fast与slow的距离就靠近了一个结点的距离, 经过在圈内多轮的你追我赶, 必将重逢在某一个节点上.
[代码实现]
/**
为什么fast 走两个节点,slow走一个节点,有环的话,一定会在环内相遇呢,而不是永远的错开呢?
首先第一点: fast指针一定先进入环中,如果fast 指针和slow指针相遇的话,一定是在环中相遇,这是毋庸置疑的。
那么来看一下,为什么fast指针和slow指针一定会相遇呢?
这是因为fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。
*/
public class Solution {
public boolean hasCycle(ListNode head) {
//快慢针
if(head == null) return false;
ListNode slow = head.next;
ListNode fast = head.next;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow) return true;
}
return false;
}
}
[思路分析二, 哈希表]
public class Solution {
public boolean hasCycle(ListNode head) {
//环形链表
//1. 集合
//2. 快慢针一次遍历
Set<ListNode> set = new HashSet<>();
ListNode temp = head;
while(true){
if(temp == null)return false;
if(!set.add(temp)){
return true;
}else{
set.add(temp);
}
temp = temp.next;
}
}
}
lt.142-环形链表 II
[案例分析]
[思路分析一, 标记法(会修改链表结点的内容)]
- 把遍历过的结点都通过结点的val打上标记(这个val的值我们设为题目给定范围( node ∈([10的-5次方, 10的5次方])之外;
- 每次遇到的新节点我们先判断是否为标记值, 如果已经是标记值了那就存在环, 如果不是就打上标记, 继续往下遍历, 知道遇到null值或标记值
非递归法
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null || head.next == null)return null;
//标记法
//一次遍历即可
ListNode cur = head;
while(cur != null && cur.val != 2147483647){
cur.val = 2147483647;
cur = cur.next;
}
return cur;
}
}
递归法
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head==null)return null;
if(head.val==100001){
return head;
}else{
head.val=100001;
return detectCycle(head.next);
}
}
}
[思路分析二, 哈希表法]
- 遍历链表, 每个遍历后的结点都要加入到List或Map中, 在加入之前我们先进行判断, 如果结合中已经存在这个节点了, 那么肯定存在环, 直接返回这个节点即可
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null) return null;
/**
遍历一遍链表, 遍历前添加第一个结点, 每遍历一个结点就把当前节点 temp.next 与 集合中的相比
集合中存在, 则说明有环, 返回当前节点 temp;
由于要求返回的是结点, 所以key为索引, value为ListNode 结点
*/
Map<ListNode, Integer> map = new HashMap<>();
ListNode temp = head;
if(temp.next == temp) return temp;
if(temp.next != null && temp.next != temp) map.put(temp, 0);
int index = 1;
while(true){
if(temp == null) return null;
if(map.containsKey(temp.next)){
return temp.next;
}
map.put(temp, index++);
temp = temp.next;
}
return null;
}
}
[思路分析二, 快慢针法法]
详细题解: 点我
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {// 有环
ListNode index1 = fast;
ListNode index2 = head;
// 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
while (index1 != index2) {
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}
面试扩展
环形链表三连问: 1. 是否有环(141) 2. 找出环的入口(142) 3. 环中节点个数
环中节点的个数?
- pre与post相遇在环的入口, 让pre单独在环里再走一圈, 并进行计数, 当pre.next == post时, 返回count+1, 就是所谓的环中节点的个数。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while(true){
if(fast == null || fast.next == null)return null;
slow = slow.next;
fast = fast.next.next;
int count = 1;
if(slow == fast){
ListNode pre = head;
ListNode post = slow;
while(pre != post){
pre = pre.next;
post = post.next;
}
while(post.next != pre){
++count;
post = post.next;
}
System.out.println(count);
return pre;
}
}
}
}
返回环的入口位置和快慢针相遇的位置?
- 环的相遇位置其实就是slow和fast相遇时, 返回slow或fast即可;
- 而环的入口, 实际上就是142的解法, 当slow和fast指针相遇时, 我们设置另外一个指针从head出发, 然后slow与这个指针同步遍历, 相遇点就是环的入口位置。
- 具体看下面:
证明环路?
摘自: 代码随想录
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)