【算法训练-链表 三】【特殊链表】环形链表、环形链表II、回文链表、相交链表

简介: 【算法训练-链表 三】【特殊链表】环形链表、环形链表II、回文链表、相交链表

废话不多说,喊一句号子鼓励自己:程序员永不失业,程序员走向架构!本篇Blog的主题是【链表的相关判断】,使用【链表】这个基本的数据结构来实现,这个高频题的站点是:CodeTop,筛选条件为:目标公司+最近一年+出现频率排序,由高到低的去牛客TOP101去找,只有两个地方都出现过才做这道题(CodeTop本身汇聚了LeetCode的来源),确保刷的题都是高频要面试考的题。

名曲目标题后,附上题目链接,后期可以依据解题思路反复快速练习,题目按照题干的基本数据结构分类,且每个分类的第一篇必定是对基础数据结构的介绍

环形链表【EASY】

判断链表中是否有环

题干

解题思路

使用快慢双指针遍历链表,有环则一定会相遇

为什么有环就一定相遇呢?具象化一下问题,如果两个指针在环里不相遇那么只有一种情况,它们保持了相同的相对距离,要想完成这个的前提是二者速度一致。显然当快慢指针的情况下,二者的距离是不断缩小的

  1. 快指针如果慢于慢指针2格或者1格,因为每次距离缩小1,一定会缩小到0
  2. 如果快指针比慢指针大1格,则最多在慢指针走完一圈前追上
  3. 但如果快指针是慢指针3倍,每次距离缩小2,则不一定,如果快指针早于慢指针1格,则会越过慢指针。

代码实现

给出代码实现基本档案

基本数据结构链表

辅助数据结构

算法迭代

技巧双指针

其中数据结构、算法和技巧分别来自:

  • 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树
  • 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
  • 技巧:双指针、滑动窗口、中心扩散

当然包括但不限于以上

import java.util.*;
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        // 1 当链表尾null直接返回false
        if (head == null) {
            return false;
        }
        // 2 定义快慢指针,快指针每次走两步,慢指针每次走一步
        ListNode fast = head;
        ListNode slow = head;
        // 3 直到快指针到链表结尾如果快慢还没有相遇则无环
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            // 如果中途相遇,则有环,停止遍历,直接返回
            if (fast == slow) {
                return true;
            }
        }
        return false;
    }
}

复杂度分析

时间复杂度:遍历链表,时间复杂度为O(N)

空间复杂度:无额外空间使用,空间复杂度为O(1)

环形链表II【MID】

相比于环形链表难度升级在找到环形链表的环的入口节点,链表的尾节点指向该入口节点。

题干

解题思路

我们使用两个指针,fast 与 slow。它们起始都位于链表的头部。随后,slow指针每次向后移动一个位置,而 fast 指针向后移动两个位置。如果链表中存在环,则 fast指针最终将再次与 slow指针在环中相遇。

设链表中环外部分的长度为 a。slow 指针进入环后,又走了 b 的距离与 fast 相遇。此时,fast指针已经走完了环的 n圈,因此它走过的总距离为 a+n(b+c)+b=a+(n+1)b+n,根据题意,任意时刻,fast指针走过的距离都为 slow 指针的 22 倍。因此,我们有

a+(n+1)b+nc=2(a+b)⟹a=c+(n−1)(b+c)

我们会发现:从相遇点到入环点的距离加上 n−1 圈的环长,恰好等于从链表头部到入环点的距离。由以上公式得出:

  1. 如果链表有环,找到相遇点
  2. 两个指针一个从头节点出发,一个从相遇点出发,二者指向同一个节点时即是环的入口节点

也是一种数学推演技巧

代码实现

给出代码实现基本档案

基本数据结构链表

辅助数据结构

算法迭代

技巧双指针

其中数据结构、算法和技巧分别来自:

  • 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树
  • 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
  • 技巧:双指针、滑动窗口、中心扩散

当然包括但不限于以上

