当前位置: 首页 > backend >正文

Leetcode 刷题记录 09 —— 链表第三弹

本系列为笔者的 Leetcode 刷题记录,顺序为 Hot 100 题官方顺序,根据标签命名,记录笔者总结的做题思路,附部分代码解释和疑问解答,01~07为C++语言,08及以后为Java语言。

01 合并 K 个升序链表

在这里插入图片描述

在这里插入图片描述

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode mergeKLists(ListNode[] lists) {}}

方法一:遍历 + 合并两个有序链表

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode mergeKLists(ListNode[] lists) {int k = lists.length;ListNode dummy = null;for(int i=0; i<k; i++){dummy = mergeTwoLists(dummy, lists[i]);}return dummy;}//合并两个有序链表public ListNode mergeTwoLists(ListNode l1, ListNode l2){ListNode l3 = new ListNode(0);ListNode flag = l3; //return flag.nextwhile(l1 != null && l2 != null){if(l1.val < l2.val){l3.next = l1;l1 = l1.next;}else{l3.next = l2;l2 = l2.next;}l3 = l3.next;}if(l1 != null){l3.next = l1;}if(l2 != null){l3.next = l2;}return flag.next;}
}

方法二:二分法(递归)+ 合并两个有序链表

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode mergeKLists(ListNode[] lists) {return merge(lists, 0, lists.length-1);}public ListNode merge(ListNode[] lists, int left, int right){if(left == right){return lists[left];}if(left > right){return null; //为啥捏?}int mid = (left + right) / 2;return mergeTwoLists(merge(lists, left, mid), merge(lists, mid+1, right));}//合并两个有序链表public ListNode mergeTwoLists(ListNode l1, ListNode l2){ListNode l3 = new ListNode(0);ListNode flag = l3; //return flag.nextwhile(l1 != null && l2 != null){if(l1.val < l2.val){l3.next = l1;l1 = l1.next;}else{l3.next = l2;l2 = l2.next;}l3 = l3.next;}if(l1 != null){l3.next = l1;}if(l2 != null){l3.next = l2;}return flag.next;}
}

在什么情况下left > right,为什么要返回null值?

当你调用 mergeKLists 时,假设 lists 为空数组,即 lists.length == 0,那么初始调用 merge(lists, 0, -1) 就会发生 left > right 的情况,在这种情况下,返回 null 是合理的,因为没有链表可以合并。

02 LRU 缓存

在这里插入图片描述

在这里插入图片描述

class LRUCache {public LRUCache(int capacity) {}public int get(int key) {}public void put(int key, int value) {}
}/*** Your LRUCache object will be instantiated and called as such:* LRUCache obj = new LRUCache(capacity);* int param_1 = obj.get(key);* obj.put(key,value);*/

方法:哈希映射 + 双向链表

class LRUCache {//1.自定义双向链表结点class DLinkedNode{int key;int value;DLinkedNode prev;DLinkedNode next;public DLinkedNode() {}public DLinkedNode(int _key, int _value){this.key = _key;this.value = _value;}}//2.自定义哈希映射 cache <int, DLinkedNode>private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();private int size, capacity;private DLinkedNode head, tail;/*3.初始化LRU缓存a.初始化 size, capacityb.初始化 head, tail*/public LRUCache(int capacity) {this.size = 0;this.capacity = capacity;head = new DLinkedNode();tail = new DLinkedNode();head.next = tail;tail.prev = head;}/*4.查询操作:如果key在cache中,返回value;否则,返回-1a. node = null, return -1b. node != null, move to head, return node.value*/public int get(int key) {DLinkedNode node = cache.get(key);if(node == null){return -1;}/*move to heada. remove nodeb. add to head*/node.prev.next = node.next;node.next.prev = node.prev;node.prev = head;node.next = head.next;head.next.prev = node;head.next = node;return node.value;}/*5.更新操作:如果key在cache中,更新value;否则,插入cache <key, nodeNew>a. node = nulli.创建 nodeNewii.插入 cache <key, nodeNew>, add to headiii. size > capacity, remove tailb. node != null, move to head*/public void put(int key, int value) {DLinkedNode node = cache.get(key);if(node == null){DLinkedNode nodeNew = new DLinkedNode(key, value);cache.put(key, nodeNew);++size;// add to headnodeNew.prev = head;nodeNew.next = head.next;head.next.prev = nodeNew;head.next = nodeNew;if(size > capacity){DLinkedNode res = tail.prev;//remove noderes.prev.next = res.next;res.next.prev = res.prev;cache.remove(res.key);--size;}}else{node.value = value;/*move to heada. remove nodeb. add to head*/node.prev.next = node.next;node.next.prev = node.prev;node.prev = head;node.next = head.next;head.next.prev = node;head.next = node;}}
}/*** Your LRUCache object will be instantiated and called as such:* LRUCache obj = new LRUCache(capacity);* int param_1 = obj.get(key);* obj.put(key,value);*/

① 为什么要采用双向链表,而不是单向链表?

  • 快速删除:双向链表可以从链表中的任何位置快速移除节点,因为每个节点都有一个指向前驱节点的指针,而单向链表则需要从头开始遍历才能找到前驱节点。
  • 移动节点到头部:在LRU缓存中,频繁的操作是将节点移动到链表头部。如果采用单向链表,这个操作会更复杂且效率低下,而双向链表可以在常数时间内完成。

② 为什么要将DLinkedNode定义为内部类?

  • 封装DLinkedNode仅在LRUCache中使用,将其作为内部类可以更好地实现封装。
  • 简化代码:在内部类中可以直接访问外部类的私有成员和方法,简化了代码的结构,而无需额外的getter/setter。
  • 逻辑关系清晰DLinkedNode本质上是LRUCache的一部分,通过内部类的方式可以更明确地表达这种包含关系。
http://www.xdnf.cn/news/4481.html

相关文章:

  • 通义读光系列文字检测+识别模型端到端OCR应用
  • 无网络环境下配置并运行 word2vec复现.py
  • tmpfs和普通文件系统相比有哪些优缺点
  • CentOS 安装 Zellij 终端复用器教程
  • Android 移动应用开发:点击按钮打开电话拨号界面
  • Object.defineProperty()
  • LC滤波电路使用TSMI一体成型贴片电感的好处
  • Python初学者笔记第十一期 -- (字符串编程练习题)
  • k8s高可用集群,自动化更新证书脚本
  • 2025-05-07 Unity 网络基础8——UDP同步异步通信
  • 111、二叉树的最小深度
  • 信息革命对经济、货币体系及权力结构的颠覆性影响
  • 数据结构——排序(万字解说)初阶数据结构完
  • 【Python爬虫电商数据采集+数据分析】采集电商平台数据信息,并做可视化演示
  • 【C/C++】虚函数
  • 某大型交通规划设计院转型实践:数智化破局复杂工程项目管理,实现高效人力资源一体化管理
  • 华为设备链路聚合实验:网络工程实战指南
  • 【LeetCode】高频 SQL 50题 题解
  • C语言编程--递归程序--Hanoi塔
  • 企业智能化第一步:用「Deepseek+自动化」打造企业资源管理的智能中枢
  • MEGA3:分子进化遗传学分析和序列比对集成软件
  • 检测内存条好坏有工具,推荐几款内存检测工具
  • github+ Picgo+typora
  • OpenCV提取图像中的暗斑/亮斑
  • IvorySQL 再次走进北京大学研究生开源公选课
  • onenet连接微信小程序(mqtt协议)
  • 【国产化】在银河麒麟ARM环境下离线安装docker
  • Spring 如何解决循环依赖问题?
  • JavaScript性能优化:从青铜到王者的进阶之路
  • 从人体姿态到机械臂轨迹:基于深度学习的Kinova远程操控系统架构解析