首页 > 其他分享 >数据结构(六)——图的应用

数据结构(六)——图的应用

时间:2024-04-04 13:32:23浏览次数:12  
标签:数据结构 拓扑 路径 算法 排序 应用 顶点 活动

6.4 图的应用

6.4.1 最小生成树

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

最小生成树可能有多个,但边的权值之和总是唯一且最小的
最小生成树的边数=顶点数–1。砍掉一条则不连通,增加一条边则会出现回路

求最小生成树
Prim算法(普里姆):
从某一个顶点开始构建生成树;
每次将代价最小的新顶点纳入生成树,
直到所有顶点都纳入为止。

Kruskal算法(克鲁斯卡尔):
每次选择一条权值最小的边,使这条边的两头连通(原本已经连通的就不选)
直到所有结点都连通


Prim算法的实现思想
第1轮:循环遍历所有个结点,找到lowCost最低的,且还没加入树的顶点
第2轮︰循环遍历所有个结点,找到lowCost最低的,且还没加入树的顶点
第3轮:循环遍历所有个结点,找到lowCost最低的,且还没加入树的顶点
第4轮:循环遍历所有个结点,找到lowCost最低的,且还没加入树的顶点
第5轮:循环遍历所有个结点,找 到lowCost最低的,且还没加入树的顶点

Kruskal 算法的实现思想
第1轮:检查第1条边的两个顶点是否 连通(是否属于同⼀个集合)
第2轮:检查第2条边的两个顶点是否 连通(是否属于同⼀个集合)
第3轮:检查第3条边的两个顶点是否 连通(是否属于同⼀个集合)
第4轮:检查第4条边的两个顶点是否 连通(是否属于同⼀个集合)
第5轮:检查第5条边的两个顶点是否 连通(是否属于同⼀个集合)
第6轮:检查第6条边的两个顶点是否 连通(是否属于同⼀个集合)

6.4.2_1 最短路径问题_BFS算法


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

#define MAX_LENGTH 2147483647			//地图中最大距离,表示正无穷

//求顶点u到其他顶点的最短路径
void BFS_MIN_Disrance(Graph G,int u){
    //d[i]表示从u到i结点的最短路径
    for(i=0; i<G.vexnum; i++){
        visited[i]=FALSE;				//初始化访问标记数组
        d[i]=MAX_LENGTH;				//初始化路径长度
        path[i]=-1;						//初始化最短路径记录
    }
    InitQueue(Q);						//初始化辅助队列
    d[u]=0;
    visites[u]=TREE;
    EnQueue(Q,u);
    while(!isEmpty[Q]){					//BFS算法主过程
        DeQueue(Q,u);					//队头元素出队并赋给u
        for(w=FirstNeighbor(G,u);w>=0;w=NextNeighbor(G,u,w)){
            if(!visited[w]){            //w为u的尚未访问的邻接顶点
                d[w]=d[u]+1;            //路径长度+1
                path[w]=u;              //最短路径应从u到w
                visited[w]=TREE;        //设已访问标记
                EnQueue(Q,w);			//顶点w入队
            }
        }
    }
}

6.4.2_2 最短路径问题_Dijkstra算法

使用 Dijkstra算法求最短路径问题,需要使用三个数组:
final[]数组用于标记各顶点是否已找到最短路径。
dist[]数组用于记录各顶点到源顶点的最短路径长度。
path[]数组用于记录各顶点现在最短路径上的前驱。

算法思想:循环遍历所有结点,找到还没确定最短路径、且dist最小的顶点Vi,令final[i]=ture。
检查所有邻接自Vi的顶点,若其final值为false,则更新dist和path信息。

#define MAX_LENGTH = 2147483647;

