description和keywords都是给爬虫看的

map myMap键值对,键唯一,按照键的大小自动排序
1 2 3 4 5 6 7 8 9 10 11 12 13
| myMap.insert(make_pair(1, 100)) #make_pair打包键值对 myMap[2] = 200; myMap.insert({{3, 300}, {4, 400}});
auto it = myMap.find(3); if (it != myMap.end()) { a= it->first; b= it->second ; } else { }
myMap.erase(1);
|
str.size()获取列表长度
string定义字符串
unordered_map:适合对元素顺序没有要求,且需要快速访问的场景,尤其是在元素数量较多且频繁进行查找、插入和删除操作时。
.push_back(),在vector后面添加元素
vector> ans 多维数组
unordered_set 只允许存储唯一的元素,列表去重
1 2 3 4
| for(int&num:nums){ st.count(num-1) }
|
哈希表:
数组,哈希范围可控,数值较小的情况
,set数值很大
,map键值对
三种set
uordered_set自动去重
KMP算法
那么什么是前缀表:记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
所以字符串a的最长相等前后缀为0。 字符串aa的最长相等前后缀为1。 字符串aaa的最长相等前后缀为2。 等等…..。
const string&str来代替string str直接传参,减少内存开销,避免完整拷贝,占用额外空间。
栈和队列
栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。
所以STL中栈往往不被归类为容器,而被归类为container adapter(容器适配器)。
我们常用的SGI STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的底层结构
deque 是双端队列
1
| std::stack<int, std::vector<int> > third;
|
队列中先进先出的数据结构,同样不允许有遍历行为,不提供迭代器, SGI STL中队列一样是以deque为缺省情况下的底部结构。
1
| std::queue<int, std::list<int>> third;
|
队列的底层容器不能用vector
vector的pop_front()是O(n)(需要移动所有元素),而deque和list的pop_front()是O(1)
栈操作
stackst
| 函数 |
功能 |
返回类型 |
时间复杂度 |
push(const T& val) |
元素入栈 |
void |
O(1) |
pop() |
移除栈顶元素 |
void |
O(1) |
top() |
返回栈顶元素(不删除) |
T&(可修改)或 const T&(只读) |
O(1) |
empty() |
判断栈是否为空 |
bool(true 为空) |
O(1) |
size() |
返回栈中元素数量 |
size_t(无符号整数) |
O(1) |
队列操作
queue que
| 操作 |
函数 |
返回类型 |
注意事项 |
| 入队 |
push() / emplace() |
void |
emplace 更高效 |
| 出队 |
pop() |
void |
需先调用 front() |
| 访问队首 |
front() |
T& 或 const T& |
可修改或只读 |
| 访问队尾 |
back() |
T& 或 const T& |
可修改或只读 |
| 判空 |
empty() |
bool |
true 表示空 |
| 大小 |
size() |
size_t |
元素数量 |
字符串拼接可以用pushback也可以直接+;
1 2 3 4 5 6
| string result;
result+=st.top();
reverse(result.begin(),result.end());
|
switch不支持string判别
哈希表转vector
vector> sortedPairs(hashMap.begin(), hashMap.end());
sort按照容器自定义排序 []中是引用外部变量,也是三种传参方式
1 2 3 4 5 6 7
| std::sort( sortedPairs.begin(), sortedPairs.end(), [](const auto& a, const auto& b) { return a.second < b.second; } );
|
1 2 3 4 5 6 7 8 9
| std::sort(nums.begin(), nums.end(), [threshold](int a, int b) { bool a_gt = (a > threshold); bool b_gt = (b > threshold); if (a_gt != b_gt) { return a_gt > b_gt; } else { return a < b; } });
|
multimap只能用.insert传参,因为可以包含重复键值对,但是multimap是有序的默认升序,都是按照键来排序
传参直接用{,}
1 2 3 4
| std::multimap<int, std::string> sortedMap; for (const auto& pair : hashMap) { sortedMap.insert({pair.second, pair.first}); }
|
multimap实现降值排序,map同样适用
1 2 3 4 5 6 7
| std::multimap<int, std::string, std::greater<int>> mmap = { {3, "Banana"}, {1, "Apple"}, {2, "Avocado"}, {1, "Apricot"} };
|
判断multimap是否有重复
哈希表转数组,注意数组变量的前后要用->来引用,
注意从后向前遍历,可以用rbegin()和rend()
1 2 3 4 5 6 7
| vector<pair<int, int>> sortedPairs(sortmap.begin(), sortmap.end()); vector<int>result; for(auto it =sortedPairs.rbegin();it<sortedPairs.rbegin()+k;it++) { result.push_back(it->second); }
|
快速排序
通过一趟排序将待排数据分割成独立的两部分,其中一部分的所有元素均比另一部分小,然后递归地对这两部分继续排序,最终完成整个序列的排序
| 快速排序 |
O(n log n) |
不稳定 |
O(log n) |
通用、数据量大 |
| 归并排序 |
O(n log n) |
稳定 |
O(n) |
需要稳定排序或外部排序 |
大顶堆和小顶堆
优先级队列
其实就是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。
堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。
比较规则的作用
返回值决定顺序
return a.first > b.first的布尔结果会告诉priority_queue
如果为 true:a 应该排在 b 的后面(即 a 的优先级更低)。
如果为 false:a 应该排在 b 的前面或保持原顺序(即 a 的优先级更高或相等)。
优先级队列的操作
| 操作 |
正确性 |
说明 |
pri_que.push(it) |
❌ 错误 |
类型不匹配,且可能引发悬空指针。 |
pri_que.push(*it) |
✅ 可用 |
隐式转换 pair<const int, int> 为 pair<int, int>。 |
pri_que.push({it->first, it->second}) |
✅ 推荐 |
显式构造 pair<int, int>,明确类型转换,避免潜在问题。 |
auto it = map.begin()
it的类型是std::unordered_map::iterator
| 特性 |
迭代器 (std::unordered_map::iterator) |
指针 (T*) |
| 本质 |
类对象(可能包含复杂逻辑) |
内存地址的数值 |
解引用 *it |
返回 std::pair<const int, int>& |
返回指向的对象 |
成员访问 it-> |
访问键值对的成员(如 it->first) |
访问结构体/类的成员 |
| 失效风险 |
容器修改(如插入/删除)可能导致迭代器失效 |
仅当内存释放后失效 |
| 算术运算 |
不支持 it + 1(无序容器) |
支持指针算术(如 ptr + 1) |
关于用->second访问还是用.second来访问
,如果是引用,则用->,是成员的话用.
双端队列常用函数deque
| 函数 |
作用 |
时间复杂度 |
front() |
返回首元素引用 |
O(1) |
back() |
返回尾元素引用 |
O(1) |
operator[] |
通过下标访问元素(无边界检查) |
O(1) |
at(index) |
通过下标访问元素(有边界检查) |
O(1) |
push_front(val) |
头部插入元素 |
O(1) |
push_back(val) |
尾部插入元素 |
O(1) |
pop_front() |
删除头部元素 |
O(1) |
pop_back() |
删除尾部元素 |
O(1) |
insert(pos, val) |
在指定位置插入元素 |
O(n) |
erase(pos) |
删除指定位置元素 |
O(n) |
size() |
返回当前元素数量 |
empty() |
判断是否为空 |
resize(n) |
调整容器大小 |
clear() |
清空所有元素 |
二叉树
堆是一个完全二叉树
二叉树的存储方式:
链式存储:指针
顺序存储:数组
数组:2i+1是左子,2i+2是右子
二叉树的节点定义
1 2 3 4 5 6 7 8 9
| struct TreeNode{ int val; TreeNode* left; TreeNode* right; TreeNode(int a):val(a),left(NULL),right(NULL){} }
TreeNode* newtree = new TreeNode(1);
|
二叉树真勾八抽象呀,老子今天下午一定给你啃下来
递归遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
void traversal(TreeNode * cur,vector<int>& vec){ if(cur==NULL)return; vec.push_back(cur->val); traversal(cur->left,vec); traversal(cur->right,vec); }
void traversal(TreeNode * cur,vector<int>& vec){ if(cur==NULL)return; traversal(cur->left,vec); vec.push_back(cur->val); traversal(cur->right,vec); }
void traversal(TreeNode * cur,vector<int>& vec){ if(cur==NULL)return; traversal(cur->left,vec); traversal(cur->right,vec); vec.push_back(cur->val); }
|
二叉树递归遍历(三种顺序:中左右,左中右,左右中)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| void traversal(TreeNode* cur,vector<int>& vec) { if (cur == NULL) return; vec.push_back(cur->val); traversal(cur->left, vec); traversal(cur->right, vec); }
void traversal(TreeNode* cur, vector<int>& vec) { if (cur == NULL) return; traversal(cur->left, vec); vec.push_back(cur->val); traversal(cur->right, vec); }
void traversal(TreeNode* cur, vector<int>& vec) { if (cur == NULL) return; traversal(cur->left, vec); traversal(cur->right, vec); vec.push_back(cur->val); }
|
二叉树的迭代遍历
递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
|
vector<int> pretraversal(TreeNode* root){ stack<TreeNode*>st; vector<int>result; TreeNode* cur = root; st.push(root) while(!st.empty()) { cur = st.top(); st.pop(); result.push_back(cur->val) if(cur->right!=NULL)st.push(cur->right); if(cur->left!=NULL)st.push(cur->left); } return result }
vector<int> midtraversal(TreeNode* root){ stack<TreeNode*>st; vector<int>result; TreeNode* cur = root; while(!st.empty()||cur!=NULL) { if(cur!=NULL) { st.push(cur); cur = cur->left; }else{ cur = st.top(); result.push_back(cur->val); st.pop(); cur=cur->right; } } return result; }
vector<int> pastraversal(TreeNode* root){ stack<TreeNode*>st; vector<int>result; TreeNode* cur = root; st.push(root) while(!st.empty()) { cur = st.top(); st.pop(); result.push_back(cur->val) if(cur->left!=NULL)st.push(cur->left); if(cur->right!=NULL)st.push(cur->right); } reverse(result.begin(),result.end()); return result; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
vector<int> preordertraversal(TreeNode* root){ vector<int>result;
if(cur==NULL)return; stack<TreeNode*>st; st.push(cur); while(!st.empty()) { TreeNode* cur = st.top(); st.pop(); result.push_back(cur->val); if(cur->right!=NULL)st.push(cur->right); if(cur->left!=NULL)st.push(cur->left); } return result }
vector<int> inordertraversal(TreeNode* root){ vector<int>result; TreeNode* cur = root; if(cur==NULL)return; stack<TreeNode*>st; while(!st.empty()||cur!=NULL) { if(cur!=NULL) { st.push(cur); cur = cur->left; }else{ cur = st.top(); st.pop(); result.push_back(cur->val); cur = cur->right; } } }
vector<int> postordertraversal(TreeNode* root){ vector<int>result; TreeNode* cur = root; if(cur==NULL)return; stack<TreeNode*>st; st.push(cur); while(!st.empty()) { cur = st.pop(); result.push_back(cur->val); if(cur->left!=NULL)st.push(cur->left); if(cur->right!=NULL)st.push(cur->right); } reverse(result.begin(), result.end()); return result }
|
迭代统一遍历
无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况
两种标记法
空指针标记法

中序遍历
空指针标记法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| vector<int> midtraversal(TreeNode* root){ vector<int>result; TreeNode* cur= root; stack<TreeNode*>st; if (root != NULL) st.push(root); while(!st.empty()) { cur = st.top(); st.pop(); if(cur!=NULL) { if(cur->right)st.push(cur->right); st.push(cur); st.push(NULL); if(cur->left)st.push(cur->left); }else{ cur = st.top(); result.push_back(cur->val); st.pop(); } } return result; }
|
boolean标记法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| vector<int> midtraversal(TreeNode* root){ vector<int>result; stack<pair<TreeNode*,bool>>st; if(root!=nullptr)st.push(make_pair(root,false)); while(!st.empty()) { auto curnode = st.top().first; auto visited = st.top().second; st.pop(); if(visited) { result.push_back(curnode->val); continue; } if(curnode->right)st.push(make_pair(curnode->right,false)); st.push(make_pair(curnodet,true)); if(curnode->left)st.push(make_pair(curnode->left,false)); } } return result; }
|
前序遍历
空指针标记法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| vector<int> pretraversal(TreeNode* root){ vector<int>result; TreeNode* cur= root; stack<TreeNode*>st; if (root != NULL) st.push(root); while(!st.empty()) { cur = st.top(); if(cur!=NULL) { st.pop(); if(cur->right)st.push(cur->right); if(cur->left)st.push(cur->left); st.push(cur); st.push(NULL); }else{ st.pop(); cur = st.top(); result.push_back(cur->val); st.pop(); } } return result; }
|
boolean标记法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| vector<int> pretraversal(TreeNode* root){ vector<int>result; stack<pair<TreeNode*,bool>>st; if(root!=nullptr)st.push(make_pair(root,false)); while(!st.empty()) { auto curnode = st.top().first; auto visited = st.top().second; st.pop(); if(visited) { result.push_back(curnode->val); continue; } if(curnode->right)st.push(make_pair(curnode->right,false)); if(curnode->left)st.push(make_pair(curnode->left,false)); st.push(make_pair(curnodet,true)); } } return result; }
|
后序遍历
空指针标记法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| vector<int> pastraversal(TreeNode* root){ vector<int>result; TreeNode* cur= root; stack<TreeNode*>st; if (root != NULL) st.push(root); while(!st.empty()) { cur = st.top(); if(cur!=NULL) { st.pop(); st.push(cur); st.push(NULL); if(cur->right)st.push(cur->right); if(cur->left)st.push(cur->left); }else{ st.pop(); cur = st.top(); result.push_back(cur->val); st.pop(); } } return result; }
|
boolean标记法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| vector<int> pastraversal(TreeNode* root){ vector<int>result; stack<pair<TreeNode*,bool>>st; if(root!=nullptr)st.push(make_pair(root,false)); while(!st.empty()) { auto curnode = st.top().first; auto visited = st.top().second; st.pop(); if(visited) { result.push_back(curnode->val); continue; } st.push(make_pair(curnodet,true)); if(curnode->right)st.push(make_pair(curnode->right,false)); if(curnode->left)st.push(make_pair(curnode->left,false)); } } return result; }
|
层序遍历
广度优先遍历/层序遍历(迭代法和递归法)(正向)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| void order(TreeNode* cur, vector<vector<int>>& result, int depth) { if (cur == nullptr) return; if (result.size() == depth) result.push_back(vector<int>()); result[depth].push_back(cur->val); order(cur->left, result, depth + 1); order(cur->right, result, depth + 1); } vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> result; int depth = 0; order(root, result, depth); return result; }
vector<vector<int>> levelOrder(TreeNode* root) { queue<TreeNode*> que; if (root != NULL) que.push(root); vector<vector<int>> result; while (!que.empty()) { int size = que.size(); vector<int> vec; for (int i = 0; i < size; i++) { TreeNode* node = que.front(); que.pop(); vec.push_back(node->val); if (node->left) que.push(node->left); if (node->right) que.push(node->right); } result.push_back(vec); } return result; }
|
反向层序遍历(迭代法)(正向遍历reverse一下就行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| vector<vector<int>> levelOrder(TreeNode* root) { queue<TreeNode*> que; if (root != NULL) que.push(root); vector<vector<int>> result; while (!que.empty()) { int size = que.size(); vector<int> vec; for (int i = 0; i < size; i++) { TreeNode* node = que.front(); que.pop(); vec.push_back(node->val); if (node->left) que.push(node->left); if (node->right) que.push(node->right); } result.push_back(vec); } reverse(result.begin(),result.end()); return result; }
|
对称二叉树的判断
递归
层序遍历
队列
栈
二叉树的高度与深度
后续遍历 从下到上求高度
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Solution { public: int getdepth(TreeNode* node) { if (node == NULL) return 0; int leftdepth = getdepth(node->left); int rightdepth = getdepth(node->right); int depth = 1 + max(leftdepth, rightdepth); return depth; } int maxDepth(TreeNode* root) { return getdepth(root); } };
|
前序遍历 从上到下求深度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Solution { public: int result; void getdepth(TreeNode* node, int depth) { result = depth > result ? depth : result;
if (node->left == NULL && node->right == NULL) return ;
if (node->left) { depth++; getdepth(node->left, depth); depth--; } if (node->right) { depth++; getdepth(node->right, depth); depth--; } return ; } int maxDepth(TreeNode* root) { result = 0; if (root == NULL) return result; getdepth(root, 1); return result; } };
|
迭代法 层序遍历
隐藏回溯:if (cur->left) traversal(cur->left, path + “->”, result); // 左 回溯就隐藏在这里
那么在如上代码中,貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在traversal(cur->left, path + "->", result);中的 path + "->"。 每次函数调用完,path依然是没有加上”->” 的,这就是回溯了。
map排序
sort cmp给的是前两个(或者相邻两个(从前往后))应满足的关系
map先转化成数组
1 2 3 4 5 6
| bool static cmp (const pair<int, int>& a, const pair<int, int>& b) { return a.second > b.second; }
vector<pair<int, int>> vec(map.begin(), map.end()); sort(vec.begin(), vec.end(), cmp);
|
二叉搜索树
找众数,即出现最多次,可以用
1 2 3 4 5 6
| if (count > maxCount) { maxCount = count; result.clear(); result.push_back(cur->val); }
|
遍历一条边和搜索整个数的区别?
找到了就一路返回
二叉搜索树是从上到下找公共祖先
二叉搜索树可以尝试用迭代法,因为只有一条路径,不需要回溯
回溯
横纵方向,横向集合大小for循环处理,纵向递归处理树的深度

在递归的过程中增加闸门 backtracking
五大类问题
组合 切割 子集 排列 棋盘
组合问题
回溯三部曲
回溯模板
1 2 3 4 5 6 7 8 9 10 11 12
| void backtracking(参数) { if (终止条件) { 存放结果; return; }
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) { 处理节点; backtracking(路径,选择列表); 回溯,撤销处理结果 } }
|
组合问题剪枝
1
| n-(k-path.size())+1来代替n;可以用n=4,k=3来举例子,当.size()=0的时候,只需要遍历1,234都被剪掉了 这里用<=要方便一些
|
由于无法控制嵌套的for循环层数,所以要用回溯算法。回溯算法抽象成一个n叉树
组合问题(取k个数控制树的深度),for循环的范围是集合宽度
组合剪枝,条件剪枝和组合个数剪枝
输出细节
1
| [""]不合法,需要转化成[],可以用.clear()
|
private中不需要传参也可以用
1 2 3
| backtracking(candidates, target, i);
backtracking(candidates, target, i + 1);
|
1 2 3 4 5 6 7 8 9 10 11 12 13
|
find(result.begin(), result.end(), path) != result.end()
vector<int> digits = {}
set转数组 unordered_set<int> set = {5, 2, 8, 1} vector<int> vec(set.begin(), set.end()); set添加元素 unordered_set<int, int> people; people.emplace(1);
|
分割问题
可以把判断放在单层搜索逻辑中
字符串操作:’’是char “”是string
在某点插入某个符号 s.insert(s.begin() + i + 1 , ‘.’);
字符串删除:注意
s.erase(s.begin() + i + 1); 这里s.begin()是迭代器,所以删除的是那一位置的元素
s.erase(1); 这里删除的是第一个索引包括之后的所有元素
子集问题
直接加进去就行
排列问题
采用used数组,used数组用来去重或者排列
最主要的是去重
数层去重和树枝去重:组合总和2视频

