LeetCode刷题指南
第 0 章 hot100
0.1 哈希
0.2 双指针
0.3 滑动窗口
0.4 子串
0.5 普通数组
0.6 矩阵
0.7 链表
0.8 二叉树
0.9 图论
0.10 回溯
0.11 二分查找
0.12 栈
0.13 堆
0.14 贪心算法
0.15 动态规划
0.16 多维动态规划
0.17 技巧
第0-1章 面试经典150
0.1 数组/字符串
0.2 双指针
0.3 滑动窗口
链表
二叉树
第 1 章 最易懂的贪心算法
1.1 算法解释
1.2 分配问题
1.3 区间问题
1.4 练习
第 2 章 玩转双指针
2.1 算法解释
2.2 Two Sum
2.3 归并两个有序数组
2.4 滑动窗口
2.5 快慢指针
2.6 练习
第 3 章 居合斩!二分查找
3.1 算法解释
3.2 求开方
3.3 查找区间
3.4 查找峰值
3.5 旋转数组查找数字
3.6 练习
第 4 章 千奇百怪的排序算法
4.1 常用排序算法
4.2 快速选择
4.3 桶排序
4.4 练习
第 5 章 一切皆可搜索
5.1 算法解释
5.2 深度优先搜索
5.3 回溯法
5.4 广度优先搜索
5.5 练习
第 6 章 深入浅出动态规划
6.1 算法解释
6.2 基本动态规划:一维
6.3 基本动态规划:二维
6.4 分割类型题
6.5 子序列问题
6.6 背包问题
6.7 字符串编辑
6.8 股票交易
6.9 练习
第 7 章 化繁为简的分治法
7.1 算法解释
7.2 表达式问题
7.3 练习
第 8 章 巧解数学问题
8.1 引言
8.2 公倍数与公因数
8.3 质数
8.4 数字处理
8.5 随机与取样
8.6 练习
第 9 章 神奇的位运算
9.1 常用技巧
9.2 位运算基础问题
9.3 二进制特性
9.4 练习
第 10 章 妙用数据结构
10.1 C++ STL
10.2 Python 常用数据结构
10.3 数组
10.4 栈和队列
10.5 单调栈
10.6 优先队列
10.7 双端队列
10.8 哈希表
10.9 多重集合和映射
10.10 前缀和与积分图
10.11 练习
第 11 章 令人头大的字符串
11.1 引言
11.2 字符串比较
11.3 字符串理解
11.4 字符串匹配
11.5 练习
第 12 章 指针三剑客之一:链表
12.1 数据结构介绍
12.2 链表的基本操作
12.3 其它链表技巧
12.4 练习
第 13 章 指针三剑客之二:树
13.1 数据结构介绍
13.2 树的递归
13.3 层次遍历
13.4 前中后序遍历
13.5 二叉查找树
13.6 字典树
13.7 练习
第 14 章 指针三剑客之三:图
14.1 数据结构介绍
14.2 二分图
14.3 拓扑排序
14.4 练习
第 15 章 更加复杂的数据结构
15.1 引言
15.2 并查集
15.3 复合数据结构
15.4 练习
第16章 面试题
第 17 章 十大经典排序算法
README
本文档使用 MrDoc 发布
-
+
首页
15.3 复合数据结构
# 15.3 复合数据结构 这一类题通常采用哈希表或有序表辅助记录,从而加速寻址;再配上数组或者链表进行连续的数据储存,从而加速连续选址或删除值。 ## [146. LRU Cache](https://leetcode.com/problems/lru-cache/) ### 题目描述 设计一个固定大小的,最近最少使用缓存 (least recently used cache, LRU)。每次将信息插入未满的缓存的时候,以及更新或查找一个缓存内存在的信息的时候,将该信息标为最近使用。在缓存满的情况下将一个新信息插入的时候,需要移除最旧的信息,插入新信息,并将该新信息标为最近使用。 ### 输入输出样例 以下是数据结构的调用样例。给定一个大小为 n 的缓存,利用最近最少使用策略储存数据。 ``` LRUCache cache = new LRUCache( 2 /* capacity */ ); cache.put(1, 1); cache.put(2, 2); cache.get(1); // 输出 value 1 cache.put(3, 3); // 移除 key 2 cache.get(2); // 输出 value -1 (未找到) cache.put(4, 4); // 移除 key 1 cache.get(1); // 输出 value -1 (未找到) cache.get(3); // 输出 value 3 cache.get(4); // 输出 value 4 ``` ### 题解 我们采用一个链表来储存信息的 key 和 value,链表的链接顺序即为最近使用的新旧顺序,最新的信息在链表头节点。同时我们需要一个哈希表进行查找,键是信息的 key,值是其在链表中的对应指针/迭代器。每次查找成功(cache hit)时,需要把指针/迭代器对应的节点移动到链表的头节点。 ```py class Node: def __init__(self, key=-1, val=-1): self.key = key self.val = val self.prev = None self.next = None class LinkedList: def __init__(self): self.dummy_start = Node() self.dummy_end = Node() self.dummy_start.next = self.dummy_end self.dummy_end.prev = self.dummy_start def appendleft(self, node) -> Node: left, right = self.dummy_start, self.dummy_start.next node.next = right right.prev = node left.next = node node.prev = left return node def remove(self, node) -> Node: left, right = node.prev, node.next left.next = right right.prev = left return node def move_to_start(self, node): return self.appendleft(self.remove(node)) def pop(self): return self.remove(self.dummy_end.prev) def peek(self): return self.dummy_end.prev.val class LRUCache: def __init__(self, capacity: int): self.n = capacity self.key_to_node = dict() self.cache_nodes = LinkedList() def get(self, key: int) -> int: if key not in self.key_to_node: return -1 node = self.key_to_node[key] self.cache_nodes.move_to_start(node) return node.val def put(self, key: int, value: int) -> None: if key in self.key_to_node: node = self.cache_nodes.remove(self.key_to_node[key]) node.val = value else: node = Node(key, value) self.key_to_node[key] = node self.cache_nodes.appendleft(node) if len(self.key_to_node) > self.n: self.key_to_node.pop(self.cache_nodes.pop().key) ``` 对于 Python 而言,我们还可以直接利用 OrderedDict 函数实现 LRU,这将大大简化题目难度。这里笔者希望读者还是仔细研读一下以上题解,了解 LRU 实现的核心原理。 ```py class LRUCache: def __init__(self, capacity: int): self.n = capacity self.cache = {} def get(self, key: int) -> int: if key not in self.cache: return -1 self.cache[key] = self.cache.pop(key) return self.cache[key] def put(self, key: int, value: int) -> None: if key in self.cache: self.cache.pop(key) self.cache[key] = value if len(self.cache) > self.n: self.cache.pop(next(iter(self.cache))) ``` ## [380. Insert Delete GetRandom O(1)](https://leetcode.com/problems/insert-delete-getrandom-o1/) ### 题目描述 设计一个插入、删除和随机取值均为 $O(1)$ 时间复杂度的数据结构。 ### 输入输出样例 以下是数据结构的调用样例。 ``` RandomizedSet randomizedSet = new RandomizedSet(); randomizedSet.insert(1); randomizedSet.remove(2); randomizedSet.insert(2); randomizedSet.getRandom(); // 50% 1, 50% 2 randomizedSet.remove(1); randomizedSet.insert(2); randomizedSet.getRandom(); // 100% 2 ``` ### 题解 我们采用一个数组存储插入的数字,同时利用一个哈希表查找位置。每次插入数字时,直接加入数组,且将位置记录在哈希表中。每次删除数字时,将当前数组最后一位与删除位互换,并更新哈希表。随机取值时,则可以在数组内任意选取位置。 ```py class RandomizedSet: def __init__(self): self.nums = [] self.v_to_k = {} def insert(self, val: int) -> bool: if val in self.v_to_k: return False self.v_to_k[val] = len(self.nums) self.nums.append(val) return True def remove(self, val: int) -> bool: if val not in self.v_to_k: return False self.v_to_k[self.nums[-1]] = self.v_to_k[val] self.nums[self.v_to_k[val]] = self.nums[-1] del self.v_to_k[val] self.nums.pop() return True def getRandom(self) -> int: return self.nums[random.randint(0, len(self.nums) - 1)] ```
嘉心糖糖
2025年3月11日 20:35
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码