// 求顶点u到其他顶点的最短路径
void BFS_MIN_Disrance(Graph G,int u){
    for(int i=0; i<G.vexnum; i++){		//初始化数组
        final[i]=FALSE;
        dist[i]=G.edge[u][i];
        if(G.edge[u][i]==MAX_LENGTH || G.edge[u][i] == 0)
            path[i]=-1;
        else
            path[i]=u;
        final[u]=TREE;
    }
 
  	for(int i=0; i<G.vexnum; i++){
        int MIN=MAX_LENGTH;
        int v;
		// 循环遍历所有结点,找到还没确定最短路径,且dist最⼩的顶点v
        for(int j=0; j<G.vexnum; j++){
	        if(final[j]!=TREE && dist[j]<MIN){
 	            MIN = dist[j];
                v = j;
            }
        }
        final[v]=TREE;
        // 检查所有邻接⾃v的顶点路径长度是否最短
        for(int j=0; j<G.vexnum; j++){
	        if(final[j]!=TREE && dist[j]>dist[v]+G.edge[v][j]){
            	dist[j] = dist[v]+G.edge[v][j];
                path[j] = v;
            }
        }
	}
}



Dijkstra算法能够很好的处理带权图的单源最短路径问题,但不适⽤于有负权值的带权图。

6.4.2_3 最短路径问题_Floyd算法

Floyd算法:求出每⼀对顶点之间的最短路径,使⽤动态规划思想,将问题的求解分为多个阶段。

Floyd算法使用到两个矩阵:
dist[]:目前各顶点间的最短路径。
path[]:两个顶点之间的中转点。

//...准备工作,根据图的信息初始化矩阵A和path
	for(k=0;k<n;k++){                //考虑以Vk作为中转点
		for(i=0;i<n;i++){            //遍历整个矩阵,i为行号,j为列号
			for(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算法不能解决带有“负权回路”的图(有负权值的边组成回路),这种图有可能没有最短路径

最短路径算法比较:

BFS算法Dijkstra算法 Floyd算法
无权图
带权图
带负权值的图
带负权回路的图
时间复杂度O(|V|^2)或O(|V|+|E|)O(|V|^2)O(|V|^3)
通常用于求无权图的单源最短路径 求带权图的单源最短路径求带权图中各顶点间的最短路径

6.4.3 有向无环图描述表达式

有向无环图:若一个有向图中不存在环,则称为有向无环图,简称DAG图(Directed Acyclic Graph)




      
解题方法


练习

6.4.4 拓扑排序

AOV网(Activity on vertex Network,用顶点表示活动的网):
用DAG图(有向无环图)表示一个工程。顶点表示活动,有向边<Vi,Vj>表示活动Vi必须先于活动Vj进行

拓扑排序︰在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序:
①每个顶点出现且只出现一次。
②若顶点A在序列中排在顶点B的前面,则在图中不存在从顶点B到顶点A的路径。

或定义为:拓扑排序是对有向无环图的顶点的一种排序,它使得若存在一条从顶点A到顶点B的路径,则在排序中顶点B出现在顶点A的后面。每个AOV网都有一个或多个拓扑排序序列。

拓扑排序的实现:
① 从AOV网中选择⼀个没有前驱(入度为0)的顶点并输出。
② 从网中删除该顶点和所有以它为起点的有向边。
③ 重复①和②直到当前的AOV网为空或当前网中不存在无前驱的顶点为止


代码实现拓扑排序

#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是以邻接表存储的图类型

// 对图G进行拓扑排序
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)){				//栈不空,则存入
        Pop(S,i);					//栈顶元素出栈
        print[count++]=i;			//输出顶点i
        for(p=G.vertices[i].firstarc;p;p=p=->nextarc){
            //将所有i指向的顶点的入度减1,并将入度为0的顶点压入栈
            v=p->adjvex;
            if(!(--indegree[v]))
                Push(S,v);			//入度为0,则入栈
        }
    }
    if(count<G.vexnum)
        return false;				//排序失败,有向图中有回路
    else
        return true;				//拓扑排序成功
}

