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

面试硬核双杀!合并 K 个升序链表 + LRU 缓存|力扣高频手撕原题全解

目录

23. 合并 K 个升序链表

题目链接

题目简介

解题思路

满分代码(规范注释 + 极简优雅)

核心亮点

146. LRU 缓存

题目链接

题目简介

解题思路

满分代码(手撕标准模板)

核心亮点


23. 合并 K 个升序链表

题目链接

23. 合并 K 个升序链表 - 力扣(LeetCode)

题目简介

给你一个链表数组,每个链表都已经按升序排列,请你将所有链表合并到一个升序链表中,返回合并后的链表。

解题思路

暴力合并 k 个链表时间复杂度极高,最优解:分治思想 + 归并排序核心逻辑极简清晰:

  1. :利用分治将 k 个链表不断拆分,直到拆分为两两一组的最小单元;
  2. :复用「合并两个有序链表」的模板,将拆分后的链表两两合并;
  3. :层层合并最终得到完整的升序链表。整体时间复杂度优化至O(n log k),是面试标准解法!

满分代码(规范注释 + 极简优雅)

java

运行

/** * 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) { // 边界判断:空数组直接返回null if(lists == null || lists.length == 0){ return null; } // 分治递归合并 return slove(lists, 0, lists.length - 1); } // 分治核心:拆分链表,两两合并 public ListNode slove(ListNode[] arr, int left, int right){ // 递归终止:只剩一个链表,无需合并 if(left == right) { return arr[left]; } // 找中点,拆分左右两部分 int mid = (left + right) / 2; ListNode lnode = slove(arr, left, mid); ListNode rnode = slove(arr, mid + 1, right); // 合并两个有序链表 return merger(lnode, rnode); } // 模板:合并两个升序链表 public ListNode merger(ListNode left, ListNode right){ // 虚拟头节点,简化拼接操作 ListNode res = new ListNode(Integer.MIN_VALUE); ListNode h = res; // 双指针遍历,按大小拼接 while(left != null && right != null){ if(left.val < right.val){ h.next = left; left = left.next; }else{ h.next = right; right = right.next; } h = h.next; } // 拼接剩余节点 h.next = left != null ? left : right; return res.next; } }

核心亮点

  1. 分治思想完美解决 k 链表合并问题,复杂度最优;
  2. 复用双指针合并有序链表模板,代码简洁无冗余;
  3. 面试高频默写题,思路固定易掌握。

146. LRU 缓存

题目链接

146. LRU 缓存 - 力扣(LeetCode)

题目简介

请你设计并实现一个满足LRU (最近最少使用) 缓存约束的数据结构,实现get获取数据和put写入数据方法,超出容量时自动删除最久未使用的数据。

解题思路

LRU 是面试手撕天花板,核心设计:双向链表 + 哈希表

  1. 双向链表:维护节点使用顺序,头部存最新使用节点,尾部存最久未使用节点,方便快速增删;
  2. 哈希表:O (1) 时间定位节点,解决链表查找慢的问题;
  3. 核心操作:
    • get:查询节点,存在则刷新到链表头部;
    • put:新增 / 更新节点,超容则删除尾部节点。

满分代码(手撕标准模板)

java

运行

class LRUCache { // 自定义双向链表节点 class Node { int key; int value; Node pre; Node next; } // 缓存容量 private int capacity; // 双向链表头尾节点 private Node first; private Node last; // 哈希表:O(1)定位节点 private Map<Integer, Node> map; // 初始化LRU缓存 public LRUCache(int capacity) { this.capacity = capacity; map = new HashMap<>(); } // 获取节点值 public int get(int key) { Node node = map.get(key); // 节点不存在返回-1 if(node == null){ return -1; } // 刷新节点到链表头部(标记为最近使用) moveToHead(node); return node.value; } // 写入/更新节点 public void put(int key, int value) { Node node = map.get(key); if(node == null){ // 新增节点 node = new Node(); node.key = key; node.value = value; // 容量超限,删除最久未使用(尾部节点) if(map.size() == capacity){ removeLast(); } // 添加到头部并更新哈希表 addToHead(node); map.put(key, node); }else{ // 更新节点值,并刷新到头部 node.value = value; moveToHead(node); } } // 将节点移动到链表头部 public void moveToHead(Node node){ // already 头部,直接返回 if(node == first){ return; } // 是尾部节点,更新尾指针 if(node == last){ last.pre.next = null; last = last.pre; }else{ // 中间节点,断开前后连接 node.pre.next = node.next; node.next.pre = node.pre; } // 插入头部 node.next = first; first.pre = node; first = node; } // 删除尾部节点(最久未使用) public void removeLast(){ map.remove(last.key); Node lastPre = last.pre; if(lastPre != null){ lastPre.next = null; last = lastPre; } } // 添加节点到头部 public void addToHead(Node node){ // 空缓存,头尾都是当前节点 if(map.isEmpty()){ first = node; last = node; }else{ node.next = first; first.pre = node; first = 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); */

