常用算法模板

最大公约数

int gcd(int a, int b){
	if(b == 0) return a;
	return gcd(b, a%b);
}

最小公倍数

int lcm(int a, int b){
	return a/gcd(a, b)*b;
} 

判断素数

时间复杂度 O ( s q r t ( n ) / 3 ) O(sqrt(n)/3) O(sqrt(n)/3)
根据质数分布规律:大于等于5的质数一定是和6的倍数相邻。

bool check(int x) {
	if(x == 2|| x == 3 ||x == 5) return true;
	if(x <= 5) return false;
	if(x%6 != 1 && x%6 != 5) return false;
	for(int i = 5; i*i <= x; i += 6){
		if(x % i == 0 || x % (i+2)==0) return false;
	}
	return true;
}

Eratosthenes素数筛法

int primes[N];
bool p[N] = {0};
int k = 0;
void get_primes(){
	for(int i = 2; i < N; i++){
		if(!p[i]){
			primes[k++] = i;
			for(int j = i+i; j < N; j += i){
				p[j] = 1;
			}
		}
	}
}

线性筛素数

时间复杂度 O ( n ) O(n) O(n)

void get_primes(int n){
    for(int i = 2; i <= n; i++){
        if(!st[i]) {
            primes[k++] = i;
        }
        for(int j = 0; primes[j] <= n/i; j++){
                st[primes[j]*i] = true;
                if(i % primes[j] == 0) break;//primes[j]是i的最小质因子
            }
    }
}

并查集

时间复杂度几乎 O ( 1 ) O(1) O(1)

void init(){
	for(int i = 0; i < n; i++) root[i] = i;
}
int find(int x){
   if(root[x] == x) return x;
   return root[x] = find(root[x]);
}
void union(int a, int b){
	int fa = find(a);
	int fb = find(b);
	if(fa != fb){
		root[fb] = fa; //合并
		num[fa] += num[fb]; //统计每个集合的个数
	}
}

Dijkstra算法

  1. 朴素版Dijkstra算法
    通常用邻接矩阵存,顶点数在500范围内,注意图的边初始化为正无穷。
int Dijkstra(){
	memset(d, 0x3f, sizeof d);
	d[1] = 0;
	for(int i = 0; i < n; i++){ //迭代n次 
		int u = -1;
		for(int j = 1; j <= n; j++)
			if(!st[j] && (u == -1 || d[u] > d[j])){
				u = j;
			}
		if(u == -1) return -1;
		st[u] = true;
		for(int v = 1; v <= n; v++){
			d[v] = min(d[v], d[u] + G[u][v]);
		}
	}
	if(d[n] == 0x3f3f3f3f) return -1;
	else return d[n];
}
  1. 堆优化版的Dijkstra算法
    通常用邻接表存,顶点数在 1 0 5 10^5 105范围内,注意邻接表表头的初始化。

void add(int a, int b, int c){ //加边加权
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a]=idx++;
}
int dijkstra(){
    memset(d, 0x3f, sizeof d);
    priority_queue<PII, vector<PII>, greater<PII>> pq;
    d[1] = 0;
    pq.push({0, 1});
    while(pq.size()){
        auto t = pq.top();
        pq.pop();
        int ver = t.second, dist = t.first;
        if(st[ver]) continue; //用于处理重边和自环
        for(int i = h[ver]; i != -1; i = ne[i]){
            int j = e[i];
            if(d[j] > w[i] + dist){ //注意这里的更新方式, 是从现有的最短距离上,判断加上边权能否改变
                d[j] = w[i] + dist;
                pq.push({d[j], j});
            }
        }
    }
    if(d[n]==0x3f3f3f3f) return -1;
    else return d[n];
}

SPFA算法

O ( m l o g m ) O(mlogm) O(mlogm)
处理有负权但是没有负环的单源最短路径
这里是用的队列,而Dijkstra堆优化用的是优先队列

int spfa(int s){
    memset(d, 0x3f, sizeof d);
    d[s] = 0;
    queue<int> q;
    q.push(1);
    st[1] = true;  //注意,这里要对在队列中的顶点进行标记
    while(q.size()){
        int t = q.front();
        q.pop();
        st[t] = false;
        for(int i = h[t]; i != -1; i = ne[i]){
            int j = e[i];
            if(d[j] > d[t]+ w[i]){
                d[j] = d[t]+ w[i];
                if(!st[j]){
                    q.push(j);
                    st[j] = true;
                }
            }
        }
        
    }
    if(d[n] == 0x3f3f3f3f) return -1;
    else return d[n];
}

Floyd算法

O ( n 3 ) O(n^3) O(n3)
用邻接矩阵存储,通常顶点数小于200

void floyd(){
    for(int k = 1; k <= n; k++){
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                d[i][j] = min(d[i][j], d[i][k]+d[k][j]);
            }
        }
    }
}

拓扑排序

图使用邻接表存储

bool topsort(){
	int cnt = 0;
	queue<int> q;
	for(int i = 1; i <= n; i++){
		if(!ind[i]) q.push(i);
	}
	while(q.size()){
		int t = q.front();
		q.pop();
		cnt++;
		for(int j = 0; j < G[t].size(); j++){
			int v = G[t][j];
			ind[v]--;
			if(!ind[v]) q.push(v);
		} 
	}
	if(cnt == n) return 1;
	else return 0;
}

树状数组

区间更新与求和

#define low(x) ((x)&(-x))
void update(int x, int v){
	while(x <= n){
		c[x] += v;
		x += low(x);
	}
}
int sum(int x){
	int sumx = 0;
	while(x){
		sumx += c[x];
		x -= low(x);
	}
	return sumx;
}

Trie树

int son[N][26], cnt[N], idx = 0;
void insert(string &word){
	int p = 0;
	for(int i = 0; i < word.size(); i++){
		int u = word[i] - 'a';
		if(son[p][u] == 0) son[p][u] = ++idx;
		p = son[p][u];
		cnt[p]++;
	}
}
int query(string &word){
	int p = 0;
	for(int i = 0; i < word.size(); i++){
		int u = word[i] - 'a';
		if(son[p][u] == 0) return 0;
		p = son[p][u];
	}
	return cnt[p];
}

最近公共祖先LCA

  1. 二叉树的LCA
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(!root || root == p || root == q) return root;
        auto left = lowestCommonAncestor(root->left, p, q);
        auto right = lowestCommonAncestor(root->right, p, q);
        if(left && right) return root;
        return left == nullptr? right: left;
    }
};

背包问题

  1. 01背包
for(int i = 1; i <= N; i++){
	for(int c = V; c >= w[i]; c--){
		dp[c] = max(dp[c], dp[c - w[i]] + v[i]);
	}
}
  1. 完全背包
for(int i = 1; i <= N; i++){
	for(int c = w[i]; c <= V; c++){
		dp[c] = max(dp[c], dp[c - w[i]] + v[i]);
	}
}

你可能感兴趣的:(常用算法模板)