文章的各个算法的代码实现由本人码出,另外会有推荐大佬的相关博客
大概会涉及到:
我这里大胆地用万能头文件来省略一大堆头文件
#include<bits/stdc++.h>
using namespace std;
Binary Indexed Trees
关于树状数组博客
为什么只有树状数组,因为我不会线段树 【\笑哭】
我写的:https://www.cnblogs.com/callmebg/p/7228491.html
树状数组的原理和实现:https://www.cnblogs.com/George1994/p/7710886.html
#define lowbit(i) (i & -i) //宏定义lowbit,含义博客有
const int N = 500005; //一般把数据范围先写出来
int n, m; //n为输入的数组大小,m为查询次数
int bit[N]; //bit[i]表示以i结尾长为lowbit(i)的数组和
void add(int x, int v) //将第x个数字加上v
{
bit[x] += v; //每个含有原第x个数字的都要加v
while (x <= n) //持续更新
{
x += lowbit(x); //跳到下一个含有第x个数字的地方
}
}
int query(int x) //查询前x个数的和
{
int ans = 0; //答案
while (x > 0) //边界条件
{
ans += bit[x]; //加上该点的值
x -= lowbit(x); //跳到下一个,还未加的地方
}
return ans;
}
Binary
关于二分的博客
你真的会写二分查找吗?:https://www.cnblogs.com/bofengyu/p/6761389.html
在从小到大的数组a的[x,y)范围内查找v的位置
int bsearch(int *a, int x, int y, int v)
{
int mid;
while (x < y)
{
mid = x + (y - x) / 2; //取中值
if (v == a[mid])
return mid; //刚好取到
else if (v < a[mid])
y = mid; //调整边界
else
x = mid + 1;
}
return -1; //无解
}
//下面代码是我自己写的lower_bound,可能有错
//在从小到大的数组a的[x,y]范围内返回第一个大于等于v的位置
int l_bound(int *a, int x, int y, int v)
{
int mid;
while (x < y)
{
mid = x + (y - x) / 2;
if (a[mid] < v)
x = mid + 1;
else
y = mid;
}
return x;
}
quick_sort
快排,当然,建议用sort
快排的递归和非递归版本:https://blog.csdn.net/Hadas_Wang/article/details/50917058
快排原理讲解:https://blog.csdn.net/TesuZer/article/details/80969006
void Qsort(int *p, int l, int r)
{
int i = l;
int j = r;
int t;
int mid = p[(l + r) / 2];
while (i <= j)
{
while (p[i] < mid) //从左端开始找不比“中间值”小的数的位置
i++;
while (p[j] > mid) //从右端开始找不比“中间值”大的数的位置
j--;
if (i <= j) //交换两值
{
t = p[i];
p[i] = p[j];
p[j] = t;
i++;
j--;
}
}
//将剩下的两部分排序
if (j > l)
Qsort(p, l, j);
if (i < r)
Qsort(p, i, r);
}
素数
线性筛,用于线性时间筛选一定范围内的素数
线性筛法求素数:https://www.cnblogs.com/grubbyskyer/p/3852421.html
//显然MAX为数据范围的最大值
bool vis[MAX]; //true为非素数,false为素数,开始时默认都为素数
int prime[MAX], cnt; //prime[i]表示第i个素数,cnt表示现在筛选出多少素数
void m_prime(int n) //n表示要从1筛到n
{
int i, j;
cnt = 0; //现在没有筛出素数
vis[1] = true; //1不是素数
for (i = 2; i < n; i++) //从2开始,不用到n,因为如果n是合数,必然在之前被判断出来
{
if (!vis[i])
prime[++cnt] = i; //如果i还没有被访问到,则i是素数,存起来
for (j = 1; j <= cnt && i * prime[j] <= n; j++) //j表示用每个素数去筛,i*prime[j]<=n防止筛过界
{
vis[i * prime[j]] = true; //显然i*prime[j]不是素数
if (i % prime[j] == 0)
break; //我忘了有什么用,大概是防止重复筛选吧
}
}
}
判断一个数是不是素数
#include <stdio.h>
#include <math.h>
int main()
{
int m; // 输入的整数
int i; // 循环次数
int k; // m 的平方根
printf("输入一个整数:");
scanf("%d", &m);
// 求平方根,注意sqrt()的参数为 double 类型,这里要强制转换m的类型
k = (int)sqrt((double)m);
for (i = 2; i <= k; i++)
if (m % i == 0)
break;
// 如果完成所有循环,那么m为素数
// 注意最后一次循环,会执行i++,此时 i=k+1,所以有i>k
if (i > k)
printf("%d是素数。\n", m);
else
printf("%d不是素数。\n", m);
return 0;
}
//未完待续 made by wcb
//2018年9月15日下午
并查集
百科:https://baike.baidu.com/item/并查集/9388442?fr=aladdin
并查集:https://blog.csdn.net/qq_32595453/article/details/80572191
int fa[N]; //fa[i]表示i的祖先
void init(int n) //初始化
{
for (int i = 1; i <= n; i++)
fa[i] = i; //默认自己是自己的祖先
}
int getf(int x) //获取x的祖先
{
return fa[x] = fa[x] == x ? x : getf(fa[x]);
//路径压缩
}
void mer(int x, int y) //合并x和y
{
if (getf(x) != getf(y)) //显然不能成环
{
fa[getf(x)] = fa[y]; //注意不能直接用fa【x】替换
}
}
并查集应用
//Kruskal 最小生成树
https://blog.csdn.net/osc_2016_4/article/details/58590471
//涉及贪心 并查集
struct edge
{
int f, t, d; //起点,终点,距离
} ed[M];
int n, m, ans = 0, now = 0; //n个点,m条边,答案,现在添加了几条边
int f[N]; //并查集
int getf(int t) return f[t] = f[t] == t ? t : getf(f[t]);
bool com(edge a, edge b) return a.d < b.d; //从小到大排序
void Kruskal()
{
int i;
for (i = 1; i <= n; i++)
f[i] = i;
sort(ed + 1, ed + m + 1, com);
for (i = 1; i <= m; i++) //遍历边
{
if (getf(ed[i].f) != getf(ed[i].t)) //不能成环
{
now++; //添加一条边
f[getf(ed[i].f)] = f[ed[i].t]; //把两点连接
ans += ed[i].d; //加上该边距离
}
if (now == n - 1)
break; //当添加到n-1条边就结束
}
}
KMP
//KMP(不考)
KMP算法最浅显理解——一看就明白:https://blog.csdn.net/starstar1992/article/details/54913261
char s[MAX], t[MAX]; //s问题,t模板
int sl, tl, next[MAX];
void kmp()
{
int i, j;
for (i = 2, j = 0; i <= tl; i++)
{
while (j && t[i] != t[j + 1])
j = next[j];
//找到最长的前后缀重叠长度
if (t[i] == t[j + 1])
j++;
next[i] = j;
}
for (i = 1, j = 0; i <= sl; i++)
{
while (j && s[i] != t[j + 1])
j = next[j];
//如果不匹配,则将利用kmp数组往回跳
if (s[i] == t[j + 1])
j++;
if (j == tl)
{
printf("%d\n", i - tl + 1);
j = next[j];
}
}
}
单调队列
//Monotone queue 滑动窗口
FZU 1894 志愿者选拔【单调队列】:https://blog.csdn.net/mengxiang000000/article/details/51207939
monotone queue(单调队列):https://blog.csdn.net/Dylan_Frank/article/details/52969191
struct node
{
int data, id; //数据,位置
} change; //当前压入
node que[N]; //单调队列
int head = 0, tail = 0; //尾为不可访问,留给下次压入的位置
int n, m, a[N]; //n整个长度,m窗口
int i;
for (i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (i = 1; i <= n; i++)
{
while (head < tail && a[i] <= que[tail - 1].data)
tail--; //压入
change.id = i, change.data = a[i];
que[tail++] = change;
if (que[head].id <= i - m)
head++; //过期
if (i >= m)
printf("%d ", que[head.data]);
//压入超过m才输出
}
链式前向星
用于存储图的边
【链式前向星+存图】讲解:https://blog.csdn.net/LOOKQAQ/article/details/81304637
int en = 0; //现在有多少条边
struct edge
{
int t, v; //终点,距离
edge *next; //下一个相同起点的边的指针 (其实是上一个)
} * h[N], ed[M];
//h【i】表示以i为起点的边的指针
//ed【i】表示第i条边的终点,和下一个相同起点的边的指针
void add(int x, int y, int v) //添加一条x到y的边,长度为v
{
ed[++en].next = h[x]; //重点,画图理解
ed[en].t = y;
ed[en].v = v;
h[x] = ed + en; //重新拿到以x为起点的边集的指针
//ed+en是ed[en]的指针
}
spfa
求解单源最短路径问题
最短路径问题—SPFA算法详解:https://blog.csdn.net/qq_35644234/article/details/61614581
void spfa(int s)
{
int dist[N]; //dist[i]表示起点到i的距离
bool inque[N]; //inque[i]表示i在队列与否
queue<int> que; //一个队列,头文件queue
memset(dist, 0x3f, sizeof(dist)); //初始化距离无穷大
dist[s] = 0; //起点自身距离为0
que.push(s); //将起点加入队列
inque[s] = true; //起点在队列里
while (!que.empty()) //当队列不为空
{
int now = que.front(); //取队列的头
que.pop(); //把头去掉
inque[now] = false; //原来的头不在队列里面了
for (edge *e = v[now]; e; e = e->next)
{
//遍历以now为起点的边
if (dist[e->e] > dist[now] + e->d)
{
//如果可以更新就更新
dist[e->e] = dist[now] + e->d;
if (!inque[e->e])
{
//如果该边终点不在队列里,则添加进去
que.push(e->e);
inque[e->e] = true;
}
}
}
}
}
//未完待续 made by wcb
//2018年9月16日12:22:58
lca
//LCA倍增算法
最近公共祖先 LCA 倍增算法:https://www.cnblogs.com/FuTaimeng/p/5655616.html
int en = 0, n;
//链式前向星
struct edge
{
int t;
edge *next;
} * h[N], ed[M];
void add_edge(int s, int t)
{
ed[++en].next = h[s];
h[s] = ed + en;
h[s]->t = t;
}
int deep[N]; //deep[i]表示i的高度
int f[N][25]; //f[i][j]表示i的2的j次方祖先
bool used[N]; //防止重复搜
void dfs(int now, int dep) //一般先搜树的根,根深度为1
{
used[now] = true; //标记
deep[now] = dep; //深度
for (int k = 1; k <= 22; k++)
{
//神奇的递推
int j = f[now][k - 1];
f[now][k] = f[j][k - 1];
}
for (edge *e = h[now]; e; e = e->next)
{
//搜完now的儿子
if (!used[e->t]) //防止重复搜索
{
f[e->t][0] = now; //可以确定它爸爸是谁
dfs(e->t, dep + 1); //接着dfs
}
}
used[now] = false; //dfs的惯例,去除标记
}
int jump(int u, int step) //返回u的第step个祖先
{
for (int k = 0; k <= 22; k++)
if (step & (1 << k)) //二进制加速的跳
u = f[u][k];
return u;
}
int qlca(int u, int v)
{
if (deep[u] < deep[v])
swap(u, v); //让u更深
u = jump(u, deep[u] - deep[v]); //让u和v相同深度
if (u == v) //如果u往上跳,刚好到v
return v; //说明v就是他两的lca
for (int k = 22; k >= 0; k--)
{
//神奇的操作,能保证跳到答案的儿子
if (f[u][k] != f[v][k])
{
u = f[u][k];
v = f[v][k];
}
}
return f[v][0];
}
RMQ
区间最值查询
//RMQ的ST表模板
【模板】RMQ问题的ST表实现:https://www.cnblogs.com/YSFAC/p/7189571.html
RMQ:https://blog.csdn.net/qq_31759205/article/details/75008659
int st[N][20]; //st[i, j]表示从第i个数起连续2^j个数中的最值。
void rmq_init()
{
int i, j;
scanf("%d%d", &n, &m); //全文一样,n表示一般题目中的n,m表示查询数
for (i = 1; i <= n; i++)
scanf("%d", &st[i][0]); //显然st[i][0]就是本身
for (j = 1; (1 << j) <= n; j++) //枚举长度,不能超过n
for (i = 1; i + (1 << j) - 1 <= n; i++) //枚举起点,长度不超过n
st[i][j] = min(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
//st[i][j]的答案从以上两个答案中选出
}
int rmq(int l, int r)
{
int k = 0;
while ((1 << (k + 1)) < r - l + 1) //确保找到2的k次方能大于等于长度的一半
k++; //因为得覆盖整个区域
return min(st[l][k], st[r - (1 << K) + 1][k]);
}
三个算法,一篇解决!(图的最短路问题)
https://www.cnblogs.com/godfray/p/4077146.html
dijkstra的堆优化(我忘了)
https://blog.csdn.net/mu399/article/details/50903876
bellman-ford
https://blog.csdn.net/sms0101/article/details/73088422