核心亮点

  1. 双向链表 + 哈希表黄金组合,读写均为 O (1) 最优复杂度;
  2. 头尾指针简化链表操作,无冗余判断;
  3. 面试必考手撕题,此模板可直接复用。
http://www.cnnetsun.cn/news/1542872.html

相关文章:

  • 终极解决方案:如何用G-Helper一键恢复ROG游戏本色彩配置文件
  • 为什么92%的金融级Python项目已在Q1完成AOT安全迁移,而你还在用CPython解释器?
  • 【Python原生AOT编译2026终极指南】:6大高频报错根源定位+3步热修复方案(PyO3/CPython 3.14+实测有效)
  • 为什么你的Python网关在EMC测试中随机重启?深度拆解CPython嵌入式移植的6大实时性盲区(附FreeRTOS+Python3.11混合调度方案)
  • 别再只盯着像素了!从镜头到屏幕:一次搞懂影响你手机成片效果的完整链路(附避坑指南)
  • volatile vs synchronized:Java 并发两大护法
  • Transformer回顾与BERT模型学习:小白程序员必备收藏指南
  • 思源宋体终极编译指南:从源码到可部署字体的完整流程
  • Qwen-Image-Lightning参数详解:10个关键设置提升生成质量
  • Docker Desktop+WSL2自定义安装路径实战指南
  • 键盘优化:机械键盘连击修复与输入稳定解决方案实战指南
  • 用Python+海康工业相机(MV-CH120-60UM)搭建一个简易的条形码扫描器(附完整代码)
  • Java毕业设计基于springboot+vue的武汉周边农家乐信息管理系统
  • OpCore-Simplify:2024年最完整的黑苹果自动化EFI构建终极指南
  • KITTI数据集实战指南:从下载到3D物体检测的完整流程(附避坑技巧)
  • open_clip:多模态模型工业化落地全方案
  • 【uniapp实战】相册图片二维码识别:从压缩优化到原生API调用的完整指南
  • OpenClaw调试技巧:GLM-4.7-Flash复杂任务拆解的5个可视化工具
  • DanKoe 视频笔记:说服性沟通:21 世纪的核心技能 [特殊字符]
  • ESP32语音交互开发实战:从离线唤醒到智能家居控制
  • 突破显卡限制:OptiScaler实现全平台AI超分辨率技术自由切换
  • Java SpringBoot+Vue3+MyBatis 学生成绩分析和弱项辅助系统系统源码|前后端分离+MySQL数据库
  • CCM Buck变换器建模进阶:从平均模型到小信号分析的实践指南
  • 告别Ollama本地部署!用硅基流动API+DeepSeek R1,5分钟搞定AI桌宠语音聊天
  • 淘宝淘金币自动脚本:每天节省20分钟的免费终极解决方案
  • 老旧Mac如何焕发新生?OpenCore Legacy Patcher全流程系统升级指南
  • llamafactory环境配置与关键包版本兼容性指南
  • 米文AD10 GMSL摄像头配置与ROS驱动集成指南
  • 3步实现专业设计软件的完美中文界面本地化
  • 用Cursor+Claude打造微信智能客服:从PRD到代码的完整开发流程