链表算法题解题技巧归纳总结

最近集中刷了一批链表的题型,在这里总结一下解题技巧,以及对应题目的解题思路。

解题思路并不会细致入微,主要是为了总结归类,并且希望用几句话来激发灵感,权当是没思路时的指引以及以后复习时的提纲了。

还有一些重要或者总会绕晕的经典题目,也在这里记录一下代码的实现逻辑。

遇到链表相关的题,无论问题是什么,先要想想是不是可以用上以下的两个技巧。

1、哨兵节点

哨兵节点是一个非常常用的链表技巧,在处理链表边界问题的场景下,可以减少我们代码的复杂度。主要解决的问题如下:

因为哨兵节点使用场景很广泛,所以在这里就不针对性的列出典型题目了。

2、双指针

双指针实在是太好用了,其实不止是链表,对于数组题来说,也是非常好用的技巧。

双指针的主要用法有以下几种:

有时,你需要将两条链表合并成一条链表,或者要将一条链表拆分成两条链表。此时,需要用两个指针分别在两条链表上行走,并处理逻辑。

典型题目:

  • 21. 合并两个有序链表:两个指针在两条链表上行走,哪个指针指向的节点值大,则将该节点放到合并后的链表上,指针继续向前一步。
  • 86. 分隔链表:根据指定的目标值,将一个链表先分割成两个链表。因此,需要两个指针在分割出来的两个链表上行走。之后再将两个链表直接头尾合并。
  • 160. 相交链表:两个指针在两条链表上行走,当一个指针指向当前列表的结尾时,继续遍历另外一个链表。这样可以保证两个指针指向相同的节点时,这个节点就是相交节点。

快指针的速度是慢指针 n 倍,则当快指针走完时,慢指针停留的地方就是整个链表的 1/n 处(大概位置)。一般情况下都是慢指针走一步,快指针走两步。

典型题目:

  • 876. 链表的中间结点:快指针走两步,慢指针走一步。快指针走到结尾时,则慢指针在中间位置。
  • 141. 环形链表:快指针走两步,慢指针走一步。当快慢指针相遇时,从头结点再出发一个慢指针,两个慢指针一起走,再次相遇时,则是环的入口。
  • 234. 回文链表:实际上也是找到链表的中间位置,然后去判断是否为回文。判断回文的逻辑,借助栈或者反转后半段链表都可以。
  • 148. 排序链表:在使用归并排序时,也是通过快慢指针找到每个子链表的中间节点,去归并处理。

因为没有办法获取链表的长度,对于寻找倒数第 n 个节点的问题时,这种方法可以一次遍历找到目标节点。

典型题目:

  • 19. 删除链表的倒数第 N 个结点:一个指针先走 n 步,然后两个指针同时前进,当先出发指针遍历完成时,后出发指针就在倒数第 n 个节点。
  • 61. 旋转链表:与上一题类似,只是先走的 n 步可能会超过链表的总长度,需要取模处理一下。

反转链表

反转链表主要有递归法和迭代法两种解决方法,因为指针指来指去的容易晕头转向,所以在这里记录标准的解答代码。

public ListNode reverseList(ListNode head) {
    // 这个head就是原链表的最后一个节点,也是反转后链表的新节点
    if (head == null || head.next == null) {
        return head;
    }
    // 本质上,resultNode就是递归到最深处后,一层层返回的新链表的头结点
    ListNode resultNode = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return resultNode;
}
public ListNode reverseList(ListNode head) {
    ListNode current = head;
    ListNode pre = null;
    ListNode next;
    while (current != null) {
        next = current.next;
        current.next = pre;
        pre = current;
        current = next;
    }

    return pre;
}

LRU缓存

而在读写方面,缓存是有要求的:函数 getput 必须以 O(1) 的平均时间复杂度运行。

为了达成这个要求,底层的数据结构需要用链表来实现。

所以我们实现的 LRU 底层的数据结构是:HashMap +链表。

代码如下,虽然不够优雅,但是方便理解逻辑。

public class LRUCache {

    private int maxSize = 0;
    private Node head = new Node();
    private Node tail = head;
    HashMap map = new HashMap<>();

    public LRUCache(int capacity) {
        this.maxSize = capacity;
        head.next = tail;
        tail.prev = head;
    }

    public int get(int key) {
        if (map.containsKey(key)) {
            Node node = map.get(key);
            // get也是使用了该值,因此需要将该值更新到最近使用的位置
            updateNode(node);
            return node.value;
        }

        return -1;
    }

    public void put(int key, int value) {
        if (map.containsKey(key)) {
            Node node = map.get(key);
            node.value = value;
            updateNode(node);
            return;
        }
        // 新插入的值放到链尾
        Node node = new Node(key, value);
        node.prev = tail;
        map.put(key, node);
        tail.next = node;
        tail = node;
        if (map.size() > maxSize) {
            // 此时需要移除最久未使用数据值
            Node removed = head.next;
            head.next = removed.next;
            head.next.prev = head;
            map.remove(removed.key);
        }

    }

    private void updateNode(Node current) {
        // 如果当前节点已经是尾节点,则不需要移动
        if (current.next == null) {
            return;
        }
        // 当前节点的前节点指向当前节点的后节点,即原链表中删除该节点
        current.prev.next = current.next;
        current.next.prev = current.prev;
        // 当前节点放到尾节点
        current.prev = tail;
        current.next = null;
        tail.next = current;
        // 尾节点移动
        tail = current;
    }