拓扑排序 每个顶点都需要处理一次 每条边都需要处理一次
时间复杂度:O(|V|+|E|)
若采用邻接矩阵,则需O(|V^2|)

逆拓扑排序
对一个AOV网,如果采用下列步骤进行排序,则称之为逆拓扑排序:
①从AOV网中选择一个没有后继(出度为O)的顶点并输出。
②从网中删除该顶点和所有以它为终点的有向边。
③重复①和②直到当前的AOV网为空。

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

void DFSTraverse(Graph G){    //对图G进行深度优先遍历
    for(v=0; v<G.vexnum;++V)
        visited[v]=FALSE;     //初始化已访问标记数据
    for(v=;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);
        }//if
    print(v);                //输出顶点
}

6.4.5 关键路径

AOE 网:在带权有向图中,以顶点表示事件,以有向边表示活动,以边上的权值表示完成该活动的开销(如完成活动所需的时间),称之为用边表示活动的网络,简称 AOE 网 (Activity On Edge NetWork)。

AOE网具有以下两个性质:
①只有在某顶点所代表的事件发⽣后,从该顶点出发的各有向边所代表的活动才能开始;
②只有在进⼊某顶点的各有向边所代表的活动都已结束时,该顶点所代表的事件才能发⽣。 
在 AOE 网中仅有⼀个入度为 0 的顶点,称为开始顶点(源点),它表示整个⼯程的开始; 也仅有⼀个出度为 0 的顶点,称为结束顶点(汇点),它表示整个⼯程的结束。

在AOE网中,有些活动是可以并行进行的。从源点到汇点的有向路径可能有多条,所有路径中,具有最大路径长度的路径称为关键路径,而把关键路径上的活动称为关键活动。完成整个工程的最短时间就是关键路径的长度,若关键活动不能按时完成,则整个工程的完成时间就会延长。

寻找冠军活动时所用到的几个参量的定义:

1. 事件 vk的最早发生时间 ve(k):决定了所有从vk 开始的活动能够开工的最早时间。
2. 事件 vk的最迟发⽣时间 vl(k):它是指在不推迟整个工程完成的前提下,该事件最迟必须发⽣的时间。
3.活动ai的最早开始时间e(i):指该活动弧的起点所表示的事件的最早发生时间。
4.活动ai的最迟开始时间l(i):它是指该活动弧的终点所表示事件的最迟发生时间与该活动所需时间之差。
5.活动 ai的时间余量d(i)=l(i)−e(i),表示在不增加完成整个⼯程所需总时间的情况下,活动ai可以拖延的时间若⼀个活动的时间余量为零,则说明该活动必须要如期完成,d(i)=0 即l(i)=e(i) 的活动ai是关键活动,由关键活动组成的路径就是关键路径。

求关键路径的步骤:
1.求所有事件的最早发生时间ve():按拓扑排序序列,依次求各个顶点的ve(k), ve(源点)=0, ve(k) =Max{vef)+W eight(vj , vk )},vj为vk的任意前驱。
2.求所有事件的最迟发生时间vl():按逆拓扑排序序列,依次求各个顶点的 vl(k),vl(汇点)= ve(汇点),vl(k)=Min{vl(j)- W eight(vk , vi)},v为Vv\k 的任意后继。
3.求所有活动的最早发生时间e():若边<Vk ,Vj>表示活动a;,则有e(i)=ve(k)。
4.求所有活动的最迟发生时间1():若边<Vk,Vj >表示活动a,则有l(i)= vl(j)- Weight(vi , vj)。
5.求所有活动的时间余量d(): d(i)= l(i)-e(i)。

关键活动、关键路径的特性:
1.若关键活动耗时增加,则整个工程的工期将增长。
2.缩短关键活动的时间,可以缩短整个工程的工期。
3.当缩短到一定程度时,关键活动可能会变成非关键活动。
4.可能有多条关键路径,只提高一条关键路径上的关键活动速度并不能缩短整个工程的工期,只有加快那些包括在所有关键路径上的关键活动才能达到缩短工期的目的。

