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

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

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

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

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

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/714806/

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

(0)

大家都在看

  • typora使用

    一:typora、配置picgo+github安装以及配置 picgo 、Node.js和typora安装包见百度网盘链接: 参考的教程如下: 二:typora使用语法 Ctrl+…

    技术杂谈 2023年6月21日
    072
  • 帮助你更好的理解Spring循环依赖

    网上关于Spring循环依赖的博客太多了,有很多都分析的很深入,写的很用心,甚至还画了时序图、流程图帮助读者理解,我看了后,感觉自己是懂了,但是闭上眼睛,总觉得还没有完全理解,总觉…

    技术杂谈 2023年7月25日
    064
  • MySQL学习-idea连接数据库导入jar包

    导包先有包 !!!一定要下载和自己MySQL版本一样的jar包!!! !!!一定要下载和自己MySQL版本一样的jar包!!! !!!一定要下载和自己MySQL版本一样的jar包!…

    技术杂谈 2023年6月21日
    091
  • 快速模幂算法

    快速模幂算法就是将指数变成二进制数来计算,每次按照底数的二进制次方进行计算,因为底数相乘指数相加,又模和乘可以相互变化,所以最后可以一边模一边乘,最后得出的结果还是正确的。 例如:…

    技术杂谈 2023年6月21日
    089
  • 苞米面C++模板库介绍

    苞米面 C++ 模板库,无需编译,直接包含头文件就可以。所有模板类和算法都包含在 bmm 名字空间里,例如: bmm::recent。需要 C++ 编译器,支持 C++17 标准,…

    技术杂谈 2023年7月23日
    059
  • Jquery_HTML-对HTML内容删除添加、操作CSS改变样式、遍历定位元素

    1 DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset=&q…

    技术杂谈 2023年7月24日
    070
  • 利用C#怎么获取 List集合中的重复值Linq操作

    跟大家聊聊有关利用C#怎么获取 List集合中的重复值,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。 一、获取集合内重复值…

    技术杂谈 2023年5月31日
    087
  • pyuic5和pyrcc的使用方法

    一、如果是使用 Qt Designer设计界面的话,那么如何将Qt Designer设计出来的界面(.ui 文件)与业务逻辑程序接合起来,如下两个方法:方法一:将.ui 文件通过命…

    技术杂谈 2023年7月11日
    077
  • 去掉烦人的:要恢复页面吗?Chrome未正确关闭

    1、将 sudo chmod 444 /home/username/.config/chromium/Default/Preferences文件权限设置读权限。 2、使用 sudo…

    技术杂谈 2023年5月31日
    087
  • Java注解与原理分析

    一、注解基础 二、注解原理 三、常用注解 1、JDK注解 2、Lombok注解 四、自定义注解 1、同步控制 2、类型引擎 五、参考源码 使用的太多,被忽略的理所当然; 一、注解基…

    技术杂谈 2023年7月24日
    062
  • 二十一、XML

    二十一、XML 21.1 XML介绍 21.1.1 一个问题引入 XML 思考:前面的反射可以加载配置文件里的信息,获取类的字节码对象从而动态创建对象和调用方法,但是如果需要创建多…

    技术杂谈 2023年7月11日
    068
  • 十一、枚举 Enumeration(完结)

    十一、枚举 Enumeration 11.1 枚举的引入 需求:用类表示季节 传统方法:声明 Season 类,有 name,temperature 两个属性,构造器,get,se…

    技术杂谈 2023年7月11日
    058
  • 从学校到公司,2022新的起点!!!

    步入新的阶段 目前仍然是大学生的身份,但也算是打工人了。2021秋招时来到了天津的一个公司做实习生,并签订了三方协议。已经来公司将近一个月了,我在这段时间想了很多关于我的未来发展方…

    技术杂谈 2023年7月11日
    052
  • Windows针对子目录共享权限控制

    Windows的共享文件设置有两种,一种是共享这一个目录然后里面的子文件,文件夹权限则集成;一种是共享这个目录后,里面的子文件与文件夹权限可单独控制。 共享一 image-2021…

    技术杂谈 2023年6月21日
    091
  • 宽带测速网站收集

    国际通用: 电信: 移动: 联通: 总结: 1、电信的测速比较专业,但要安装flash 2、移动和联通比较难找,需要安装一堆插件,看起来非常像钓鱼的站点。 3、如果没什么特殊需求,…

    技术杂谈 2023年5月31日
    084
  • 初识C++05:运算符重载

    运算符重载 运算符重载基础 函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作。 运算符重载(Operator Overlo…

    技术杂谈 2023年7月25日
    059
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球