文章目录
- 11.1 C++ STL
- 11.2 数组
- 448. 找到所有数组中消失的数字(简单)
- 48. 旋转图像(中等)
- 74. 搜索二维矩阵(中等)
- 240. 搜索二维矩阵 II(中等)
- 769. 最多能完成排序的块(中等)
- 768. 最多能完成排序的块 II(困难)
- 11.3 栈和队列
- 232. 用栈实现队列(简单)
- 155. 最小栈(中等)
- 20. 有效的括号(简单)
- 11.4 单调栈
- 739. 每日温度(中等)
- 11.5 优先队列
- 23. 合并 K 个升序链表(困难)
- 218. 天际线问题(困难)
- 11.6 双端队列
- 239. 滑动窗口最大值(困难)
- 11.7 哈希表
- 1. 两数之和(简单)
- 128. 最长连续序列(中等)
- 149. 直线上最多的点数(困难)
- 11.8 多重集合和映射
- 332. 重新安排行程(困难)
- 11.9 前缀和与积分图
- 303. 区域和检索 - 数组不可变(简单)
- 304. 二维区域和检索 - 矩阵不可变(中等)
- 560. 和为 K 的子数组(中等)
- 11.10 练习
- 566. 重塑矩阵(简单)
- 225. 用队列实现栈(简单)
- 503. 下一个更大元素 II(中等)
- 217. 存在重复元素(简单)
- 697. 数组的度(简单)
- 594. 最长和谐子序列(简单)
- 287 . 寻找重复数(中等)
- 313. 超级丑数
- 870 . 优势洗牌
- 307 . 区域和检索 - 数组可修改
11.1 C++ STL
C++ 提供的数据结构包括:
-
Sequence Containers:维持顺序的容器。
- vector:动态数组,用于 O(1) 的随机读取。因为大部分算法的时间复杂度都会大于 O(n) ,因此我们经常新建 vector 来存储各种数据或中间变量。
- list:双向链表,也可以当作 stack 和 queue 来使用。由于 LeetCode 的题目多用 Node 来表示链表,且链表不支持快速随机读取,因此很少用到该数据结构。 一个例外是经典的 LRU 问题,需要利用链表的特性来解决。
- deque:双端队列,既支持 O(1) 的随机读取,又支持 O(1) 时间的头部增删和尾部增删,不过有一定的额外开销。
- array:固定大小的数组,一般不使用。
- forward_list:单向链表,一般不使用。
-
Container Adaptors:基于其他容器实现的数据结构。
- stack:后入先出(LIFO) 的数据结构,默认基于 deque 实现,stack常用于深度优先搜索、字符串匹配问题以及单调栈问题。
- queue:先入先出(FIFO) 的数据结构,默认基于 deque 实现,queue 常用于广度优先搜索。
- priority_queue:最大值先出的数据结构,默认基于 vector 是实现堆结构。它可以在 O(n logn) 的时间排序数组, O(logn) 的时间插入任意值,O(1) 的时间获得最大值, O(logn) 的时间删除最大值。 priority_queue 常用于维护数据结构并快速获取最大或最小值。
-
Associative Containers:实现了排好序的数据结构。
-
set:有序集合,元素不可以重复,底层实现默认为红黑树,即一种特殊的二叉查找树(BST)。它可以在 O(nlogn) 的时间排序数组,O(logn) 的时间插入、删除、查找任意值,O(logn) 的时间获得最小或最大值。
这里注意,set 和 priority_queue 都可以用于维护数据结构并快速获取最大最小值,但是它们的时间复杂度和功能略有区别。比如, priority_queue 默认不支持删除任意值,而 set 获得最大或最小值的时间复杂度略高。
-
multiset:支持重复元素的 set。
-
map: 有序映射或有序表,在 set 的基础上加上映射关系,可以对每个元素 key 存一个值 value。
-
multimap:支持重复元素的 map。
-
-
Unordered Associative Containers:对每个 Associative Containers 实现了哈希版本。
- unordered_set :哈希集合,可以在 O(1) 的时间快速插入、查找、删除元素,常用于快速查询一个元素是否在这个容器内。
- unordered_multiset:支持重复元素的 unordered_set 。
- unordered_map:哈希映射或哈希表,在 unordered_set 的基础上加上映射关系,可以对每一个元素 key 存一个值 value。在某些情况下,如果 key 的范围已知且较小,我们也可以用 vector 代替 unordered_map,用位置表示 key,每个位置的值表示 value。
- unordered_multimap:支持重复元素的 unordered_map。
11.2 数组
448. 找到所有数组中消失的数字(简单)
思路及代码: 448. 找到所有数组中消失的数字
48. 旋转图像(中等)
思路及代码: 48. 旋转图像
74. 搜索二维矩阵(中等)
思路及代码: 74. 搜索二维矩阵
240. 搜索二维矩阵 II(中等)
思路及代码:240. 搜索二维矩阵 II
769. 最多能完成排序的块(中等)
思路及代码: 769. 最多能完成排序的块
768. 最多能完成排序的块 II(困难)
思路及代码: 768. 最多能完成排序的块 II
11.3 栈和队列
232. 用栈实现队列(简单)
思路及代码: 232. 用栈实现队列
155. 最小栈(中等)
思路及代码: 155. 最小栈
20. 有效的括号(简单)
思路及代码: 20. 有效的括号
11.4 单调栈
单调栈 通过维持栈内值的单调递增(递减)性,在整体 O(n) 的时间内处理需要大小比较的问题。
739. 每日温度(中等)
思路及代码: 739. 每日温度
11.5 优先队列
-
优先队列可以在 O(1) 时间内获得最大值,并且可以在 O(log n) 时间内取出最大值或插入任意值。
-
优先队列常常用 堆 来实现。堆是一个完全二叉树,其每个节点的值总是大于等于子节点的值。实际实现堆的时候,通常用数组而不是指针建立一个树,这是因为堆是完全二叉树,所以用数组表示的时候,位置 i 的节点的父节点位置一定是
(i-1)/2
,而它的两个子节点的位置又一定分别为2i+1
和2i+2
。 -
以下是堆的实现方法,其中最核心的两个操作是上浮和下沉:.
-
如果一个节点比父节点大,那么需要交换这两个节点;交换后它可能比新的父节点还大,因此需要不断进行比较和交换操作,称之为上浮;
-
如果一个节点比父节点小,那么也需要不断地进行向下比较和交换操作,称之为下沉。
如果一个节点有两个子节点,我们总是交换最大的子节点。
-
vector<int> heap;
// 上浮
void swim(int pos){
while(pos > 0 && heap[(pos-1)/2] < heap[pos]){
swap(heap[(pos-1)/2], heap[pos]);
pos = (pos - 1) / 2;
}
}
// 下沉
void sink(int pos){
while(2 * pos + 1 <= N){
int i = 2 * pos + 1;
// 两个子节点进行比较,找到更大的子节点
if(i < N && heap[i] < heap[i+1]) ++i;
if(heap[pos] >= heap[i]) break;
swap(heap[pos], heap[i]);
pos = i;
}
}
// 插入任意值:把新数字放到最后一位,然后上浮
void push(int k){
heap.push_back(k);
swim(heap.size()-1);
}
// 删除最大值:把最后一个数字挪到开头,然后下沉
void pop(){
// 原本的heap[0]就是最大值
heap[0] = heap.back();
heap.pop_back();
sink(0);
}
// 获取最大值
int top(){
return heap[0];
}
- 通过将算法中的大于号和小于号互换,我们也可以得到一个快速获得最小值的优先队列。
- 另外,如果在维持大小关系的同时,还需要支持查找任意值、删除任意值、维护所有数字的大小关系等操作,可以考虑使用 set 或 map来代替优先队列。
23. 合并 K 个升序链表(困难)
思路及代码: 23. 合并 K 个升序链表
218. 天际线问题(困难)
思路及代码: 218. 天际线问题
11.6 双端队列
239. 滑动窗口最大值(困难)
思路及代码: 239. 滑动窗口最大值
11.7 哈希表
- 哈希表 ,又称散列表,使用 O(n) 空间复杂度存储数据,通过哈希函数映射位置,从而实现近似 O(1) 时间复杂度的插入、查找和删除操作。
- c++ 中的哈希集为 unordered_set ,可以查找元素是否在集合中,如果需要同时存储键和值,则需要用 unordered_map,可以用来统计频率,记录内容等。如果元素有限个,并且范围不大,则可以用一个固定大小的数组来存储或统计元素。例如我们需要统计一个字符串中所有字母的出现次数,则可以用一个长度为 26 的数组来进行统计,其哈希函数即为字母在字母表的位置,这样空间复杂度就可以降为常数。
1. 两数之和(简单)
思路及代码: 1. 两数之和
128. 最长连续序列(中等)
思路及代码: 128. 最长连续序列
149. 直线上最多的点数(困难)
思路及代码: 149. 直线上最多的点数
11.8 多重集合和映射
332. 重新安排行程(困难)
思路及代码: 332. 重新安排行程
11.9 前缀和与积分图
- 一维的前缀和,二维的积分图,都是把每个位置之前的一维线段或二维矩形预先存储,方便加速计算。如果需要对前缀和或积分图的值做寻址,则要存在哈希表里;如果要对每个位置记录前缀和或积分图的值,则可以存储到一维或二维数组里,常常伴随着动态规划。
303. 区域和检索 - 数组不可变(简单)
思路与代码: 303. 区域和检索 - 数组不可变
304. 二维区域和检索 - 矩阵不可变(中等)
思路及代码: 304. 二维区域和检索 - 矩阵不可变
560. 和为 K 的子数组(中等)
思路及代码: 560. 和为 K 的子数组
11.10 练习
566. 重塑矩阵(简单)
思路及代码: 566. 重塑矩阵
225. 用队列实现栈(简单)
思路及代码: 225. 用队列实现栈
503. 下一个更大元素 II(中等)
思路及代码: 503. 下一个更大元素 II
217. 存在重复元素(简单)
思路及代码: 217. 存在重复元素
697. 数组的度(简单)
思路及代码: 697. 数组的度
594. 最长和谐子序列(简单)
思路及代码: 594. 最长和谐子序列
287 . 寻找重复数(中等)
思路及代码: 287. 寻找重复数
313. 超级丑数
思路及代码: 313. 超级丑数
870 . 优势洗牌
思路及代码: 870 . 优势洗牌
307 . 区域和检索 - 数组可修改
思路及代码: 307 . 区域和检索 - 数组可修改