图的应用

最小生成树(最小代价树)

对于一个带权连通无向图G=(V,E),生成树不同,每棵树的权(即树中所有边上的权值之和)也可能不同。设R为G的所有生成树的集合,若T为R中边的权值之和最小的生成树,则T称为G的最小生成树(Minimum-Spanning-Tree, MST)。

  • 最小生成树可能有多个,但边的权值之和总是唯一且最小的

  • 最小生成树的边数=顶点数一1。砍掉一条则不连通,增加一条边则会出现回路

  • 如果一个连通图本身就是―棵树,则其最小生成树就是它本身

  • 只有连通图才有生成树,非连通图只有生成森林

Prim算法(普里姆)

Prim 算法(普里姆): 从某一个顶点开始构建生成树; 每次将代价最小的新顶点纳入生成树, 直到所有顶点都纳入为止。 3+5+1+4+2=15 时间复杂度:O(|V|2次方) 适合用于边稠密图

Kruskal算法(克鲁斯卡尔)

Kruskal算法(克鲁斯卡尔) 每次选择一条权值最小的边,使这条边的两头连通(原本已经连通的就不选) 直到所有结点都连通 时间复杂度:O(|E|log|E|) 适合用于边稀疏图

Prim算法的实现思想

更新还没加入的各个顶点的lowCast值

第二轮: 更新还没加入的各个顶点的lowCast值 第3轮: 第4轮: 第5轮:

Kruskal算法的实现思想

初始︰将各条边按权值排序 第1轮:检查第1条边的两个顶点是否连通(是否属于同一个集合) 第2轮︰检查第2条边的两个顶点是否连通(是否属于同一个集合) 第3轮︰检查第3条边的两个顶点是否连通(是否属于同一个集合) 第4轮︰检查第4条边的两个顶点是否连通(是否属于同一个集合) 第5轮︰检查第5条边的两个顶点是否连通(是否属于同一个集合) ..... 每轮判断两个顶点是否属于同一集合

最短路径问题

"G港"是个物流集散中心,经常需要往各个城市运东西,怎么运送距离最近? --单源最短路径问题 各个城市之间也需要互相往来,相互之间怎么走距离最近? --每对顶点间的最短路径

单源最短路径--BFS算法(无权图)、Dijkstra算法(带权图、无权图) 各顶点间的最短路径--Floyd算法(带权图、无权图)

BFS求无权图的单源最短路径

c 复制代码
bool visited[MAX_VERTEX_NUM];//访问标记数组
//广度优先遍历
void BFS(Graph G,int v){//从顶点v出发,广度优先遍历图G
	visit(v);			//访问初始顶点v
    visited[v]=TRUE;	//对v做已访问标记
    Enqueue(Q,v);		//顶点v入队列Q
    while(!isEmpty(Q)){
        DeQueue(Q,v);	//顶点v出队列
        for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
            //检测v所有邻接点
            if(!visited[w]){	//w为v的尚未访问的邻接顶点
                visit(w);		//访问顶点w
                visited[w]=TRUE;//对w做已访问标记
                EnQueue(Q,w);	//顶点w入队列
            }
    }
}
c 复制代码
//求顶点u到其他顶点的最短路径
void BFS_MIN_Distance(Graph G,int v){//从顶点v出发,广度优先遍历图G
	//d[i]表示从v到i结点的最短路径
    for(i=0;i<G.vexnum;++i){
        d[i]=∞; //初始化路径长度
        path[i]=-1;//最短路径从哪个顶点过来
    }
    d[u]=0;
    visited[v]=TRUE;	//对v做已访问标记
    Enqueue(Q,v);		//顶点v入队列Q
    while(!isEmpty(Q)){
        DeQueue(Q,v);	//顶点v出队列
        for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
            //检测v所有邻接点
            if(!visited[w]){	//w为v的尚未访问的邻接顶点
                d[w]=d[v]+1;	//路径长度加1
                path[w]=u;		//最短路径应从v到w
                visited[w]=TRUE;//对w做已访问标记
                EnQueue(Q,w);	//顶点w入队列
            }
    }
}

最短路径问题-Dijkstra算法

Dijkstra算法 检查所有邻接自Vi的顶点,若其final值为false,则更新dist和path信息

检查所有邻接自Vi的顶点,若其final值为false,则更新dist和path信息 V0到V2的最短(带权)路径长度为:dist[2]=9 通过path[ ]可知,V0到V2的最短(带权)路径:V0->V4->V1->V2 Dijkstra算法不适用于有负权值的带权图

最短路径-Floyd算法

Floyd算法:求出每一对顶点之间的最短路径 #0:若允许在V0中转,最短路径是? #1:若允许在V0、V1中转,最短路径是? #2:若允许在V0、V1、V2中转,最短路径是? Floyd算法核心代码

c 复制代码
//....准备工作,根据图的信息初始化矩阵A和path
for(int k=0;k<n;k++){		//考虑以Vk作为中转点
	for(int i=0;i<n;i++){	//遍历整个矩阵,i为行号,j为列号
        for(int j=0;j<n;j++){
            if(A[i][j]>A[i][k]+A[k][j]){//以Vk为中转点的路径更短
                A[i][j]=A[i][k]+A[k][j];//更新最短路径长度
                path[i][j]=k;			//中转点
            }、
        }
    }
}

Floyd算法不能解决带有"负权回路"的图(有负权值的边组成回路),这种图有可能没有最短路径

有向无环图描述表达式

有向无环图:若一个有向图中不存在环,则称为有向无环图,简称DAG图 Step4:从底向上逐层检查同层的运算符是否可以合体

