常见算法用法及模板总结
算法
排序
排序大部分是一个题的子部分,使其有序,便于其他操作。
- partition ,用于找出第k个元素,使k的左右部分大小分明
int partition(vector<int> &nums, int l, int h) {
int i = l, j = h + 1;
while (true) {
while (nums[++i] < nums[l] && i < h) ;
while (nums[--j] > nums[l] && j > l) ;
if (i >= j) {
break;
}
swap(nums, i, j);
}
swap(nums, l, j); //注意是j
return j;
}
随机版本
int rp(vector<int> &nums, int l, int h) {
srand(time(0));
int r = rand() % (h - l) + l;
swap(nums[v],nums[l]);
return partition(nums,l,h);
}
- 桶排序,处理频率相关
Two Pointer
主要用于遍历数组,指针移动条件明确
贪心
- 从局部最优可以推到全局最优
- 区间贪心。
- 对于边界不好处理的可以加上边界。
搜索: 1.二分查找
- 其实也属于搜索题,每次可以根据条件劈掉一半,即可使用。最常用有序数组查找、BST、要求第一个大于、小于、大于等于、小于等于元素。
- binarySearch
while (l < r) {
int m = l + (r - l) / 2;
if (true)
r = m;
else
l = m + 1;
}
return l;
int lower_bound(int A[], int l, int r, int x) {
while (l < r) {
int m = l + (r - l) / 2;
if (A[m] >= x)
r = m;
else
l = m + 1;
}
return l;
}
int upper_bound(int A[], int l, int r, int x) {
while (l < r) {
int m = l + (r - l) / 2;
if (A[m] > x)
r = m;
else
l = m + 1;
}
return l;
}
搜索: 2.DFS
- 用于穷举每种情况,递归下去,大规模数据容易爆栈。
以dfs + 回溯为例
for (int i = 0; i < vsize; i++) {
if (true)//满足搜索要求,如标记为1
dfs();
}
void dfs() {// 返回值视需要而定
if (true) //满足搜索值要求,如到达目的地,到达规定步数step等,做相应处理
marked[i] = true; //标记准备进入下一层dfs,有时可以在原grid标记,视具体情况
for (auto d : direction) //枚举下次dfs可能走向
dfs(); //下层
marked[i] = false; //回溯
}
搜索: 3.BFS
- 常用如,最短路径,floodfill
queue<Typename> q;
q.push();
while (!q.emtpy()) {
int size = q.size();
while (size--) {
Typename t = q.front();
处理所得值,如标记,路径加1
判断t是否有下一层次
q.push();
q.pop();
}
}
递归
- 可以划分子问题
DP
- 可以通过上一状态得到现在状态结果,即可使用
- 背包问题
- 01背包问题, 有N件物品,容量V,放入第i件物品费用
C
i
C_i
Ci,价值
W
i
W_i
Wi
状态方程: d p [ i ] [ v ] = m a x ( d p [ i − 1 ] [ v ] , d p [ i − 1 ] [ v − c i ] + w i ) dp[i][v] = max(dp[i - 1][v], dp[i - 1][v - c_i] + w_i) dp[i][v]=max(dp[i−1][v],dp[i−1][v−ci]+wi)
初始化细节:不要求装满时,dp[0 ~ n]可以均取0,如果要求恰好装满dp[0]为0其他为-INF
还有个常数项优化
模板:
c[i]费用, w[i]价值
for (int i = 1; i <= n; i++) {
for (int j = v; j >= c[i]; j--)
dp[j] = max(dp[j], dp[j - c[i]] + w[i])
}
- 完全背包
即每种物品无限使用
状态方程: d p [ i ] [ v ] = m a x ( d p [ i − 1 ] [ v − k ∗ c i ] + k ∗ w i ) dp[i][v] = max(dp[i - 1][v - k * c_i] + k * w_i) dp[i][v]=max(dp[i−1][v−k∗ci]+k∗wi)
简单优化:如果有i,j满足 C i < = C j C_i <= C_j Ci<=Cj且 W i > = W j W_i >= W_j Wi>=Wj,可以将j直接省去
模板:
for (int i = 1; i <= n; i++) {
for (int j = c[i]; j <= v; j++)
dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
}
- 多重背包
设第i件最多 M i M_i Mi件可用。
将 M i M_i Mi件拆成独立的每一件,这就变成了01背包
优化:利用二进制思想,将第i种物品分成若干件01背包中的物品,其中每件物品有一个系数。这件物品的费用和价值均是原来的费用和价值乘以这个系数。令这些系数分别为 1 , 2 , 2 2 , . . . , 2 ( k − 1 ) , M i − 2 k + 1 1, 2, 2^2, ..., 2^{(k - 1)}, M_i - 2^k + 1 1,2,22,...,2(k−1),Mi−2k+1,且k是满足 M i − 2 k + 1 > 0 M_i - 2^k + 1 > 0 Mi−2k+1>0的最大整数。
模板:
for (int i = 1; i <= n; i++) {
for (int j = v; j >= c[i]; j++) {
for (int k = 1; k <= m[i] && k * w[i] <= j; k++)
dp[j] = max(dp[j], dp[j - k * c[i]] + k * w[i]);
}
}
//二进制优化:
// 先处理物品
vector<pair<int, int>> goods;
for (int i = 1; i <= n ; i++) {
for (int k = 1; k <= m[i]; k *= 2) {
m[i] -= k;
goods.push_back(k * c[i], k * w[i]);
}
if (m[i] > 0)
goods.push_ back(m[i] * c[i], m[i] * w[i]);
}
for (auto p : goods) {
for (int j = v; j >= p.first; j++)
dp[j] = max(dp[j], dp[j - p.first] + p.second);
}
究极优化:单调队列
我不会。。。
-
混合背包
分情况处理即可 -
二维费用背包
最大重量M,m[i]
加一重循环即可
for (int i = 1; i <= n; i++) {
for (int j = v; j >= c[i]; j--) {
for (int k = M; k >= m[i]; k--)
dp[j][k] = max(dp[j][k], dp[j - c[i][k - m[i]] + w[i])
}
}
- 分组背包
有n组,每组最多只能选一个。每组个数s[i]
for (int i = 1; i <= n; i++) {
for (int j = v; j >= 0; j--) {
for (int k = 1; k <= s[i]; k++) {
if (j >= c[i][k])
dp[j] = max(dp[j], dp[j - c[i][k]] + w[i][k]);
}
}
}
- 依赖背包
- 方案数
初始状态需要为-INF,体积0的方案数为1
int f[N + 1], g[N +1];
int main () {
g[0] = 1;
for (int i = 1; i < N; i++) f[i] = -INF;
for (int i = 1; i<= n; i++) {
for(int j = v; j >= c[i]; j--) {
int t = max(f[j], f[j - c[i]] + w[i]);
int s = 0;
if (t == f[j]) s += g[j];
if (t == f[j - c[i]] + w[i]) s += g[j - c[i]];
f[j] = t;
g[j] = s;
}
}
int maxw = 0;
int re = 0;
for (int i = 1; i <= N; i++) maxw = max(maxw, f[i]);
for (int i = 1; i <= N; i++) {
if (maxw == f[i]) {
re += g[i];
}
}
}
- 输出方案
用最原始dp解
如果dp[i - 1][m] == dp[i][m]说明没有使用物品i
数学
- 素数
bool isPrime(int n) {
if (n <= 1) return false;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0)
return false;
}
return true;
}
- GCD
int gcd (int a,int b) {
return b == 0 ? a : gcb(b , a % b);
}
- 大数计算
- 位操作
n&(-n) 得到 n 的位级表示中最低的那一位。-n 得到 n 的反码加 1,对于二进制表示 10110100,-n 得到 01001100,相与得到 00000100
n & (n - 1)操作,可以去除最低位的1
数据结构
链表
- 注意判所指是否为空
树
- 递归
- BST,中序遍历为有序
- 前中后序遍历,非递归实现
TreeNode *preOrder(TreeNode *root) {
stack<TreeNode *>s;
s.push(root);
while (!s.empty()) {
TreeNode *temp = s.top();
s.pop();
if (temp == nullptr) continue;
//相应操作,如printf
s.push(root->right);
s.push(root->left);
}
}
TreeNode *postOrder(TreeNode *root) {
stack<TreeNode *> s;
s.push(root);
while (!s.empty()) {
TreeNode *temp = s.top();
if (temp == nullptr) continue;
//存值v
s.push(root->right);
s.push(root->left);
}
//reverse v
}
TreeNode *inOrder(TreeNode *root) {
TreeNode *cur = root;
stack<TreeNode *>s;
while (cur || !s.empty()) {
while (cur) {
s.push(cur);
cur = cur->left;
}
TreeNode *temp = s.top();
s.pop();
cur = temp->right;
}
}
- 层次遍历,即BFS
- 前缀树
- 线段树
栈
- 用于匹配算法,括号匹配,先进先出
队
Hash
- 映射关系
字符串
数组
- 元素交换
- 双指针
图 基本都是模板题
- 二分图
染色
int color[101] = {0};
bool isBipartite(vector<vector<int>>& graph) {
for (int i = 0; i < graph.size(); i++) {
if (color[i] == 0 && !dfs(i, 1, graph)) //如果染色且相邻颜色相同即false
return false;
}
return true;
}
bool dfs(int cur, int c,vector<vector<int>>& graph) {
if (color[cur] != 0) return color[cur] == c;
color[cur] = c;
for (int n : graph[cur]) {
if (!dfs(n, -c, graph))
return false;
}
return true;
}
- 拓扑排序
vector<vector<int>> edge(numCourses); //边
vector<int> in(numCourses); // 入度
for (auto p : prerequisites) {
edge[p[1]].push_back(p[0]);
}
for (int i = 0; i < numCourses; i++) {
for (auto n : edge[i])
in[n]++;
}
queue<int> q;
vector<int> re;
for (int i = 0; i < numCourses; i++) { //入度为0即可操作
if (in[i] == 0) {
re.push_back(i);
q.push(i);
}
}
while (!q.empty()) {
int temp = q.front();
q.pop();
for (int n : edge[temp]) {
in[n]--;
if (in[n] == 0) {
q.push(n);
re.push_back(n);
}
}
}
- 并查集
void union(int a,int b) {
int x = find(a);
int y = find(b);
if (x == y)
return ;
p[y] = x;
}
int find(int x) {
return x == p[x]? x : find(p[x]);
}