棋盘问题
N皇后 字符串初始化
1 2 3
| int n = 4; vector<string> board(n, string(n, '.'));
|
数独
二维递归
贪心算法
动态规划
默写
二叉树递归遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void pretraversal(TreeNode* cur,vector<int>&vec){ if(cur==NULL)return; vec.push_back(cur->val); pretraversal(cur->left,vec); pretraversal(cur->right,vec); } void midtraversal(TreeNode* cur,vector<int>&vec){ if(cur==NULL)return; pretraversal(cur->left,vec); vec.push_back(cur->val); pretraversal(cur->right,vec); } void pastraversal(TreeNode* cur,vector<int>&vec){ if(cur==NULL)return; pretraversal(cur->left,vec); pretraversal(cur->right,vec); vec.push_back(cur->val); }
|
二叉树的统一迭代遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| vector<int>itertraversal(TreeNode* root){ vector<int>result; stack<pair<TreeNode*,bool>>st; if(root!=nullptr)st.push(make_pair(root,false)); while(!st.empty()) { auto curnode = st.top().first; auto visited = st.top().second; st.pop(); if(visited){ result.push_back(curnode->val); }else{ if(curnode->right)st.push((make_pair(curnode->right,false)); if(curnode->left)st.push((make_pair(curnode->left,false)); st.push(make_pair(curnode,true)); } } return result; }
|
二叉树的迭代层序遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| vector<vector<int>>leveltraversal((TreeNode* root){ vector<vector<int>>result; queue<int>que; if(root!=nullptr)que.push(root) while(!que.empty()) { vector<int>vec; int len = que.size(); for(int i = 0;i<len;i++) { TreeNode* cur = que.front(); que.pop(); vec.push_back(cur->val); if(cur->left)que.push(cur->left); if(cur->right)que.push(cur->right); } result.push_back(vec); } return result; }
|