数据结构学习:图的最小生成树
连通图的生成树是包含图中全部顶点的一个极小连通子图。
若图中顶点数为n,则它的生成树含有 n-1 条边。对生成树而言,若砍去它的一条边,则会变成非连通图,若加上一条边则会形成一个回路。
对于⼀个带权连通⽆向图G = (V, E),⽣成树不同,每棵树的权(即树中所有边上的权值之和)也可能不同。设 R 为 G 的所有⽣成树的集合,若 T 为 R 中边的权值之和最⼩的⽣成树,则 T 称为 G 的最⼩⽣成树
最⼩⽣成树可能有多个,但边的权值之和总是唯⼀且最⼩的
最⼩⽣成树的边数 = 顶点数 - 1。砍掉⼀条则不连通,增加⼀条边则会出现回路
如果⼀个连通图本身就是⼀棵树,则其最⼩⽣成树就是它本身
只有连通图才有⽣成树,⾮连通图只有⽣成森林
Prim 算法(普⾥姆)
从某⼀个顶点开始构建⽣成树;每次将代价最⼩的新顶点纳⼊⽣成树,直到所有顶点都纳⼊为⽌。
时间复杂度:O(|V|^2 )适合⽤于边稠密图
算法实现思路:
1.标记各节点是否已加⼊树
2.计算各节点加⼊树的最低代价
3.找到未加入树的结点中代价最低的加入树,并修改标记
4.重复2和3直至所有结点加入树(每加入一个结点则重新计算各结点加入树的最低代价)
Kruskal 算法(克鲁斯卡尔)
每次选择⼀条权值最⼩的边,使这条边的两头连通(原本已经连通的就不选)直到所有结点都连通
时间复杂度:O( |E| * log2( |E| ) )适合⽤于边稀疏图
算法实现思想:
1.将各条边按权值排序
2.从低到高依次检查每条边的两个顶点是否连通(判断是否属于同⼀个集合),若不连通则连起来,已连通跳过
最短路径问题
⽆权图可以视为⼀种特殊的带权图,只是每条边的权值都为1
// 求顶点u到其他顶点的最短距离(BFS算法宽度优先)
void BFS_MIN_Distance(Grapg G,int u)
{
for(int i = 0;i<G.vexnum;i++)
{
d[i] = 1000000; //初始化路径长度
path[i] = -1; //最短路径从哪个顶点过来
}
d[u] = 0;
visited[u] = True;
EnQuene(Q,u);
while(!isEmpty(Q)) //BFS的主过程
{//当队列Q不为空时
DeQuene(Q,u); //队头顶点出队,并返回值为u
for(w = firstNeighbor(G,u); w >= 0;w = NextNeighbor(G,u,w))
{
//定义w为u在图G中的第一个临界点
//每循环一次,w为除w之外顶点u的下一个邻接点的顶点号,若w是u的最后一个邻接点,则返回-1
if(!visited[w]) //当w为False,即未访问过时
{
d[w] = d[u] + 1; //路径长度加一
path[w] = u; //最短路径应为u到w
visited[w] = True; //标记w顶点为已访问
EnQuene(Q,w); //将w顶点入队
}
}
}
}
最短路径长度在数组d[]中,最短路径在数组path[]中
BFS算法求最短路径的局限性:
BFS算法求单源最短路径只适⽤于⽆权图,或所有边的权值都相同的图
Dijkstra算法
定义三个数组
1.标记各顶点是否已找到最短路径 final
2.最短路径⻓度 dist
3.路径上的前驱 path
思路:
1.从V0 开始,初始化三个数组
2.循环遍历所有结点,找到还没确定最短路径,且dist 最⼩的顶点Vi ,令final[i]=ture
3.检查所有邻接⾃ Vi 的顶点,若其 final 值为false,则更新 dist 和 path 信息
4.循环2和3,直到所有结点都找到最短路径
通过dist[]数组可以知道V0到其他结点的最短路径长度
通过path[]数组可以知道V0到其他结点的最短路径
时间复杂度:O(n^2 )即O(|V|^2)
Dijkstra 算法不适⽤于有负权值的带权图
Eg:
使用Dijkstra算法的话,第一步从V0到V2,第二步从V0到V1结束。
Floyd算法
求出每⼀对顶点之间的最短路径
使⽤动态规划思想,将问题的求解分为多个阶段
对于n个顶点的图G,求任意⼀对顶点 Vi —> Vj 之间的最短路径可分为如下⼏个阶段:
#初始:不允许在其他顶点中转,最短路径是?
#0:若允许在 V0 中转,最短路径是?
#1:若允许在 V0 、V1 中转,最短路径是?
#2:若允许在 V0 、V1 、V2 中转,最短路径是?
…
#n-1:若允许在 V0 、V1 、V2 …… Vn-1 中转,最短路径是?
算法借助两个二位数组A[]和path[]
通过使用A[]数组来记录顶点间的最短路径长度
通过使用path[]数组来记录顶点间的最短路径的中转点,若直接到达不需要中转点记为-1
思路:
1.初始,不中转,将两个数组初始化,A[]中记录不中转的最短路径(即一步到达),path[]全部初始化为-1,若不中转无法到达则在数组A[]中记为无穷
2.允许i=1个顶点可以中专,更新两个数组
3.i+1,并重复步骤2,直到 I = n-1
for(int k = 0;k < n;k++)
{
for(int i = 0;i < n;i++)
{
for(int j = 0;j < n;j++)
{
if(A[i][j] > A[i][k] + A[k][j])
{
A[i][j] = A[i][k] + A[k][j]; //找到更短的路径,更新路径
path[i][j] = k; //更新最短路径的中转点
}
}
}
}
时间复杂度,O(|V|^3 )
空间复杂度,O(|V|^2 )
Floyd 算法不能解决带有“负权回路”的图(有负权值的边组成回路),这种图有可能没有最短路径
有向⽆环图:若⼀个有向图中不存在环,则称为有向⽆环图,简称DAG图
化简为:
顶点中不可能出现重复的操作数
Step 1:把各个操作数不重复地排成⼀排
Step 2:标出各个运算符的⽣效顺序(先后顺序有点出⼊⽆所谓)
Step 3:按顺序加⼊运算符,注意“分层”
Step 4:从底向上逐层检查同层的运算符是否可以合体