图论
nth_element · · 个人记录
目录
1.图的基础算法
- 图的遍历
2.集合合并
- 并查集
3.最短路
-
SPFA -
Dijkstra -
Floyed
4.最小生成树
-
Kruscal -
Prim
5.拓扑排序
-
Topsort
6.强连通分量
- 缩点(tarjan)
7.最近公共祖先
- tarjan
- 倍增
- 树链剖分
一、图的基础算法
\left (1 \right ) 图的遍历
1.邻接矩阵
①定义:
int ditu[N][N]; //N为点的个数
②遍历:
for(int i=1;i<=N;i++)
{
for(int j=1;j<=N;j++)
{
ditu[i][j]=z;//指i到j有路径且边权为ditu[i][j]=z
}
}
③好处:好写,遍历起来方便
④坏处:不能有重边,
⑤使用时候:
⑥空间复杂度:
2.邻接表
①定义:
vector<int>vc[N],vcw[N];//vc[N]是x到y的路径,vcw[M]是它们的边权
for(int i=1;i<=M;i++)//M是路径条数
{
int x,y;//有两个点联通
vc[x].push_back(y);//x到y有路径
vcw[x].push_back(w);//x到y边权为w
}
②遍历
int l = vc[f1].size();//f1为起点
for(int i=0;i<l;i++)
{
int t = vc[f1].at(i);//t为终点
int tv = vcw[f1].at(i);//tv为边权
}
③好处:动态数组,比邻接矩阵范围更大
④坏处:受到vector影响,访问需要时间,速度受限
⑤使用时候:基本不用。
⑥空间复杂度:
3.链式前向星
①定义:
struct edge
{
int to,next,v;
}e[M];//前向星结构体
int head[N],ei=0;
int add(int x,int y,int v)//建图函数
{
ei++;
e[ei].next=head[x];
e[ei].to=y;
e[ei].v=v;
head[x]=ei;
}
for(int i=1;i<=M;i++)
{
add(x,y,v);//指起点为x,终点为y,边权为v
}
②遍历:
for(int i=head[from];i;i=e[i].next)//from为起点
{
int to=e[i].to;//寻找它的终点to
}
③优点:速度快,使用灵活
④坏处:代码长,不好写,常数高
⑤使用时候:几乎所有时候
⑥空间复杂度;
二、集合合并
\left (1 \right ) 并查集
1.定义:(以下摘自维基百科)
在计算机科学中,并查集是一种树型的数据结构,用于处理一些不交集(Disjoint Sets)的合并及查询问题。
个人理解:
因为并查集是一种树型的数据结构,它的原理就是不断寻找父亲,并判断是否是同一个父亲,这就是并查集的本质
2.考点:kruscal算法,判断几个数是否为同一集合。
3.代码实现:
①不初始化+递归(附带路径压缩+Rank合并)
int f[N];//父亲数组
inline int findf(int x)//Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。
{
if(f[x]==0)
{
return x;
}
f[x]=findf(f[x]);
return f[x];
}
inline int uion(int x,int y)//Union:将两个子集合并成同一个集合。
{
x=findf(x);
y=findf(y);
if(x!=y)
{
f[x]=y;
}
}
②初始化+循环(附带路径压缩+Rank合并)
int pre[N];
int find(int x)//查找根节点
{
int r=x;
while(pre[r]!=r)//返回根节点 r(初始化)
{
r=pre[r];
}
int i=x,j;
while(i!=r)//路径压缩
{
j=pre[i];// 在改变上级之前用临时变量 j 记录下他的值
pre[i]=r;//把上级改为根节点
i=j;
}
return r;
}
void join(int x,int y)//判断x y是否连通,如果已经连通,就不用管了,如果不连通,就把它们所在的连通分支合并起。
{
int fx=find(x),fy=find(y);
if(fx!=fy)
{
pre[fx]=fy;
}
}
4.时间复杂度:O\left ( \alpha \left(N \right )\right )
其中
因此,平均运行时间是一个极小的常数。