    class Node {
        public int key;
        public int value;
        public Node prev;
        public Node next;
        public Node(){}
        public Node(int key,int value) {
            this.key = key;
            this.value = value;
        }
    }
}

Original: https://www.cnblogs.com/codeflyer/p/16398419.html
Author: 小白码上飞
Title: 链表算法题解题技巧归纳总结

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/570682/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Fork/Join框架

    我们要使用ForkJoin框架,必须首先创建一个ForkJoin任务。它提供在任务中执行 fork()和 join() 操作的机制,通常情况下我们不需要直接继承ForkJoinTa…

    Java 2023年6月6日
    055
  • Java中的static关键字

    Java中的static关键字 static为java中的关键字,可以修饰类中的方法、变量,以及修饰静态代码块当用static修饰的方法和变量时可以直接通过类名.方法名和类名.变量…

    Java 2023年6月8日
    061
  • Fizz网关入门教程-路由初体验

    概念 路由就是定义网关如何处理请求,分反向代理、服务发现、服务编排三种类型。 配置 路由的定义可通过网关管理后台完成: 下面分别介绍反向代理和服务发现路由的配置,设网关部署为 1….

    Java 2023年6月9日
    070
  • 记一次重复造轮子(Obsidian 插件设置说明汉化)

    杂谈 #Java脚本 因本人英语不好在使用Obsidian时,一些插件的设置英文多令人头痛。故有写一个的翻译插件介绍和设置脚本的想法。看到有些前人写的一下翻译方法,简直惨目忍睹。竟…

    Java 2023年6月6日
    065
  • Nginx 源码分析– 浅谈对模块module 的基本认知

    分析nginx源码,谈到模块module是必然的。纵观nginx源码,可以说模块module机制是整个nginx的骨架。因此,对nginx的源码进行分析,那么对模块module就需…

    Java 2023年6月15日
    069
  • SpringBoot开启日志级别

    #开启logging logging.level.org.springframework.boot.autoconfigure: error logging: level: mai…

    Java 2023年6月16日
    050
  • IDEA插件和个性化配置推荐

    插件推荐 我自己现在使用的一些插件和一些自己感觉比较舒服配置分析给大家 idea如何安装插件: 如果打开设置没有看到,直接搜索plugins 然后在这里搜索即可 CodeGlanc…

    Java 2023年6月6日
    095
  • 解决云服务器响应慢,网页加载慢的问题

    问题: 本文接上一次博客,云服务发布springboot项目踩过的坑 自从上次,一咬牙买了阿里云的服务器(虽然是白嫖的15天试用期)。 但是有一个问题一直困扰着我,如鲠在喉! 非常…

    Java 2023年6月6日
    069
  • 用NginX+keepalived实现高可用的负载均衡

    经过前面的配置,如果主服务器的keepalived停止服务,从服务器会自动接管VIP对外服务;一旦主服务器的keepalived恢复,会重新接管VIP。 但这并不是我们需要的,我们…

    Java 2023年5月30日
    071
  • Java实现6种常见排序

    1.冒泡排序(Bubble Sort) 第0轮 3 1 4 1 5 9 2 6 5 3 5 8 9 第1轮 1 3 1 4 5 2 6 5 3 5 8 9 9 第2轮 1 1 3 …

    Java 2023年6月16日
    063
  • JDBC:(java database Connection) java数据库连接。

    JDBC 指 Java 数据库连接,是一种标准Java应用编程接口( JAVA API),用来连接 Java 编程语言和广泛的数据库。 JDBC连接步骤: 1.先导入jar包,把j…

    Java 2023年6月5日
    084
  • 数据结构与算法之插入排序与归并排序

    插入排序举个最好的例子就是扑克牌,当我们手里拿了N张无序的牌,如果要对牌进行排序,最好的思路是从第二张开始,我们设置这个为i,让它和前边的牌做比较如果需要换位置就换,换了以后继续往…

    Java 2023年6月8日
    078
  • Hexo 博客部署至腾讯云

    一.安装环境 推荐购买腾讯云活动的轻应用服务器 2C2G 就可以啦,我买的是 45 一年的:点我购买 以下命令默认在 ubuntu 系统上执行 安装 nginx apt-get i…

    Java 2023年6月8日
    078
  • 类加载器(JVM)

    一.JVM概述 JVM是java是二进制字节码的运行环境 特点: 一次编译,到处运行(跨平台) 自动内存管理 自动垃圾回收功能 常见的JVM Sun Classic VM:世界上第…

    Java 2023年6月7日
    0270
  • 包装类Integer的equal方法与“==”运算符 比较

    包装类Integer的equal方法与”==”运算符 比较 一、在讲述之前先扔出一段代码看看 public static void main(String[…

    Java 2023年6月5日
    062
  • 干掉 PowerDesigner,这款数据库设计神器真的绝了!!!

    最近在造轮子,从 0 到 1 的那种,就差前台的界面了,大家可以耐心耐心耐心期待一下。其中需要设计一些数据库表,可以通过 Navicat 这种图形化管理工具直接开搞,也可以通过一些…

    Java 2023年6月9日
    071
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球