LC-随机链表的复制、排序链表、合并K个升序链表、LRU缓存

随机链表的复制

为了在 O(n) 时间复杂度内解决这个问题,并且使用 O(1) 的额外空间,可以利用以下技巧:

  1. 将新节点插入到原节点后面:我们可以将复制节点插入到原节点后面。例如,如果链表是 A -> B -> C,我们将链表改为 A -> A' -> B -> B' -> C -> C',其中 A'B'C'ABC 的拷贝节点。
  2. 复制 random 指针:因为复制节点与原节点紧挨在一起,我们可以直接利用原节点的 random 指针,来为新节点复制 random 指针。
  3. 拆分链表:最后,我们将原链表和复制链表拆分成两个独立的链表。
/*
// Definition for a Node.
class Node {int val;Node next;Node random;public Node(int val) {this.val = val;this.next = null;this.random = null;}
}
*/class Solution {public Node copyRandomList(Node head) {if(head == null){return null;}//插入新节点到原节点后面Node cur = head;while(cur != null){Node copy = new Node(cur.val);//创建新节点copy.next = cur.next;//新节点的next指向原节点的nextcur.next = copy;//原节点的next指向新节点cur = copy.next;//移动到原节点的下一个节点}//复制random节点cur = head;while(cur != null){if(cur.random != null){cur.next.random = cur.random.next;//新节点的random指向原节点random对应的新节点}cur = cur.next.next;//跳到下一个原节点}//拆分链表,恢复原链表并生成新链表Node newHead = head.next;Node copyCur = newHead;cur = head;while(cur != null){cur.next = cur.next.next;//恢复原链表if(copyCur.next != null){copyCur.next = copyCur.next.next;//更新新链表的next指针copyCur = copyCur.next;}cur = cur.next;}return newHead;}
}

排序链表

解题思路

  1. 归并排序:我们可以使用归并排序来对链表进行排序。归并排序的核心思想是将链表递归地分为两半,对两半分别进行排序,然后将排序后的两部分合并。
  2. 分割链表:我们可以通过快慢指针的方法找到链表的中间节点,从而分割链表。在 findMiddle 方法中,slow 应该是慢指针,每次移动一步;fast 是快指针,每次移动两步。当 fast 到达链表末尾时,slow 应该正好指向中间节点的前一个节点。
  3. 合并两个有序链表:归并排序的合并操作通常是合并两个有序链表。我们可以直接操作链表的 next 指针来合并。

步骤

  1. 递归分割链表:通过快慢指针找到中间节点,将链表分为两部分。
  2. 排序子链表:对每个子链表递归调用归并排序。
  3. 合并两个有序链表:将两个排序后的子链表合并。
/*** 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 sortList(ListNode head) {// 递归终止条件:如果链表为空或者只有一个节点,直接返回if (head == null || head.next == null) {return head;}// 1. 找到链表的中间节点ListNode mid = findMiddle(head);// 2. 将链表分为两部分ListNode left = head;ListNode right = mid.next;mid.next = null;  // 切断链表,确保左右两部分互不影响// 3. 对两部分链表递归排序left = sortList(left);right = sortList(right);// 4. 合并两个有序链表return merge(left, right);}// 找到链表的中间节点(快慢指针)private ListNode findMiddle(ListNode head) {if (head == null || head.next == null) {return head;}ListNode slow = head;ListNode fast = head.next;while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;}return slow;}// 合并两个有序链表private ListNode merge(ListNode l1, ListNode l2) {ListNode dummy = new ListNode(0);ListNode cur = dummy;// 合并两个有序链表while (l1 != null && l2 != null) {if (l1.val < l2.val) {cur.next = l1;l1 = l1.next;} else {cur.next = l2;l2 = l2.next;}cur = cur.next;}// 如果有剩余节点,直接连接if (l1 != null) {cur.next = l1;} else {cur.next = l2;}return dummy.next;}
}

合并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) {//定义一个最小堆PriorityQueue<ListNode> pq = new PriorityQueue<>((a,b) -> a.val - b.val);//将所有链表的头节点加入堆中for(ListNode list : lists){if(list != null){pq.offer(list);}}ListNode dummy = new ListNode(0);ListNode current = dummy;//从堆中取出最小节点,并将其后续节点加入堆while(!pq.isEmpty()){ListNode node = pq.poll();current.next = node;current = current.next;if(node.next != null){pq.offer(node.next);//将当前节点的下一个节点加入堆中}}return dummy.next;}
}

使用归并思想

归并的思想和合并两个有序链表的方法类似。每次从 k 个链表中合并两个链表,直到最终合并成一个链表。这个方法适用于链表数目较少的情况,因为其时间复杂度为 O(k log 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) {if (lists == null || lists.length == 0) {return null;}return mergeKListsHelper(lists, 0, lists.length - 1);}// 分治法,合并两个链表private ListNode mergeKListsHelper(ListNode[] lists, int left, int right) {if (left == right) {return lists[left];}int mid = left + (right - left) / 2;ListNode leftMerged = mergeKListsHelper(lists, left, mid);ListNode rightMerged = mergeKListsHelper(lists, mid + 1, right);return mergeTwoLists(leftMerged, rightMerged);}// 合并两个有序链表private ListNode mergeTwoLists(ListNode l1, ListNode l2) {ListNode dummy = new ListNode(0);ListNode current = dummy;while (l1 != null && l2 != null) {if (l1.val < l2.val) {current.next = l1;l1 = l1.next;} else {current.next = l2;l2 = l2.next;}current = current.next;}if (l1 != null) {current.next = l1;} else {current.next = l2;}return dummy.next;}
}

LRU缓存

思路:

  1. 哈希表(HashMap):用于存储缓存的 key-value 对,通过 key 快速查找对应的值。哈希表中的每个元素将指向双向链表中的一个节点,这样可以在 O(1) 时间内访问链表节点。
  2. 双向链表(Doubly Linked List):用于表示缓存的使用顺序。最近使用的元素放在链表的头部,最久未使用的元素放在链表的尾部。当缓存达到容量限制时,我们可以从尾部移除最久未使用的元素。

操作:

  • get(key)
    • 如果 key 存在缓存中,则返回该值,并将该节点移动到双向链表的头部(表示最近使用)。
    • 如果 key 不存在,返回 -1。
  • put(key, value)
    • 如果 key 已经存在,更新其值,并将该节点移动到链表的头部。
    • 如果 key 不存在,插入新节点,并将其添加到链表头部。如果缓存已满,移除链表尾部的节点(最久未使用)。

设计步骤:

  1. 构造双向链表:节点存储 keyvalue,同时拥有指向前一个节点和后一个节点的指针。
  2. 哈希表存储:将 key 和对应的链表节点关联起来,以便快速查找。
  3. 维护链表的顺序:每次访问节点时,将其移动到链表头部。
class LRUCache {class Node{int key,value;Node prev,next;public Node(int key,int value){this.key = key;this.value = value;}}public Map<Integer,Node> cache;private int capacity;private Node head,tail;public LRUCache(int capacity) {this.capacity = capacity;this.cache = new HashMap<>();head = new Node(0,0);tail = new Node(0,0);head.next = tail;tail.prev = head;}//从链表中移除节点private void remove(Node node){node.prev.next = node.next;node.next.prev = node.prev;}//将节点插入到头部private void insertToHead(Node node){node.next = head.next;node.prev = head;head.next.prev = node;head.next = node;}//获取缓存中的值public int get(int key) {if(cache.containsKey(key)){Node node = cache.get(key);remove(node);//移除节点insertToHead(node);//将节点移到头部return node.value;}return -1;}//插入或更新节点public void put(int key, int value) {if(cache.containsKey(key)){//更新节点的值Node node = cache.get(key);node.value = value;remove(node);insertToHead(node);}else{if(cache.size() >= capacity){//缓存已满,删除尾部节点Node tailNode = tail.prev;remove(tailNode);cache.remove(tailNode.key);}//插入新节点Node newNode = new Node(key,value);cache.put(key,newNode);insertToHead(newNode);//插入到头部}}}/*** 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);*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/19125.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

编码格式大全:类型 特点及其在网络安全中的作用

目录 说明: 1. Base64 Base64编码的字符集通常包括&#xff1a; Base64的工作原理&#xff1a; Base64编码在安全渗透中的应用场景 常见的Base64编码绕过场景 如何防范Base64绕过攻击 2. URL编码&#xff08;Percent Encoding&#xff09; URL编码与安全渗透的关系 示…

BGP分解实验·18——BGP选路原则之权重

在本地对进入的NLRI做权重设置&#xff0c;从而对过滤特定的路由进行优选。严格来说&#xff0c;权重值并不能算是路径属性&#xff0c;因为它并处传递&#xff0c;所能影响的仅仅限于本地路由器。 实验拓扑如下&#xff1a; 完成实验拓扑的基础实验&#xff0c;R1的配置如下…

pandas(13 Caveats Gotchas和SQL比较)

前面内容&#xff1a;pandas(12 IO工具和稀松数据) 目录 一、Caveats警告 & Gotchas预见 1.1 在Pandas中使用if/Truth语句 1.2 位运算布尔 1.3 isin操作 1.4 重新索引reindex和 loc&iloc 使用注意事项 1.5 loc和iloc 二、Python Pandas 与SQL的比较 2.1 数…

FPGA的星辰大海

编者按 时下风头正盛的DeepSeek,正值喜好宏大叙事的米国大统领二次上岗就业,OpenAI、软银、甲骨文等宣布投资高达5000亿美元“星际之门”之际,对比尤为强烈。 某种程度上,,是低成本创新理念的直接落地。 包括来自开源社区的诸多赞誉是,并非体现技术有多“超越”,而是…

AI大模型的文本流如何持续吐到前端,实时通信的技术 SSE(Server-Sent Events) 认知

写在前面 没接触过 SSE&#xff08;Server-Sent Events&#xff09;&#xff0c;AI大模型出来之后&#xff0c;一直以为文本流是用 WebSocket 做的偶然看到返回到报文格式是 text/event-stream,所以简单认知&#xff0c;整理笔记博文内容涉及 SSE 认知&#xff0c;以及对应的 D…

大学信息安全技术 期末考试复习题

一、单选题&#xff08;一&#xff09; 1、在以下人为的恶意攻击行为中&#xff0c;属于主动攻击的是&#xff08; &#xff09;A A&#xff0e;数据篡改及破坏 B&#xff0e;数据窃听 C&#xff0e;数据流分析 D&#xff0e;非法访问 2、数据完整性指的是&#xff08; &#x…

CES 2025 上的创新方案——无电池智能纸尿裤-AP4470

这款纸尿裤采用了可重复使用的组件&#xff0c;通过检测液体的存在来增强老年人和婴儿的护理&#xff0c;即使电极上滴了几滴液体也是如此。 其原理为尿液中的水分作为电解液&#xff0c;将尿布里安装的两种导电性材料作为正负极&#xff0c;充当电池&#xff0c;从而产生300m…

C++17中的LegacyContiguousIterator(连续迭代器)

文章目录 特点内存连续性与指针的兼容性更高的性能 适用场景与C接口交互高性能计算 支持连续迭代器的容器示例代码性能优势缓存局部性指针算术优化 注意事项总结 在C17标准里&#xff0c;LegacyContiguousIterator&#xff08;连续迭代器&#xff09;是一类特殊的迭代器。它不仅…

小白win10安装并配置yt-dlp

需要yt-dlp和ffmpeg 注意存放路径最好都是全英文 win10安装并配置yt-dlp 一、下载1.下载yt-dlp2. fffmpeg下载 二、配置环境三、cmd操作四、yt-dlp下视频操作 一、下载 1.下载yt-dlp yt-dlp地址 找到win的压缩包点下载&#xff0c;并解压 2. fffmpeg下载 ffmpeg官方下载 …

【线性代数】2矩阵

1.矩阵的运算 1.1.定义 矩阵行列式数表数行数和列数可以不相等行数和列数必须相等1.2.加法与数乘 矩阵的数乘:所有元素都乘这个数 矩阵的加法:对应位置处元素相加 🦊已知,求 1.3.乘法 矩阵乘法三步法 ①能不能乘:内定乘 ②乘完是何类型:外定型 ③中的元素是什么:左…

【Leetcode 952】按公因数计算最大组件大小

题干 给定一个由不同正整数的组成的非空数组 nums &#xff0c;考虑下面的图&#xff1a; 有 nums.length 个节点&#xff0c;按从 nums[0] 到 nums[nums.length - 1] 标记&#xff1b;只有当 nums[i] 和 nums[j] 共用一个大于 1 的公因数时&#xff0c;nums[i] 和 nums[j]之…

DeepSeek-R1 大模型本地部署指南

文章目录 一、系统要求硬件要求软件环境 二、部署流程1. 环境准备2. 模型获取3. 推理代码配置4. 启动推理服务 三、优化方案1. 显存优化技术2. 性能加速方案 四、部署验证健康检查脚本预期输出特征 五、常见问题解决1. CUDA内存不足2. 分词器警告处理3. 多GPU部署 六、安全合规…

家里WiFi信号穿墙后信号太差怎么处理?

一、首先在调制解调器&#xff08;俗称&#xff1a;猫&#xff09;测试网速&#xff0c;网速达不到联系运营商&#xff1b; 二、网线影响不大&#xff0c;5类网线跑500M完全没问题&#xff1b; 三、可以在卧室增加辅助路由器&#xff08;例如小米AX系列&#xff09;90~200元区…

win11系统 Docker Desktop提示Docker Engine stopped解决全过程记录

DockerDesktop安装指南以及Windows下WSL2和 Hyper-V相关问题追查 【已解决】win10系统 Docker 提示Docker Engine stopped解决全过程记录 本篇文章主要记录Docker Desktop安装和使用时出现的问题及解决方法&#xff0c;以及后续使用夜神模拟器&#xff0c;关闭了Hyper-V时&am…

手动埋点的demo

上代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>埋点示例</title> </head><b…

数据结构——链表

目录 一、链表 1.1 链表的概念 二、单链表 2.1 单链表的概念 2.2单链表的操作 2.2.1 单链表的结点创建 2.2.2 单链表的头插 2.2.3 单链表遍历 2.2.4 单链表的头删 2.2.5 单链表的尾插 2.2.6 单链表的尾删 2.2.7 单链表按位置插入 2.2.8 单链表按位置删除 2.2.9 单链表按位置修…

LeetCode每日精进:142.环形链表II

题目链接&#xff1a;142.环形链表II 题目描述&#xff1a; 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环…

【办公类-90-02】】20250215大班周计划四类活动的写法(分散运动、户外游戏、个别化综合)

背景需求&#xff1a; 做了中班的四类活动安排表&#xff0c;我顺便给大班做一套 【办公类-90-01】】20250213中班周计划四类活动的写法&#xff08;分散运动、户外游戏、个别化&#xff08;美工室图书吧探索室&#xff09;&#xff09;-CSDN博客文章浏览阅读874次&#xff0…

网络工程师 (42)IP地址

一、定义与功能 IP地址是IP协议提供的一种统一的地址格式&#xff0c;它为互联网上的每一个网络和每一台主机分配一个逻辑地址&#xff0c;以此来屏蔽物理地址的差异。这种地址分配方式确保了用户在连网的计算机上操作时&#xff0c;能够高效且方便地从众多计算机中选出自己所需…

cap1:TensorRT介绍及CUDA环境安装

《TensorRT全流程部署指南》专栏文章目录&#xff1a; cap1&#xff1a;TensorRT介绍及CUDA环境安装cap2&#xff1a;1000分类的ResNet的TensorRT部署指南&#xff08;python版&#xff09;cap3&#xff1a;自定义数据集训练ResNet的TensorRT部署指南&#xff08;python版&…