标签:数据结构,拓扑,路径,算法,排序,应用,顶点,活动
From: https://blog.csdn.net/a_rain2333/article/details/137247944

相关文章

  • 循环控制语句的实际应用(2)
    3193:【例32.2】凑数时间限制:1000ms      内存限制:65536KB提交数:3351   通过数:2377【题目描述】现有两个正整数 x,y,试问你在 1000 以内最大既不是 x 的倍数,也不是 y 的倍数的正整数是多少?【输入】只有两个整数 x,y(2≤x,y≤1000)。......
  • 机械识别技术在懂车帝SEO排名代发中的应用与优势
    机械识别技术在懂车帝SEO排名代发中的应用与优势机械行业在懂车帝如何做SEO布局#seo优化seo排名关键词排名#干货分享#短视频运营欢迎大家来到百收网SEO课堂,我是狂潮老师,那么我们第三节课讲的是网页,必须符合机械识别啊。那么在这里再次提醒一下,如果同学们你们的基础不好,那......
  • CHC5223数据结构和算法
    CHC5223数据结构和算法2023-2024第2学期第1页,共4页课业1价值40%的课程个人工作学习成果学生将能够理解:1.1数据结构1.2数据结构的应用1.3面向对象编程概念1.4程序测试方法学生将掌握以下方面的技能:2.1数据抽象2.2数据结构的使用2.3使用高级面向对象语言进行更高级的编程2.4程序......
  • 数据结构——从入门到飞升——两个有序链表的合并
    首先,我们要知道sort()函数的使用方法:1.需要函数头#include2.sort(begin,end,cmp)begin:指向待分类元素的第一个指针end:指向待分类元素最后一个的指针其中end-begin是所有数的数量cmp:表示排序的样式,没有就是默认从小到大排要是想从大到小排,可写成greater,int也可以写成别的......
  • 散列表的数据结构以及对象在JVM堆中的存储过程
    【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)https://www.cnblogs.com/cnb-yuchen/p/18032068出自【进步*于辰的博客】参考笔记二,P67、P68.1。目录1、什么是“散列表”?2、关于对象存储过程2.1加载过程2.2注意事项3、Hashtable扩容机制3.1扩容机制是什么......
  • 更相减损术的应用
    题目示例input16212313671210012868968106811681268136814681568166813146819941131994712outputCase#1:IakaCase#2:YuwgnaCase#3:YuwgnaCase#4:IakaCase#5:IakaCase#6:IakaCase#7:YuwgnaCase#8:Y......
  • 数据结构——线段树
    本来是想先写树状数组的,但想到要在树状数组里面用到这篇文章,就先写这个了。线段树的思想线段树是在用一颗树存一个数组中的所有数以及他们所有的2的次方数长度的区间,具体样子如下:                      [1~8]     ......
  • AI大模型工具应用实战篇之绘画
    1、核心说明提示词最好用英文、用词语、短语交流更方便大模型理解2、万能公式主体描述:主体目标,比如:兔子、猫、西瓜、老人、小孩内容描述:在哪里,在干什么、穿什么、戴什么等绘画类型:水墨画、油画、素描、速写、水彩画、版画、漫画、插画绘画风格:赛博朋克风格、现代风格......
  • JS Graph (图-数据结构)
    Code:/***基于邻接表实现的无向图*@class*/classGraphAdjList{/***@type{Map<any,any[]>}*/adjList;/***构造函数*@constructor*@param{[any,any][]}edges*/constructor(edges){this.a......
  • 面试了微软 bing 应用组大模型算法岗,被自己菜哭了。。。
    节前,我们组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、参加社招和校招面试的同学,针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何备战、面试常考点分享等热门话题进行了深入的讨论。合集在这里:《大模型面试宝典》(2024版)正式发......