【算法社区】链表之合并两个有序链表、LRU缓存机制

简介: 字节跳动企业题库,链表系列,从出题频率最高刷到最低,题目有21. 合并两个有序链表 、146. LRU缓存机制

image.png

前言:📫 作者简介:小明java问道之路,专注于研究计算机底层,就职于金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的设计和架构📫

🏆 Java领域优质创作者、阿里云专家博主、华为云享专家🏆

🔥 如果此文还不错的话,还请👍关注点赞收藏三连支持👍一下博主哦

本文导读

字节跳动企业题库,链表系列,因为有leetcode会员能看到企业出题频率,那我们从出题频率最高刷到最低,题目有21. 合并两个有序链表 、146. LRU缓存机制


21. 合并两个有序链表

【简单】【题目描述】将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4]

示例 2:输入:l1 = [], l2 = [] 输出:[]

示例 3:输入:l1 = [], l2 = [0] 输出:[0]

这道题是一到老生常谈的题目,对于熟悉链表的同学来说是秒杀的,我们分递归和迭代两种方法解题

【迭代、递归】图解:

image.png


代码详解,逐行注释:

/**
 * 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 {
    /* 递归法:O(m+n),空间复杂度O(m+n) */
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        // 如果两个链表有一个为空,递归结束。
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        // 判断 l1 和 l2 哪一个链表的头节点的值更小,然后递归地决定下一个添加到结果里的节点。
        if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}


 146. LRU缓存机制

【中等】【题目描述】运用你所掌握的数据结构,设计和实现一个LRU(最近最少使用) 缓存机制

实现 LRUCache 类:

LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存

int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。

void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?

示例:输入:

["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]

[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]

输出:[null, null, null, 1, null, -1, null, -1, 3, 4]

解释:

LRUCache lRUCache = new LRUCache(2);

lRUCache.put(1, 1); // 缓存是 {1=1}

lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}

lRUCache.get(1);    // 返回 1

lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}

lRUCache.get(2);    // 返回 -1 (未找到)

lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}

lRUCache.get(1);    // 返回 -1 (未找到)

lRUCache.get(3);    // 返回 3

lRUCache.get(4);    // 返回 4

【模拟法】图解:

image.png


代码详解,逐行注释:

publicclassLRUCache {
classDLinkedNode {
intkey;
intvalue;
DLinkedNodeprev;
DLinkedNodenext;
publicDLinkedNode() {}
publicDLinkedNode(int_key, int_value) {key=_key; value=_value;}
    }
privateMap<Integer, DLinkedNode>cache=newHashMap<Integer, DLinkedNode>();
privateintsize;
privateintcapacity;
privateDLinkedNodehead, tail;
publicLRUCache(intcapacity) {
this.size=0;
this.capacity=capacity;
// 使用伪头部和伪尾部节点head=newDLinkedNode();
tail=newDLinkedNode();
head.next=tail;
tail.prev=head;
    }
publicintget(intkey) {
DLinkedNodenode=cache.get(key);
if (node==null) {
return-1;
        }
// 如果 key 存在,先通过哈希表定位,再移到头部moveToHead(node);
returnnode.value;
    }
publicvoidput(intkey, intvalue) {
DLinkedNodenode=cache.get(key);
if (node==null) {
// 如果 key 不存在,创建一个新的节点DLinkedNodenewNode=newDLinkedNode(key, value);
// 添加进哈希表cache.put(key, newNode);
// 添加至双向链表的头部addToHead(newNode);
++size;
if (size>capacity) {
// 如果超出容量,删除双向链表的尾部节点DLinkedNodetail=removeTail();
// 删除哈希表中对应的项cache.remove(tail.key);
--size;
            }
        }
else {
// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部node.value=value;
moveToHead(node);
        }
    }
privatevoidaddToHead(DLinkedNodenode) {
node.prev=head;
node.next=head.next;
head.next.prev=node;
head.next=node;
    }
privatevoidremoveNode(DLinkedNodenode) {
node.prev.next=node.next;
node.next.prev=node.prev;
    }
privatevoidmoveToHead(DLinkedNodenode) {
removeNode(node);
addToHead(node);
    }
privateDLinkedNoderemoveTail() {
DLinkedNoderes=tail.prev;
removeNode(res);
returnres;
    }
}
/*** 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);*/
相关文章
|
5月前
|
安全 vr&ar
降本增效神器:AR眼镜远程协助在数字化工业的应用
AR眼镜助力工业维修,实现远程实时协助,提升效率,降低成本,推动工业智能化发展。
|
7月前
|
机器学习/深度学习 人工智能 算法
基于FPGA的SNN脉冲神经网络之IM神经元verilog实现,包含testbench
本内容介绍了一种基于Izhikevich-Memristive(IM)神经元模型的算法,该模型结合忆阻器特性和神经元动力学,适用于神经形态计算。算法通过Vivado2019.2运行,提供无水印运行效果预览及部分核心程序,完整版含中文注释与操作视频。理论部分详细解析了Izhikevich神经元方程及其放电行为,包括膜电位、恢复变量等参数的作用,并探讨了IM模型在人工智能和脑机接口领域的应用潜力。
|
6月前
|
存储 缓存 分布式计算
OSS大数据分析集成:MaxCompute直读OSS外部表优化查询性能(减少数据迁移的ETL成本)
MaxCompute直读OSS外部表优化方案,解决传统ETL架构中数据同步延迟高、传输成本大、维护复杂等问题。通过存储格式优化(ORC/Parquet)、分区剪枝、谓词下推与元数据缓存等技术,显著提升查询性能并降低成本。结合冷热数据分层与并发控制策略,实现高效数据分析。
160 2
|
4月前
|
消息中间件 人工智能 缓存
Go与Java Go和Java微观对比
本文对比了Go语言与Java在线程实现上的差异。Go通过Goroutines实现并发,使用`go`关键字启动;而Java则通过`Thread`类开启线程。两者在通信机制上也有所不同:Java依赖共享内存和同步机制,如`synchronized`、`Lock`及并发工具类,而Go采用CSP模型,通过Channel进行线程间通信。此外,文章还介绍了Go中使用Channel和互斥锁解决并发安全问题的示例。
252 0
|
7月前
|
Arthas 监控 Java
Arthas jvm(查看当前JVM的信息)
Arthas jvm(查看当前JVM的信息)
275 17
|
8月前
|
存储 弹性计算 资源调度
阿里云服务器收费模式对比:包年包月与按量付费的适用场景与选择参考
在我们购买阿里云服务器的时候,云服务器的收费模式主要有多种收费模式,其中包年包月和按量付费两种主流模式。对于准备在阿里云上部署应用的用户来说,选择合适的收费模式至关重要,因为它直接关系到成本控制和资源使用的灵活性。本文将对这两种收费模式做一个对比,以供参考和选择。
1132 14
|
JavaScript
jQuery 效果 方法
jQuery 效果 方法
75 3
|
机器学习/深度学习 数据可视化 TensorFlow
基于tensorflow深度学习的猫狗分类识别
基于tensorflow深度学习的猫狗分类识别
698 1
Windbg双击调试(真机WIN10+虚拟机WIN10)
Windbg双击调试(真机WIN10+虚拟机WIN10)
234 0
|
机器学习/深度学习 人工智能 算法
milvus源码编译
milvus源码编译
387 1