拓扑排序

AOV网 AOV网(Activity On Vertex NetWork,用顶点表示活动的网) 用DAG网(有向无环图)表示一个工程。顶点表示活动,有向边<Vi,Vj>表示活动Vi必须先语活动Vj进行 拓扑排序 拓扑排序的实现: 1:从AOV网中选择一个没有前驱(入度为0)的顶点并输出 2:从网中删除该顶点和所有以它为起点的有向边 3:重复1和2直到当前的AOV网为空或当前网中不存在无前驱的顶点为止

c 复制代码
#define MaxVertexNum 100 //图中顶点数目的最大值
typedef struct ArcNode{  //边表结点
	int adjvex;		//该弧所指向的顶点的位置
    struct ArcNode *nextarc;	//指向下一条弧的指针
}ArcNode;
typedef struct VNode{	//顶点表结点
	VertexType data;	//顶点信息
    ArcNode *firstarc;	//指向第一条依附该顶点的弧的指针
}VNode,AdjList[MaxVertexNum];
typedef struct{
    AdjList vertices;	//邻接表
    int vexnum,arcnum;	//图的顶点数和弧数
}Graph;		//Graph是以邻接表存储的图类型
bool TopologicalSort(Graph G){
	InitStack(S);	//初始化栈,存储入度为0的顶点
    for(int i=0;i<G.vexnum;i++)
        if(indegree[i]==0)
            Push(S,i);	//将所有入度为0的顶点进栈
    int count=0;		//计数,记录当前已经输出的顶点数
    while(!isEmpty(S)){	//栈不空,则存在入度为0的顶点
        Pop(S,i);	//栈顶元素出栈
        print[count++]=i;	//输出顶点i
        for(p=G.vertices[i].firstarc;p;p=p->nextarc){
        	//将所有i指向的顶点的入度减1,并且将入度减为0的顶点压入栈s
            v=p->adjvex;
            if(!(--indegree[v]))
                Push(S,v);	//入度为0,则入栈
        }
    }
    if(count<G.vexnum)
        return false;	//排序失败,有向图中有回路
    else 
        return true;	//拓扑排序成功
}

逆拓扑排序的实现(DFS算法)

c 复制代码
void DFSTraverse(Graph G){	//对图G进行深度优先遍历
	for(v=0;v<G.vexnum;++v)	
        visited[v]=FALSE;	//初始化已访问标记数据
    for(v=0;v<G.vexnum;++v)	//本代码中是从v=0开始遍历
        if(!visited[v])
            DFS(G,v);
}
void DFS(Graph G,int v){//从顶点v出发,深度优先遍历图G
	visited[v]=TRUE;	//设已访问标记
    for(w=FirstNeighbor(G,v);w>=0;w=NextNeighor(G,v,w))
        if(!visited[w]){	//w为u的尚未访问的邻接顶点
        DFS(G,w);
        }
    print(v);//输出顶点
}

关键路径

AOE网 在带权有向图中,以顶点表示事件,以有向边表示活动,以边上的权值表示完成该活动的开销(如完成活动所需的时间),称之为用边表示活动的网络,简称AOE网(Activity On Edge NetWork) AOE网具有以下两个性质: 1:只有在某顶点所代表的事件发生后,从该顶点出发的各有向边所代表的活动才能开始 2:只有在进入某顶点的各有向边所代表的活动都已结束时,该顶点所代表的事件才能发生。 另外,有些活动是可以并行进行的。

从源点到汇点的有向路径可能有多条,所有路径中,具有最大路径长度的路径称为关键路径,而把关键路径上的活动称为关键活动 完成整个工程的最短时间就是关键路径的长度,若关键活动不能按时完成,则整个工程的完成时间就会延长 求关键路径的步骤 求所有事件的最早发生时间 求所有事件的最迟发生时间 求所有活动的最早发生时间 求所有活动的最迟发生时间 求所有活动的时间余量 关键活动:a2、a5、a7 关键路径:V1->V3->V4->V6 关键活动、关键路径的特性 若关键活动耗时增加,则整个过程的工期将增长 缩短关键活动的时间,可以缩短整个工程的工期 当缩短到一定程度时,关键活动可能会变成非关键活动 可能有多条关键路径,只提高一条关键路径上的关键活动速度并不能缩短整个工程的工期,只有加快那些包括在所有关键路径上的关键活动 才能达到缩短工期的目的

相关推荐
手握风云-2 分钟前
零基础Java第十六期:抽象类接口(二)
数据结构·算法
<但凡.1 小时前
编程之路,从0开始:知识补充篇
c语言·数据结构·算法
f狐0狸x1 小时前
【数据结构副本篇】顺序表 链表OJ
c语言·数据结构·算法·链表
Tmbcan2 小时前
zkw 线段树-原理及其扩展
数据结构·zkw 线段树
2301_801760932 小时前
数据结构--PriorityQueue
数据结构
乐悠小码2 小时前
数据结构------队列(Java语言描述)
java·开发语言·数据结构·链表·队列
爱吃生蚝的于勒7 小时前
C语言内存函数
c语言·开发语言·数据结构·c++·学习·算法
workflower13 小时前
数据结构练习题和答案
数据结构·算法·链表·线性回归
一个不喜欢and不会代码的码农13 小时前
力扣105:从先序和中序序列构造二叉树
数据结构·算法·leetcode
No0d1es15 小时前
2024年9月青少年软件编程(C语言/C++)等级考试试卷(九级)
c语言·数据结构·c++·算法·青少年编程·电子学会