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次方, 105次方])之外;
- 每次遇到的新节点我们先判断是否为标记值, 如果已经是标记值了那就存在环, 如果不是就打上标记, 继续往下遍历, 知道遇到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与这个指针同步遍历, 相遇点就是环的入口位置。
  • 具体看下面:

证明环路?

摘自: 代码随想录

posted @   青松城  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示