基础算法
-
快速排序
时间复杂度 O(nlogn) ~ O(n^2)
分治的思想 → 划分 求解 合并
快排不稳定,如果想稳定可以用pair 加上下标 双关键字排序 (保证元素不重复)
具体步骤:
-
确定分界点 q[l] q[(l+r)/2] q[r]
-
调整区间 > x <
-
递归处理左右两端
void quick_sort(int q[], int l , int r) { if(l >= r) return; int i = l - 1, j = r + 1, x = q[l + r >> 1]; while(i < j) { do ++i; while(q[i] < x); do --j; while(q[j] > x); if(i < j) swap(q[i], q[j]); } quick_sort(q, l, j); quick_sort(q, j + 1, r); } -
-
归并排序
时间复杂度 O(nlogn)
分治的思想
具体步骤:
- 确定分界点 mid = (l + r) / 2
- 递归排序
- 归并 — 合二为一
int t[N] void merge_sort(int q[], int l, int r) { if(l >= r) return; int mid = l + r >> 1; merge_sort(q, l, mid); merge_sort(q, mid + 1, r); int i = l , j = mid + 1, k = 0; while(i <= mid && j <= r) { if(q[i] < q[j]) t[k++] = q[i++]; else t[k++] = q[j++]; } while(i <= mid) t[k++] = q[i++]; while(j <= r) t[k++] = q[j++]; for(int i = l, k = 0; i <= r; i ++, k ++) q[i] = t[k]; } -
二分
二分的本质不是单调性, 而是边界,左半边满足某种性质,而右半边不满足
单调一定可以二分,但是能二分不一定单调
步骤 :
- 选一个mid mid = (l+r)/ 2
- check一个性质
- 确定要找的结果所在的区间
// 二分模板 // 整数模板 int bsearch_1(int l, int r) { while(l < r) { int mid = l + r >> 1; //mid 向下取整 if(check(mid)) r = mid; //检查mid是否满足某种性质 else l = mid + 1; } return l; } int bsearch_2(int l, int r) { while(l < r) { int mid = l + r + 1 >> 1; // mid 向上取整 if(check(mid)) l = mid; //检查mid是否满足某种性质 else r = mid - 1; } return l; } bool check(double x) {/* ... */} // 检查x是否满足某种性质 // 浮点数二分模板 double bsearch_3(double l, double r) { const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求 while (r - l > eps) { double mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid; } return l; } -
高精度
步骤
- 大整数存储 低位在左
- 模拟人工算的过程
// 高精度加法 A + B vector<int> add(vector<int> &A, vector<int> &B) { vector<int> C; int t = 0; for(int i = 0; i < A.size() || i < B.size(); i ++) { if(i < A.size()) t += A[i]; if(i < B.size()) t += B[i]; C.push_back(t % 10); t /= 10; } if(t) C.push_back(t); return C; } // 高精度减法 A - B // 假定 A >= B // assume A >= B // C = A - B vector<int> sub(vector<int> &A, vector<int> &B) { vector<int> C; int t = 0; for(int i = 0; i < A.size(); i ++) { t = A[i] - t; if(i < B.size()) t -= B[i]; C.push_back((t + 10) % 10); t = (t < 0 ? 1 : 0); } // 去除前导0 while(C.size() > 1 && C.back() == 0) C.pop_back(); return C; } // 高精度乘法 // A * a 大数 ✖ 小数 vector<int> mul(vector<int> &A, int b) { vector<int> C; int t = 0; for(int i = 0; i < A.size() || t; i ++) { if(i < A.size()) t += A[i] * b; C.push_back(t % 10); t /= 10; } // 去除前导0 while(C.size() > 1 && C.back() == 0) C.pop_back(); return C; } //高精度除法 求商 和 余数 // A / b 大数 / 小数 vector<int> div(vector<int> &A, int b, int &r) { vector<int> C; r = 0; for(int i = A.size() - 1; i >= 0; i --) { r = ((r * 10) + A[i]); C.push_back(r / b); r = r % b; } reverse(C.begin(), C.end()); while(C.size() > 1 && C.back() == 0) C.pop_back(); return C; } -
前缀和与差分
前缀和:
一维前缀和:
S[i] = a[1] + a[2] + … a[i] a[l] + … + a[r] = S[r] - S[l - 1]
二维前缀和:
S[i, j] = 第i行j列格子左上部分所有元素的和 以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为: S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]
差分:
一维差分:
给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c
二维差分:
给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c: S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c
前缀和与差分互为逆运算
-
双指针算法
for (int i = 0, j = 0; i < n; i ++ ) { while (j < i && check(i, j)) j ++ ; // 具体问题的逻辑 } 常见问题分类: (1) 对于一个序列,用两个指针维护一段区间 (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作 -
位运算
n的二进制表示中,第K位是几: n >> k & 1
步骤:
- 先把第K位移到最后一位 n >> k
- 看个位是几 与上1 x & 1
返回n的最后一位1:
lowbit(n) = n & -n
eg: x = 1010 lowbit(x) = 10
x = 11100 lowbit(x) = 100
-
离散化
主要问题:
- 去重:可能有重复元素 (set, unique)
- 如何快速找出映射的值(map, 二分查找→有序映射)
vector<int> alls; // 存储所有待离散化的值 sort(alls.begin(), alls.end()); // 将所有值排序 alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去掉重复元素 // 二分求出x对应的离散化的值 int find(int x) // 找到第一个大于等于x的位置 { int l = 0, r = alls.size() - 1; while (l < r) { int mid = l + r >> 1; if (alls[mid] >= x) r = mid; else l = mid + 1; } return r + 1; // 映射到1, 2, ...n } -
区间合并
左端点排序,比较右端点,判断交集
// 将所有存在交集的区间合并 void merge(vector<PII> &segs) { vector<PII> res; sort(segs.begin(), segs.end()); int st = -2e9, ed = -2e9; for (auto seg : segs) if (ed < seg.first) { if (st != -2e9) res.push_back({st, ed}); st = seg.first, ed = seg.second; } else ed = max(ed, seg.second); if (st != -2e9) res.push_back({st, ed}); segs = res; }