前言
记录c++算法基础的模板
就像cin.tie(0);cout.tie(0);可以提速一样,提高代码编写速度。
文章目录
一、基础算法
1、快速排序模板
void quick_sort(vector<int> &q, int l, int r){
if(l >= r) return;
int i = l - 1, j = r + 1, x = q[ (l + r)/2];
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);
}
2、归并排序模板
void merge_sort(vector<int> &q, int l, int r){
if(l >= r) return;
int mid = (r + l)/2;
merge_sort(q, l, mid);
merge_sort(q, mid+1, r);
int k =0, i = l, j = mid+1;
vector<int> tmp(r - l + 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];
}
3、高精度加法
vector<int> add(vector<int> &A, vector<int> &B){
vector<int> C;
int val = 0, carry = 0;
int i = 0, j = 0;
while(carry || i < A.size() || j < B.size()){
val = carry;
if(i < A.size()){val += A[i++];}
if(j < B.size()){val += B[j++];}
carry = val / 10;
val = val % 10;
C.push_back(val);
}
return C;
}
4、高精度减法
void sub(vector<int> &A, vector<int> &B, vector<int> &C){
int carry = 0, val = 0;
int i = 0, j = 0;
while(carry || i < A.size() || j < B.size()){
val = carry;
if(i < A.size()) {val += A[i++];}
if(j < B.size()) {val -= B[j++];}
carry = val < 0 ? -1 : 0;
val = (val + 10) % 10;
C.push_back(val);
}
while(C.size()>1&&C.back()==0)
{
C.pop_back();
}
}
5、高精度乘法
vector<int> mul(vector<int> &A, int &b){
vector<int> C;
int i = 0;
int val = 0, carry = 0;
while(carry || i < A.size()){
if(i < A.size()) {val = A[i++] * b + carry;}
else {val = carry;}
carry = val / 10;
val = val % 10;
C.push_back(val);
}
return C;
}
6、前缀和
前缀和值得是sum[i]为a数组0-i个值的和。
用于解决数组[l,k]的和,在K组查询中
从O(n)到O(1)复杂度。
vector<int> sum(n + 1);
sum[0] = 0;
for(int i = 1; i <= n ; i++){
sum[i] = sum[i - 1] + q[i - 1];
}
7、差分
差分和前缀和为逆运算。
用于解决数组[l,k]中全部加C,在K组查询中
从O(n)到O(1)复杂度。
for(int i = 1; i <= n ; i++){
temp[i] = array[i] - array[i - 1];
}
8、双指针
int res = 0;
for(int i = 0, j = 0; i < n; i++){
s[a[i]] ++;//为计数数组,存放该位的个数。
while(s[a[i]] > 1){
s[a[j]] --;
j ++;
}
res = max(res, i - j + 1);
哈希表
unordered_set<int> haxi;
int i = 0, j = 0, res = 0;
while(i < n){
if(haxi.count(a[i]) == 0){
haxi.insert(a[i]);
i++;
}
else{
haxi.erase(a[j]);
j++;
}
res = max(res, i - j);
}
9、位运算
cout<<(n >> k & 1);//取二进制第K位
cout<< lowbit(x);//例1010返回10 实现x & (~x + 1)及( x & -x)
//取出x中的1的个数
int x, res;
while(cin>>x){
res = 0;
while(x != 0) x -= lowbit(x), res++;
cout<<res<<" ";
}
10、离散化
思想:将点放在数组里,利用二分查找下标
typedef pair<int, int> PII;
const int N = 300010;
int a[N], s[N];
vector<int> all;
vector<PII> add, query;
int main(){
int n, m;
cin>>n>>m;
for(int i = 0; i < n; i++)
{
int x, y;
cin>>x>>y;
add.push_back({x,y});
all.push_back(x);
}
for(int i = 0; i < m; i++){
int x, y;
cin>>x>>y;
query.push_back({x,y});
all.push_back(x);
all.push_back(y);
}
sort(all.begin(),all.end());
all.erase(unique(all.begin(),all.end()), all.end());
for(auto i : add){
int t = lower_bound(all.begin(), all.end(), i.first) - all.begin() + 1;
a[t] += i.second;
}
for(int i = 1; i <= all.size();i++){
s[i] = s[i-1] + a[i];
}
for(auto i : query){
int l = lower_bound(all.begin(), all.end(), i.first) - all.begin() + 1;
int r = lower_bound(all.begin(), all.end(), i.second) - all.begin() + 1;
cout<< s[r] - s[l -1]<<endl;
}
return 0;
}
11、区间合并
void merge(vector<PII> &segs){
vector<PII> res;
sort(segs.begin(), segs.end());
int l = -2e9, r = -2e9;
for(auto seg : segs){
if(r < seg.first){
if(l != -2e9)
res.push_back({l,r});
l = seg.first;
r = seg.second;
}
else
r = max(seg.second, r);
}
if(l != -2e9)
res.push_back({l,r});
segs = res;
}
二、数据结构
1、单链表(数组模拟)
//静态链表 开始的为空-1;
const int N = 100010;
int val[N], next_node[N], idx, head;
void init(){
head = -1;
idx = 0;
}
void insert_head(int x){
val[idx] = x;
next_node[idx] = head;
head = idx ++;
}
void insert(int k, int x){
val[idx] = x;
next_node[idx] = next_node[k];
next_node[k] = idx++;
}
void remove(int k){
next_node[k] = next_node[next_node[k]];
}
/**动态链表
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
2、双链表(数组模拟)
//开始0作为最左边,1作为最右边,idx从第2开始
const int N = 100010;
int idx, r[N], l[N], val[N];
void init(){
r[0] = 1;
l[1] = 0;
idx = 2;
}
void remove(int k){
r[l[k]] = r[k];
l[r[k]] = l[k];
}
void insert_k_right(int k, int x){
val[idx] = x;
r[idx] = r[k];
l[idx] = k;
l[r[k]] = idx;
r[k] = idx;
idx ++;
}
void insert_k_left(int k, int x){
insert_k_right(l[k],x);
}
3、栈(数组模拟)
const int N = 100010;
int stk[N], tt;
void push(int x){
stk[++tt] = x;
}
void pop(){
tt --;
}
void empty(){
if(tt > 0)
cout<<"NO"<<endl;
else
cout<<"YES"<<endl;
}
void query(){
cout<<stk[tt]<<endl;
}
4、队列(数组模拟)
const int N = 100010;
int q[N], hh, tt = -1;
void push(int x){
q[++tt] = x;
}
void pop(){
hh ++;
}
void empty(){
if(hh <= tt > 0)
cout<<"NO"<<endl;
else
cout<<"YES"<<endl;
}
void query(){
cout<<q[hh]<<endl;
}
5、单调栈(数组模拟)
单调栈解决求前一个小于x的值
#include<iostream>
using namespace std;
const int N = 100010;
int stk[N], tt;
int main(){
int n;
cin>>n;
while(n--){
int x;
cin>>x;
while(tt && stk[tt] >= x) tt--;//这里当i < j && stk[i] > stk[j]时,i无用
if(!tt) cout<<"-1 ";
else cout<<stk[tt]<<" ";
stk[++tt] = x;
}
return 0;
}
6、单调队列(数组模拟)
解决在滑动窗口中输出最大值和最小值
const int N= 1000010;
int a[N];
int q[N];//存放下标
int tt = -1, hh;
int main(){
int n, k;
cin>>n>>k;
for(int i = 0; i < n; i++) cin>>a[i];
for(int i = 0; i < n; i++){
if(hh <= tt && i - q[hh] + 1 > k) hh++;//多出来以后将hh++
while(hh <= tt && a[q[tt]] >= a[i]) tt--;//当i值比队列尾小时,队列尾没用
q[++tt] = i;
if(i >= k - 1) printf("%d ",a[q[hh]]);
}
cout<<endl;
tt = -1, hh = 0;
for(int i = 0; i < n; i++){
if(hh <= tt && i - q[hh] + 1 > k) hh++;
while(hh <= tt && a[q[tt]] <= a[i]) tt--;
q[++tt] = i;
if(i >= k - 1) printf("%d ",a[q[hh]]);
}
return 0;
}
7、KMP字符串匹配
//find函数查找全部位置
int position;
while(position != s.npos){
position = s.find(p,position);
if(position != s.npos) {
cout<<position<<" ";
position ++;
}
}
#include<iostream>
using namespace std;
const int N = 1e5 + 10, M = 1e6 + 10;
int ne[N];
char p[N], s[M];
int main(){
int n,m;
cin>> n;
for(int i = 1; i <= n; i++) cin>>p[i];
cin>>m;
for(int i = 1; i <= m; i++) cin>>s[i];
//求ne[j]数组
for(int i = 2, j = 0; i <= n; i++){
while(j && p[i] != p[j + 1]) j = ne[j];
if(p[i] == p[j + 1]) j++;
ne[i] = j;
}
for(int i = 1, j = 0; i<= m; i++){
while(j && s[i]!= p[j+1]) j = ne[j];
if(s[i] == p[j+1]) j++;
if(j == n){
cout<<i - n<<" ";
j = ne[j];
}
}
return 0;
}
8、Trie字符串统计
Trie主要是快速插入和查询string
const int N = 1e5 + 10;
int son[N][26], cnt[N], idx;
void insert(char *str){
int p = 0;
for(int i = 0; str[i]; i++){
int u = str[i] - 'a';
if(!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
}
cnt[p] ++;
}
int query(char *str){
int p = 0;
for(int i = 0; str[i]; i++){
int u = str[i] - 'a';
if(!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
9、并查集
int find_p(int x){//找祖宗结点 + 路径压缩
if(p[x] != x) p[x] = find_p(p[x]);
return p[x];
}
if(op == 'M'){
p[find_p(y)] = find_p(x);
}
else if(op == 'Q'){
if(find_p(x) == find_p(y)) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
10、字符串哈希
#include<iostream>
#include<algorithm>
using namespace std;
//字符串哈希,不一定完全对,看人品,99.99%
//思想:将string转化成P进制的数,利用前缀和求
typedef unsigned long long ULL;
const int N = 1e5 + 10, P = 131;//P一般为131或13331(经验),冲突率0.001%
ULL h[N], p[N];//p存放位数的次方, h作为前缀和
ULL get(int l, int r){
return h[r] - h[l - 1] * p[r - l + 1];
}
int main(){
int n, m;
cin>>n>>m;
string s;
cin>>s;
s = " " + s;
p[0] = 1;
for(int i = 1; i <= n; i++){
p[i] = p[i - 1] * P;
h[i] = h[i - 1] * P + s[i];
}
while(m--){
int l1, r1, l2, r2;
cin>>l1>>r1>>l2>>r2;
if(get(l1, r1) == get(l2, r2)) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}
三、搜索与图论
1、DFS(数字全排列)
#include <iostream>
using namespace std;
const int N = 10;
int n;
int state[N], path[N];
void dfs(int u){
if(u == n){
for(int i = 0; i < n; i++) cout<<path[i]<<" ";
cout<<endl;
return;
}
for(int i = 1; i <= n; i++){
if(!state[i]){
state[i] = true;
path[u] = i;
dfs(u + 1);
state[i] = false;
path[u] = 0;
}
}
}
int main(){
cin>>n;
dfs(0);
return 0;
}
2、BFS(迷宫问题)
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
int n, m;
int path[N][N];
int g[N][N];
int bfs(){
memset(path, -1, sizeof path);
queue<PII> q;
path[0][0] = 0;
q.push({0, 0});
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
while(q.size()){
auto t = q.front();
q.pop();
for(int i = 0; i < 4; i++){
int x = t.first + dx[i], y = t.second + dy[i];
if(x >= 0 && x < n && y >=0 && y <m && path[x][y] == -1 && g[x][y] == 0)
{
q.push({x, y});
path[x][y] = path[t.first][t.second] + 1;
}
}
}
return path[n - 1][m -1];
}
int main(){
cin>>n>>m;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
cin>>g[i][j];
cout << bfs() <<endl;
}
3、树与图的深度优先遍历(树的重心)
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 10, M = N * 2;
int n, ans = N;
bool state[N];
vector<int> q[N];
void add(int a, int b){
q[a].push_back(b);
q[b].push_back(a);
}
int dfs(int u){
state[u] = true;
int sum = 1, size = 0;
for(int i = 0; i < q[u].size(); i++){
int j = q[u][i];
if(state[j]) continue;
int s = dfs(j);
sum += s;
size = max(size, s);
}
size = max(size, n - sum);
ans = min(ans, size);
return sum;
}
int main(){
cin>>n;
int a, b;
while(cin>>a>>b){
add(a, b), add(b, a);
}
dfs(1);
cout<< ans <<endl;
return 0;
}
4、树与图的广度优先遍历(树的层次)
#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int path[N];
vector<int> h[N];
queue<int> q;
void add(int a, int b){
h[a].push_back(b);
}
void bfs(){
path[1] = 0;
q.push(1);
while(q.size()){
auto t = q.front();
q.pop();
for(int i = 0; i < h[t].size(); i++){
int j = h[t][i];
if(path[j] != -1) continue;
path[j] = path[t] + 1;
q.push(j);
}
}
}
int main(){
memset(path, -1, sizeof path);
cin>>n>>m;
while(m --){
int a, b;
cin>>a>>b;
add(a, b);
}
bfs();
cout << path[n] <<endl;
return 0;
}
5、有向图的拓扑序列
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int d[N], q[N];
vector<int> h[N];
void add(int a, int b){
h[a].push_back(b);
d[b] ++;
}
bool topsort(){
int hh = 0, tt = -1;
for(int i = 1; i <= n; i++){
if(!d[i]){
q[++ tt] = i;
}
}
while(hh <= tt){
int t = q[hh ++];
for(int i = 0; i < h[t].size(); i++){
int j = h[t][i];
if(-- d[j] == 0){
q[++ tt] = j;
}
}
}
return tt == n - 1;
}
int main(){
cin>>n>>m;
int a, b;
while(cin>>a>>b){
add(a, b);
}
if(!topsort()) cout<<"-1 "<<endl;
else{
for(int i = 0; i < n; i++)
cout<<q[i]<<" ";
}
return 0;
}