二叉树遍历
void inorder(TreeNode* root, vector<int>& ans) {
if(!root) return;
inorder(root->left, ans);
ans.push_back(root->val);//处理root节点,可换为其他代码
inorder(root->right, ans);
}
void preorder(TreeNode* root, vector<int>& ans) {
if(!root) return;
ans.push_back(root->val);//处理root节点,可换为其他代码
preorder(root->left, ans);
preorder(root->right, ans);
}
void postorder(TreeNode* root, vector<int>& ans) {
if(!root) return;
postorder(root->left, ans);
postorder(root->right, ans);
ans.push_back(root->val);//处理root节点,可换为其他代码
}
//level traversal
vector<vector<int>> levelorder(TreeNode* root, vector<int>& ans){
if(!root) return {};
vector<vector<int>> res;
queue<TreeNode*> q = {{root}};
while(!q.empty()){
vector<int> level;
for(int i=q.size(); i > 0; --i){
TreeNode* temp = q.front();
q.pop();
level.push_back(temp->val);
if(temp->left) q.push_back(temp->left);
if(temp->right) q.push_back(temp->right);
}
res.push_back(level);
}
return res;
}
并查集
并查集使用一种树形的数据结构,处理例如:两个人是否认识,两个地点能否通过一些路径连通,等抽象为连通性的问题。
核心API:union, find, connected
注:并查集只能回答连通与否,并不能找到连通路径。
典型题目:力扣323, 399. 除法求值
典型应用:
- 检查图中是否有环(将边进行合并,在合并之前判断是否已经连通,如果在合并之前就已经连通则说明图中存在环)
- c++代码
//带路径压缩的代码模板
class UnionFind{
vector<int> parent;
vector<int> size;
int n;
int part;
UnionFind(int n){
this->n = n;
part = n;
parent.resize(n);
size.resize(n,1);
for(int x=0; x<n; x++){
parent[x] = x;
}
}
//利用递归实现,每次查找之后都对find过程进行了路径的压缩
int find(int x){
if(x != parent[x]){
parent[x] = find(parent[x]);
return parent[x];
}
return x;
}
bool connected(int x, int y){
return find(x) == find(y);
}
void Union_part(int x, int y){
//给定两个点,将两个点所在的根连起来(小的放到大的里面去)
int root_x = find(x);
int root_y = find(y);
if(root_x == root_y) return;
if(size[root_x] >= size[root_y]){
parent[root_y] = root_x;
size[root_x] += size[root_y];
part -= 1;
}
else{
parent[root_x] = root_y;
size[root_y] += size[root_x];
part -= 1;
}
}
int get_part_size(int x){
int root = find(x);
return size[root];
}
};
- python代码
//带有路径压缩的代码模板
class UF:
def __init__(self, M):
self.parent = {}
self.size = {}
self.cnt = 0
# 初始化 parent,size 和 cnt
for i in range(M):
self.parent[i] = i
self.cnt += 1
self.size[i] = 1
def find(self, x):
if x != self.parent[x]:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
return x
def union(self, p, q):
if self.connected(p, q): return
# 小的树挂到大的树上, 使树尽量平衡
leader_p = self.find(p)
leader_q = self.find(q)
if self.size[leader_p] < self.size[leader_q]:
self.parent[leader_p] = leader_q
self.size[leader_q] += self.size[leader_p]
else:
self.parent[leader_q] = leader_p
self.size[leader_p] += self.size[leader_q]
self.cnt -= 1
def connected(self, p, q):
return self.find(p) == self.find(q)
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
- 全新的界面设计 ,将会带来全新的写作体验;
- 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
- 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
- 全新的 KaTeX数学公式 语法;
- 增加了支持甘特图的mermaid语法1 功能;
- 增加了 多屏幕编辑 Markdown文章功能;
- 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
- 增加了 检查列表 功能。
DFS
从root节点开始,尽可能的搜索每一个分支(一个分支搜完搜下一个)
典型题目:走迷宫
void dfs(int step)
{
判断边界
{
相应操作
}
尝试每一种可能
{
满足check条件
标记
继续下一步dfs(step+1)
恢复初始状态(回溯的时候要用到)
}
}
撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G
二分法
- [l, r)
def binary_search(l, r):
while l < r:
m = l + (r - l) // 2
if f(m): return m # optioncal
if g(m)
r = m
else
l = m + 1
return l # 最小的,满足 g(m)条件的索引
- [l, r]
def binary_search(l, r):
while l <= r:
m = l + (r - l) // 2
if f(m): return m # optioncal
if g(m)
r = m - 1
else
l = m + 1
return l # 最小的,满足 g(m)条件的索引
- 有序数列,数字出现次数不唯一
int find(vector<int>& nums, int begin, int end, int k){
while(begin < end){
int mid = (begin + end)/2;
if(nums[mid]>=k){
end = mid;
}
else{
begin = mid+1;
}
}
if(begin == end && nums[begin]!=k) return -1;
return begin;
}
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC
语法后生成一个完美的目录。
背包问题(动态规划)
具体可参见《背包九讲》
0-1背包
N件物品,容量为V的背包,c[i]表示费用,w[i]表示重量,总重量不超且总价值最大。每个物品仅有一件,可以选择放/不放
-
恰好装满
初始化:dp[0][0]=0, dp[0][j]=INT_MIN
状态:f[i][j]表示容量为j,放前i件物品的最大价值。
状态转移:f[i][j] = max(f[i-1][j],f[i-1][j-c[i]]+w[i])
结果:f[N][0…V]的最大值 -
只要小于就可以
初始化:if j>c[0], f[0][j] = w[0]
状态:f[i][j]表示容量为j,放前i件物品的最大价值。
状态转移:f[i][j] = max(f[i-1][j],f[i-1][j-c[i]]+w[i])
结果:f[N][0…V]的最大值
// 初始化时倒叙遍历
for (int j = bagWeight; j >= weight[0]; j--) {
dp[0][j] = dp[0][j - weight[0]] + value[0]; // 初始化i为0时候的情况
}
0-1背包,背包容量要从大到小遍历
// 核心代码
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
完全背包
每个物品无限件可用
状态:f[i][j]表示容量为j,放前i件物品的最大价值。
状态转移:f[i][j] = max(f[i-1][j],f[i-1][j-c[i]]+w[i])
结果:f[N][0…V]的最大值
完全背包,背包容量可以从小到大遍历
// 先遍历物品,再遍历背包
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = weight[i]; j < bagWeight ; j++) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
多重背包
第i件物品最多有Mi件可用
将有多件的物品拆成Mi个,题目变为0-1背包问题。
// 多件的物品拆成Mi个
for (int i = 0; i < nums.size(); i++) {
while (nums[i] > 1) { // nums[i]保留到1,把其他物品都展开
weight.push_back(weight[i]);
value.push_back(value[i]);
nums[i]--;
}
}
// 核心代码
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}