图论知识点
1、表达式树
2、最小生成树
G=(V,E),连接图G的所有点,且边集是E是子集的树称为G的生成树,而权值最小的生成树称为最小生成树。构造最小生成树,可以采用Kruskal算法。
一、Kruskal算法(求图中最小生成树算法)
Kruskal算法首先将按照边的权值,将各边按照从小到大的顺序排列,然后依次考察各边(u,v)。
情况1:u和v在同一连通分量中,那么假如(u,v)后会形成环(即首尾相连),因此不能选择。
情况2:如果u和v在不同的连通分量里,那么加入(u,v)一定是最优的。
//n个顶点,m条边,u[i],v[i]表示第i条边的两个端点,w[i]表示第i条边的权值
//排序后第i小的边的序号保存在r[i]中。
int cmp(const int i,const int j){return w[i]
二、Dijkstra算法(求图中单源最短路)
Dijkstra算法用于求解边权为正,从单个源点出发,到所有结点的最短路。该算法同时适用于有向图和无向图。
假设起点是结点 0,它到结点i的路径长度为d[i].未标号结点的v[i]=0,已标号结点的v[i]=1。为了简单起见,用w[x][y]=INF表示边(x,y)不存在。
memset(v,0,sizof(v));
//设置d[0]=0,其余为INF.
for(int i=0;i
打印路径的时候,从终点出发,不断顺着d[i]+w[i][j]==d[j]边回退到起点,也可以在进行算法中,记录最短路径的父节点。
邻接表存储图
使用邻接表存储图,在稀疏的图中有空间优势。下面的代码分别采用数组和vector的方式实现邻接表建图。
//首先需要给每条边编号,first[u]保存结点u的第一条边的编号,
//next[e]表示编号为e的边的“下一条边”的编号。
//下面是使用数组建立邻接表的过程。
int n,m
int first[manx];
int u[maxm],v[maxm],w[maxm],next[maxm];
void read_graph()
{
scanf("%d%d",&n,&m);//输出顶点数量和边的数量
for(int i=0;i
版本二:采用结构题和优先队列实现Dijkstra算法
struct Edge
{
int from, to, dist;
Edge(int u,int v,int d):from(u),to(v),dist(d){}
};
struct Dijkstra
{
int n,m;
vector edges;
vector G[maxn];
bool done[maxn];//是否进行标记
int d[maxn];//s到各点的距离
int p[maxn];//最短路中的上一条边
void init (int n)
{
this->n = n;
for(int i=0;i Q;
for(int i=0;id[u]+e.dist)
{
d[e.to] = d[u] + e.dist;
p[e.to] = G[u][i];
Q.push((HeapNode){d[e.to],e.to});
//使用优先队列进行动态排序。
}
}
}
}
三、Bellman-Ford 算法(图中有负权,当最短路存在时,求最短路)
代码如下:
for (int i=0;i
采用队列形式实现代码:
bool bellman_ford(int s)
{
queue Q;
memset(inq,0,sizeof(inq));
memset(cnt,0,sizeof(cnt));
for(int i=0;i d[u] + e.dist)
{
e[e.to] = d[u] + e.dist;
p[e.to] = G[u][i];
if(!inq[e.to])
{
Q.push(e.to);
inq[e.to]= true;
if(++cnt[e.to]>n)
return false;
}
}
}
}
return true;
}
三、Floyd算法(求图中每两点的距离)
初始时,d[i][i]=0,其他d值设置为无穷大。
for(int k=0;k
通过将代码中最后一行更换成d[i][j] = d[i][j] || (d[i][k] && d[k][j])