import java.util.*;
/*
 public class ListNode {
    int val;
    ListNode next = null;
    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ListNode EntryNodeOfLoop(ListNode pHead) {
        ListNode p1 = pHead;
        ListNode p2 = pHead;
        // 1 如果存在碰撞点,则证明链表有环,将p2设置到碰撞点
        if (getCrashNode(pHead) != null) {
            p2 = getCrashNode(pHead);
        } else {
            // 2 如果不存在碰撞点,则证明链表无环,直接返回null
            return null;
        }
        // 3 节点相交位置即是环入口节点,当然p2可能会经过N圈
        while (p1 != p2) {
            p1 = p1.next;
            p2 = p2.next;
        }
        return p1;
    }
    // 获取换节点碰撞点
    private ListNode getCrashNode(ListNode pHead) {
        ListNode fast = pHead;
        ListNode slow = pHead;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (slow == fast) {
                return fast;
            }
        }
        return null;
    }
}

复杂度分析

时间复杂度:O(n),需要将链表的所有结点遍历一遍,时间复杂度为O(n);

空间复杂度:O(1),需要额外两个快慢指针来遍历结点。

回文链表【EASY】

判断链表是否是回文结构

题干

解题思路

这题是让判断链表是否是回文链表,所谓的回文链表就是以链表中间为中心点两边对称。我们常见的有判断一个字符串是否是回文字符串,这个比较简单,可以使用两个指针,一个最左边一个最右边,两个指针同时往中间靠,判断所指的字符是否相等。但这题判断的是链表,因为这里是单向链表,只能从前往后访问,不能从后往前访问,所以使用判断字符串的那种方式是行不通的。但我们可以通过找到链表的中间节点然后把链表后半部分反转,最后再用后半部分反转的链表和前半部分一个个比较即可

  1. 使用快慢指针找到链表的中点,如果是奇数个,则舍弃中心点
  2. 将链表后半部分反转
  3. 双指针分别在前半部分后半部分移动,如果遇到不相等的值则返回false

最终如果指针到达链尾且值都相同则返回true

代码实现

给出代码实现基本档案

基本数据结构链表

辅助数据结构

算法迭代

技巧双指针

其中数据结构、算法和技巧分别来自:

  • 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树
  • 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
  • 技巧:双指针、滑动窗口、中心扩散

当然包括但不限于以上

import java.util.*;
/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param head ListNode类 the head
     * @return bool布尔型
     */
    public boolean isPail (ListNode head) {
        // 1 判断入参,如果是空链表,返回false
        if (head == null) {
            return false;
        }
        // 2 定义快慢指针,快指针每次两步,到结尾时,慢指针刚好到中间
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        // 如果fast不为null,则链表长度为奇数,且slow位于中点,中点不用判断,所以slow应该向后移一位
        if (fast != null) {
            slow = slow.next;
        }
        // 3 反转后半段回文链表,并且slow指向后半部分头部、fast回到头部,两个链表继续向后
        slow = reverse(slow);
        fast = head;
        while (slow != null) {
            if (slow.val != fast.val) {
                return false;
            } else {
                fast = fast.next;
                slow = slow.next;
            }
        }
        return true;
    }
    private ListNode reverse(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while (cur != null) {
            ListNode pNext = cur.next;
            cur.next = pre;
            pre = cur;
            cur = pNext;
        }
        return pre;
    }
}

复杂度分析

时间复杂度:遍历链表,时间复杂度为O(N)

空间复杂度:无额外空间使用,空间复杂度为O(1)

相交链表【EASY】

当两个链表相交时找到其交点

题干

解题思路

使用两个指针N1,N2,一个从链表1的头节点开始遍历,我们记为N1,一个从链表2的头节点开始遍历,我们记为N2。

  1. 让N1和N2一起遍历,当N1先走完链表1的尽头(为null)的时候,则从链表2的头节点继续遍历,同样,如果N2先走完了链表2的尽头,则从链表1的头节点继续遍历,也就是说,N1和N2都会遍历链表1和链表2。
  2. 因为两个指针,同样的速度,走完同样长度(链表1+链表2),不管两条链表有无相同节点,都能够到达同时到达终点

(N1最后肯定能到达链表2的终点,N2肯定能到达链表1的终点)。

所以,如何得到公共节点:

  • 有公共节点的时候,N1和N2必会相遇,因为长度一样嘛,速度也一定,必会走到相同的地方的,所以当两者相等的时候,则会第一个公共的节点
  • 无公共节点的时候,此时N1和N2则都会走到终点,那么他们此时都是null,所以也算是相等了,这样循环也会终止

可以看做将非公共节点互相补全后,两个指针从头开始遍历,一定会找到终点,终点是一个就表示相交,不是一个都是null,也算相等

代码实现

给出代码实现基本档案

基本数据结构链表

辅助数据结构

算法迭代

技巧双指针

其中数据结构、算法和技巧分别来自:

  • 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树
  • 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
  • 技巧:双指针、滑动窗口、中心扩散

当然包括但不限于以上

import java.util.*;
/*
public class ListNode {
    int val;
    ListNode next = null;
    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        // 1 定义双指针分别指到链表头节点
        ListNode p1 = pHead1;
        ListNode p2 = pHead2;
        // 2 相等时结束循环,可能是相交节点,也可能是null
        while (p1 != p2) {
            p1 = p1 == null ? pHead2 : p1.next;
            p2 = p2 == null ? pHead1 : p2.next;
        }
        // 3 相交节点或null都满足题目要求:如果无相交节点则返回空
        return p1;
    }
}

复杂度分析

时间复杂度:O(m+n)。链表1和链表2的长度之和。

空间复杂度:O(1)。常数的空间。

相关文章
|
14天前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
14天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
15天前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
1月前
|
存储 缓存 算法
经典算法之链表篇(三)
经典算法之链表篇(三)
|
1月前
|
算法
经典算法之链表篇(二)
经典算法之链表篇(二)
|
1月前
|
算法 索引
经典算法之链表篇
经典算法之链表篇
|
1月前
|
算法
❤️算法笔记❤️-(每日一刷-160、相交链表)
❤️算法笔记❤️-(每日一刷-160、相交链表)
17 1
|
1月前
|
算法
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
30 0
|
5月前
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
|
5月前
|
存储 SQL 算法
LeetCode 题目 86:分隔链表
LeetCode 题目 86:分隔链表