算法基础课模板(一)

一:基础算法

1.快排

参考模板: https://www.acwing.com/blog/content/4747/。

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
// 快排(从小到大),模板题:acwing.785
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]);
}
// while循环结束后,q[l..j] <= x,q[j+1..r] >= x
quick_sort(q,l,j),quick_sort(q,j+1,r);// 注意:这个模板必须以j为划分点,不能取i
}
-------
// 快排(从大到小)
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]);
}
// while循环结束后,q[l..j] <= x,q[j+1..r] >= x
quick_sort(q,l,j),quick_sort(q,j+1,r);// 注意:这个模板必须以j为划分点,不能取i
}

2.归并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 归并(从小到大),模板题:acwing.787
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);// 以下标中点划分递归
// 对两个有序数组进行排序,需要临时数组tmp
int k = 0,i = l,j = mid+1;
while (i <= mid && j <= r){
if (q[i] <= q[j]) tmp[k++] = q[i++];
else tmp[k++] = q[j++];
}
while (i <= mid) tmp[k++] = q[i++];
while (j <= r) tmp[k++] = q[j++];

for (int i = l,j = 0;i <= r;i++,j++) q[i] = tmp[j];// 将tmp拷贝回q
}

3.整数与浮点数二分

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
// 整数二分,模板题:acwing.789
bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;// l = mid时+1上取整
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
-------
// 浮点数二分,模板题:acwing.790
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;
}

4.高精度加减乘除法

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
// 高精度加法,模板题:acwing.791
// C = A + B,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(1);// 判断最高位是否需要进位
return C;
}
-------
// 高精度减法,模板题:acwing.792
// C = A - B,A和B都是正整数,保证了 A >= B
vector<int> sub(vector<int> &A,vector<int> &B){
vector<int> C;
for (int i = 0,t = 0;i < A.size();i++){
t = A[i] - t;
if (i < B.size()) t -= B[i];// B的位数<=A的位数,需要判断是否存在
C.push_back((t + 10) % 10);// 统一处理Ai-Bi-t大于等于0小于0的两种情况
if (t < 0) t = 1;// <0借一位
else t = 0;// 否则不借位
}
// C的位数等于A的位数,需要处理前导零
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
-------
// 高精度乘法,模板题:acwing.793
// C = A * b,A >= 0,b >= 0
vector<int> mul(vector<int> &A,int b){
vector<int> C;
for (int i = 0,t = 0;i < A.size() || t;i++){
// 循环退出条件相比加法多了|| t,就不用额外判断最后的进位
t += A[i]*b;
C.push_back(t % 10);
t /= 10;
}
// 去除前导零
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
-------
// 高精度除法,模板题:acwing.794
// A / b = C ... r,A >= 0,b > 0
vector<int> div(vector<int> &A,int b,int &r){
vector<int> C;
// 注意:除法是从高位开始计算的,和加减乘法不一样
for (int i = A.size()-1;i >= 0;i--){
r = r*10 + A[i];
C.push_back(r / b);
r %= b;
}
reverse(C.begin(),C.end());// 除法需要翻转一下
// 去除前导零
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}

5.前缀和

1
2
3
4
5
6
7
8
9
10
// 一维前缀和 —— 模板题 AcWing 795
s[i] = s[i-1] + a[i];// 前缀和初始化
s[r] - s[l-1] // l到r的区间和
-------
// 二维前缀和 —— 模板题 AcWing 796
S[i, j] = 第i行j列格子左上部分所有元素的和
求前缀和矩阵:预处理
s[i][j] = s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]

6.差分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 一维差分 —— 模板题 AcWing 797
给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c
对于给定数组a的修改查询,构造差分数组b,数组a就是数组b的前缀和数组
// 计算前缀和并输出修改之后的数组
方式1:a[i] = a[i-1] + b[i]; 输出数组a
方式2:b[i] += b[i-1]; 输出数组b
-------
// 二维差分 —— 模板题 AcWing 798
给以(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
// 求b[1][1]到b[i][j]的前缀和a[i][j]并输出修改之后的数组
方式1:a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1] + b[i][j]; 输出数组a
方式2:b[i][j] += b[i-1][j] + b[i][j-1] - b[i-1][j-1]; 输出数组b

// 方式2不需要另外再开数组a,推荐方式2

7.位运算

1
2
3
// 位运算 —— 模板题 AcWing 801
(1) 求x的第k位数字 x >> k & 1
(2) lowbit(x) = x & -x=x & (~x + 1),返回x的最后一位1及以后的数

8.双指针

1
2
3
4
5
6
7
8
9
// 双指针算法 —— 模板题 AcWIng 799, AcWing 800
for (int i = 0, j = 0; i < n; i ++ )
{
while (j < i && check(i, j)) j ++ ;
// 具体问题的逻辑
}
常见问题分类:
(1) 对于一个序列,用两个指针维护一段区间
(2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作

9.离散化

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
// 离散化 —— 模板题 AcWing 802
vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去掉重复元素

// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{// find的作用也就是用二分的方式通过alls中的数找到其在alls中的下标
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
}
/*手动实现unique函数
vector<int>::iterator unique(vector<int> &a){// 针对java、python等可以手动实现
// C++中比STL慢一点
int j = 0;
for (int i = 0;i < a.size();i++){
if (!i || a[i] != a[i-1]) a[j++] = a[i];// j走得总比i慢
}// a[0] ~ a[j - 1] 存放所有a中不重复的数
return a.begin() + j;
}*/

10.区间合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 区间合并 —— 模板题 AcWing 803
// 将所有存在交集的区间合并
// 注意如果在端点处相交,也算有交集。比如[1,2]和[2,3]算有交集。
void merge(vector<PII> &segs)
{
vector<PII> res;
sort(segs.begin(), segs.end());

int st = -2e9, ed = -2e9;// 初始化维护区间为-INF到-INF,本题可取-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;
}
坚持原创技术分享,您的支持将鼓励我继续创作!