| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 数据结构与算法 -> 数据结构——图 -> 正文阅读 |
|
[数据结构与算法]数据结构——图 |
目录 1.图的存储结构1.1.邻接矩阵
1.2.邻接表?可以将每一行都看成一个单链表,第一个结点的下标就是源点,其余结点的数据域就是源点的邻接点在顶点数组中的下标
2.图的创建2.1.用邻接矩阵存储图的算法实现(无向带权图)
2.2.用邻接表存储图的算法实现(无向带权图)
头插法:其实顶点表和每一个顶点和它的邻接点的都构成了一个单链表,顶点表的指针域存的就是首结点,然后将邻接点用头插法插入这个单链表中,即邻接点插入到首节点之前,每一个单链表的头结点就是图的顶点表的结点,第i个顶点对应的单链表的首结点就是G.vertiecs[i].firstarc,让s->next=G.vertiecs[i].firstarc,再改变头结点的指针域指向s,即s成为首节点。
3.图的遍历3.1.邻接矩阵的深度优先遍历(DFS)图的遍历就是从一个顶点出发,遍历图中的所有顶点,深度优先遍历也叫深度优先搜索,深度优先遍历就像树的前序遍历。它从图中某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。这里讲的是连通图,对于非连通图,只需要对它的连通分量分别进行深度优先遍历,即在先前一个顶点进行一次深度优先遍历后,若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作为起始点,重复上述过程,直至图中所有顶点都被访问到为止。
3.2.邻接表的深度优先遍历(DFS)
3.3.邻接矩阵的广度优先遍历(BFS)广度优先遍历有称作广度优先搜索。图的广度优先搜索就类似于树的层序遍历,需要借助队列来实现,对于每一个顶点,访问该顶点后访问它的所有邻接点,即一层层访问。
3.4.邻接表的广度优先遍历(BFS)
4.最小生成树一个连通图的生成树是一个极小的联通子图,它含有图中的全部顶点,但只有足以构成一棵树的n-1条边,我们把构造联通网的最小代价生成树称为最小生成树。 4.1.Prim算法Prime算法是从点的角度来考虑构造一个最小生成树,每次加入一个点,改点和已加入的点构成一条权最小的边,加入n个点的时候也有了n-1条边。假设加入最小生成树的点集合为U,未加入的点集合为V,初始时选择任意一个顶点加入集合U,假设该点为a,则U={a},然后从未加入U的点集合V中选一个距离a最近的顶点,假设为b,则U={a,b},然后再从V中选一个距离U中任意一个点最近的顶点,以此类推,直到选完n个顶点,则这n个顶点和n-1条边就构成最小生成树。 其实,每次选入最小生成树的顶点所构成的集合可以看成一个整体,然后我们看这个集合的所有顶点往外引出的边中权值最小的是哪一条,而这个边的终点便是我们要加入最小生成树的顶点 以下面的图为例: 用lowcost[i]表示以i为终点的边的最小权值,当lowcost[i]=0表示以i为终点的最小权值为0,也就是顶点i已经加入了最小生成树 用ajdvex[i]表示以adjvex[i]为起点,i为终点,即加入最小生成树的终点i与点adjvex[i]构成的边 则最小生成树第i条边可以表示为<adjvex[i],i>=lowcost[i] 我们以V0为起始点 ,则顶点V0加入生成树 初始时lowcost数组值为[0,10,*,*,*,11,*,*,*] adjvex为[0,0,0,0,0,0,0,0,0]? ? 最小生成树只有V0,所以找一个离V0最近的顶点,即V1加入最小生成树,则V0-V1这条边被加入最小生成树,?因为V1的加入,这个最小生成树中的点到其他点的最小距离也可能会改变,更新两个数组 lowcost数组的值为[0,0,18,*,*,11,16,*,12] adjvex数组为[0,1,0,0,0,0,1,0,1] 最小生成树已有V0和V1,我们找距离这两个点任意一个最小的顶点,其实就是找从V0和V1引出的边权值最小的,即V5加入最小生成树,边V0-V5权值最小,加入生成树,因为V5的加入,我们更新到最小生成树到其他顶点的距离 lowcost数组的值为[0,0,18,*,26,0,16,*,12] adjvex数组为[0,0,1,0,5,0,1,0,1] ? 最小生成树已有V0,V1,V5,我们找一个距离三个顶点任意一个权值最小的边,即V8,V1-V8加入最小生成树,由于V8的加入,我们更新其他顶点到最小生成树中顶点的距离??lowcost数组的值为[0,0,8,21,26,0,16,*,0] adjvex数组为[0,0,8,8,5,0,1,0,1] 最小生成树已有V0,V1,V5,V8,我们再从未加入最小生成树的顶点中找一个距离已加入最小生成树里的任意顶点权值最小的顶点,可知V2到V8这条边权值最小,V2加入最小生成树,V2-V8这条边加入最小生成树,由于V2的加入,?我们更新其他顶点到最小生成树的顶点距离??lowcost数组的值为[0,0,0,21,26,0,16,*,0] adjvex数组为[0,0,8,8,5,0,1,0,1] 最小生成树已有V0,V1,V5,V8,V2,我们再从未加入最小生成树的顶点中找一个距离已加入最小生成树里的任意顶点权值最小的顶点,可知V1到V6这条边权值最小,V6加入最小生成树,V1-V6这条边加入最小生成树,由于V6的加入,?我们更新其他顶点到最小生成树的顶点距离??lowcost数组的值为[0,0,0,21,26,0,0,19,0] adjvex数组为[0,0,8,8,5,0,1,6,1]? 最小生成树已有V0,V1,V5,V8,V2,V6,我们再从未加入最小生成树的顶点中找一个距离已加入最小生成树里的任意顶点权值最小的顶点,可知V6到V7这条边权值最小,V7加入最小生成树,V6-V7这条边加入最小生成树,由于V7的加入,?我们更新其他顶点到最小生成树的顶点距离??lowcost数组的值为[0,0,0,16,4,0,0,0,0] adjvex数组为[0,0,8,7,7,0,1,6,1]?? ? 最小生成树已有V0,V1,V5,V8,V2,V6,V7,我们再从未加入最小生成树的顶点中找一个距离已加入最小生成树里的任意顶点权值最小的顶点,可知V7到V4这条边权值最小,V4加入最小生成树,V7-V4这条边加入最小生成树,由于V4的加入,?我们更新其他顶点到最小生成树的顶点距离??lowcost数组的值为[0,0,0,16,0,0,0,0,0] adjvex数组为[0,0,8,8,7,0,1,6,1]??? ? 最小生成树已有V0,V1,V5,V8,V2,V6,V7,V4,我们再从未加入最小生成树的顶点中找一个距离已加入最小生成树里的任意顶点权值最小的顶点,可知V7到V3这条边权值最小,V4加入最小生成树,V7-V3这条边加入最小生成树,由于V3的加入,?我们更新其他顶点到最小生成树的顶点距离??lowcost数组的值为[0,0,0,0,0,0,0,0,0] adjvex数组为[0,0,8,8,7,0,1,6,1]???? ?现在,lowcost数组的值全部为0,表示所有的顶点均已加入最小生成树,则该图的最小生成树就出来啦,而adjvex这个数组里存的值代表什么意思呢,让我们分析一下adjvex[0]=0因为0是起始点,adjvex[1]=0因为我们加入最小生成树的边是V0-V1,adjvex[2]=8因为我们加入最小生成树的边是V2-V8,依次类推,便知道adjvex的作用啦,<adjvex[i],i>其实就是加入最小生成树的一条边。 算法实现:
根据本图输出样例: ? 4.3.Kruskal算法Kruskal算法是从边的角度构造最小生成树,每次选择一条权值最小的边加入最小生成树,直到找到n-1条边,则构造完成。但加入的边不仅要是最小,还要满足不何已加入最小生成树的边构成回路,这个条件可以用并查集来实现。此时我们要用到图的存储结构的边集数组。
我们用一个parent数组来存每个点的祖宗结点。初始化是parent数组全部为0 我们会有一个find函数来找传入的顶点的祖宗结点
我们还以下图为例来演示如何构造最小生成树?? ? ??选择第1条边,从未加入最小生成树的边中选择一条最小的边,由图可知,权值最小的边为V4-V7,而且这条边没有与最小生成树里的其他边构成回路,则加入最小生成树。在find函数里,先传入4,由于parent[4]=0,所以直接返回4,同样,传入7的时候也返回7了,4!=7即说明这条边的两个顶点原来不属于同一个集合,我们就可以将这条边加入最小生成树,并合并这两个集合,我们将以此边的结尾顶点放入下标为起点的parent数组里,即parent[4]=7,此时parent数组的值为[0,0,0,0,7,0,0,0,0] ?选择第2条边,从未加入最小生成树的边中选择一条最小的边,由图可知,权值最小的边为V2-V8,而且这条边没有与最小生成树里的其他边构成回路,则加入最小生成树。在find函数里,先传入2,由于parent[2]=0,所以直接返回2,同样,传入8的时候也返回8了,2!=8即说明这条边的两个顶点原来不属于同一个集合,我们就可以将这条边加入最小生成树,并合并这两个集合,我们将以此边的结尾顶点放入下标为起点的parent数组里,即parent[2]=8,此时parent数组的值为[0,0,8,0,7,0,0,0,0] ? 选择第3条边,从未加入最小生成树的边中选择一条最小的边,由图可知,权值最小的边为V0-V1,而且这条边没有与最小生成树里的其他边构成回路,则加入最小生成树。在find函数里,先传入0,由于parent[0]=0,所以直接返回0,同样,传入1的时候也返回1了,0!=1即说明这条边的两个顶点原来不属于同一个集合,我们就可以将这条边加入最小生成树,并合并这两个集合,我们将以此边的结尾顶点放入下标为起点的parent数组里,即parent[0]=1,此时parent数组的值为[1,0,8,0,7,0,0,0,0]? ?选择第4条边,从未加入最小生成树的边中选择一条最小的边,由图可知,权值最小的边为V0-V5,而且这条边没有与最小生成树里的其他边构成回路,则加入最小生成树。在find函数里,先传入0,由于parent[0]=1,parent[1]=0,所以返回1,同样,传入5的时候parent[5]=0,返回5了,1!=5即说明这条边的两个顶点原来不属于同一个集合,我们就可以将这条边加入最小生成树,并合并这两个集合,我们将以此边的结尾顶点放入下标为起点的parent数组里,即parent[1]=5,此时parent数组的值为[1,5,8,0,7,0,0,0,0]? ?选择第5条边,从未加入最小生成树的边中选择一条最小的边,由图可知,权值最小的边为V1-V8,而且这条边没有与最小生成树里的其他边构成回路,则加入最小生成树。在find函数里,先传入1,由于parent[1]=5,parent[5]=0,所以返回5,同样,传入8的时候parent[8]=0,返回8了,5!=8即说明这条边的两个顶点原来不属于同一个集合,我们就可以将这条边加入最小生成树,并合并这两个集合,我们将以此边的结尾顶点放入下标为起点的parent数组里,即parent[5]=8,此时parent数组的值为[1,5,8,0,7,8,0,0,0]? 选择第6条边,从未加入最小生成树的边中选择一条最小的边,由图可知,权值最小的边为V3-V7,而且这条边没有与最小生成树里的其他边构成回路,则加入最小生成树。在find函数里,先传入3,由于parent[3]=0,所以返回3,同样,传入7的时候parent[7]=0,返回7了,3!=7即说明这条边的两个顶点原来不属于同一个集合,我们就可以将这条边加入最小生成树,并合并这两个集合,我们将以此边的结尾顶点放入下标为起点的parent数组里,即parent[3]=7,此时parent数组的值为[1,5,8,7,7,8,0,0,0]?? ? 选择第7条边,从未加入最小生成树的边中选择一条最小的边,由图可知,权值最小的边为V1-V6,而且这条边没有与最小生成树里的其他边构成回路,则加入最小生成树。在find函数里,先传入1,由于parent[1]=5,parent[5]=8,parent[8]=0,所以返回8,同样,传入6的时候parent[6]=0,返回6了,8!=6即说明这条边的两个顶点原来不属于同一个集合,我们就可以将这条边加入最小生成树,并合并这两个集合,我们将以此边的结尾顶点放入下标为起点的parent数组里,即parent[8]=6,此时parent数组的值为[1,5,8,7,7,8,0,0,6]??? ? 选择第8条边,从未加入最小生成树的边中选择一条最小的边,由图可知,权值最小的边为V5-V6,但这条边与加入生成树的其他边构成回路了,不能加入最小生成树。在find函数里,先传入5,由于parent[5]=8,parent[8]=6,parent[6]=0,所以返回6,同样,传入6的时候parent[6]=0,返回6了,6=6即说明这条边的两个顶点属于同一个集合,我们就不可以将这条边加入最小生成树 那就要重新选择第8条边,由图可知,除了已加入最小生成树的边和V5-V6这条边,权值最小的边是V6-V7,且这条边不与最小生成树的其他边构成回路,故可以加入最小生成树。在find函数里,先传入6,parent[6]=0,故返回6,同样传入7的时候,parent[7]=0返回7,6!=7即说明这条边的两个顶点不属于同一个集合,我们就可以将这条边加入最小生成树,并合并这两个集合,我们将以此边的结尾顶点放入下标为起点的parent数组里,即parent[6]=7,此时parent数组的值为[1,5,8,7,7,8,7,0,6]???? ?到此,我们就找到了n-1条边,且将所有顶点都加入了最小生成树。 算法实现:
?根据本图的输出样例: ?5.最短路径5.1.DijKstra算法该算法是典型的单源最短路算法,可以求源点到其余各点的最短路径。从源点出发,先找到一个距离源点最近的顶点,然后根据这个新加入的顶点去更新源点到其他点的距离,比如说如果a到b的距离是4,a到c的距离是6,b到c的距离是1,如果直接从a到c的距离是大于从a到b在到c的,那我们就可以通过b来更新a到c的最短路径,而且因为每次我们都选的是目前所能到达的边里最短的,基于贪心,我们最后到达每一个顶点的路径一定都是最短的。 用D[i]表示从源点到顶点i的最短路径,用final[i]=1表示已求出源点到顶点i的最短路径中,用P[i]表示从源点到顶点i的最短路径要进过的前驱顶点。初始时用v0到各点的距离来初始化D数组,所以P数组全部初始化为0,即都是从V0顶点直接到达的,final数组全部赋值为0. 以下图为例来模拟DijKstra算法 ?我们以V0为源点来求V0到各点的最短路径 ?则D数组的值为[0,1,5,*,*,*,*,*,*],final数组的值[1,0,0,0,0,0,0,0,0] P数组的值[0,0,0,0,0,0,0,0,0] 距离源点最近的很明显就是1,并求出了V0到V1的最短路径就是1,故应将此顶点的final值设为1,并根据V1的加入来修改V0到其他点的最短路径,比如说V0到V5的路径本来是5,但当从V0-V1-V5时,最短路径变成了1+3=4,这就是我们每次加入一个顶点就以此顶点为源点更新其他路径的原因,此次更新的还要顶点V3,V4,因为本来V0到V3和V4的距离是无穷大的,但此时因为V1的加入,这两点是可达的了。而且顶点V2,V3,V4的前驱都应该是1。 则D数组的值为[0,1,4,8,6,*,*,*,*],final数组的值[1,1,0,0,0,0,0,0,0] P数组的值[0,0,1,1,1,0,0,0,0] 然后再从D数组里找一条距离V0权值最小的顶点,由于V0和V1已求出最短路径,则不参与最小权值的比较了,这样就可以得到剩下的里面权值最小的是4,对应的顶点小标2,即顶点V2加入,则说明求出了V0到V2的最短路径就是4,修改final[2]=1,再根据加入的V2更新V0到其他点的最短距离,原本V0到V5的最短距离是无穷,由于V2的加入,最短距离更新为4+7,本来V0到V4的最短距离是从V0-V1-V4为6,但现在可以从V0-V1-V2-V4为,5,且这两个顶点的前驱都是2 则D数组的值为[0,1,4,8,5,11,*,*,*],final数组的值[1,1,1,0,0,0,0,0,0] P数组的值[0,0,1,1,2,2,0,0,0] ? 然后再从D数组里找一条距离V0权值最小的顶点,由于V0和V1还有V2已求出最短路径,则不参与最小权值的比较了,这样就可以得到剩下的里面权值最小的是5,对应的顶点下标4,即顶点V4加入,则说明求出了V0到V4的最短路径就是5,修改final[4]=1,再根据加入的V4更新V0到其他点的最短距离,原本V0到V6,V7的最短距离是无穷,由于V4的加入,最短路径变成了5+6,5+9,本来V0到V5的最短距离是11,现在经过V4,最短路径变成了5+3=8,本来V0到V3的最短路径是8,现在,最短路径变成了5+2=7,故更新这几个顶点到V0的最短距离并将前驱改为4 则D数组的值为[0,1,4,7,5,8,11,14,*],final数组的值[1,1,1,0,1,0,0,0,0] P数组的值[0,0,1,4,2,4,4,4,0] 然后再从D数组里找一条距离V0权值最小的顶点,由于V0、V1、V2、V4已求出最短路径,则不参与最小权值的比较了,这样就可以得到剩下的里面权值最小的是7,对应的顶点下标3,即顶点V3加入,则说明求出了V0到V3的最短路径就是7,修改final[3]=1,再根据加入的V3更新V0到其他点的最短距离,原本V0到V6的最短路径是11,现在经过V3从V0到V6的最短路径是7+3=10,将顶点6的前驱修改为3 则D数组的值为[0,1,4,7,5,8,10,14,*],final数组的值[1,1,1,1,1,0,0,0,0] P数组的值[0,0,1,4,2,4,3,4,0] 然后再从D数组里找一条距离V0权值最小的顶点,由于V0、V1、V2、V4、V3已求出最短路径,则不参与最小权值的比较了,这样就可以得到剩下的里面权值最小的是8,对应的顶点下标5,即顶点V5加入,则说明求出了V0到V5的最短路径就是8,修改final[5]=1,再根据加入的V5更新V0到其他点的最短距离,原本V0到V7的最短路径是14,但由于V5的加入,最短路径变成了8+5=13,将顶点V7的前驱修改为5 则D数组的值为[0,1,4,7,5,8,10,13,*],final数组的值[1,1,1,1,1,1,0,0,0] P数组的值[0,0,1,4,2,4,3,5,0] ? ? 然后再从D数组里找一条距离V0权值最小的顶点,由于V0、V1、V2、V4、V3、V5已求出最短路径,则不参与最小权值的比较了,这样就可以得到剩下的里面权值最小的是10,对应的顶点下标6,即顶点V6加入,则说明求出了V0到V6的最短路径就是10,修改final[6]=1,再根据加入的V6更新V0到其他点的最短距离,原本V0到V8的最短路径为 无穷,但由于V6的的加入变成了10+7=17,原本V0到V7的最短路径是13,但由于V6的加入变成了10+2=12,故更新源点到这两个顶点的最短路径并将前驱修改为6 则D数组的值为[0,1,4,7,5,8,10,12,17],final数组的值[1,1,1,1,1,1,1,0,0] P数组的值[0,0,1,4,2,4,3,6,6] 然后再从D数组里找一条距离V0权值最小的顶点,由于V0、V1、V2、V4、V3、V5、V6已求出最短路径,则不参与最小权值的比较了,这样就可以得到剩下的里面权值最小的是12,对应的顶点下标7,即顶点V7加入,则说明求出了V0到V7的最短路径就是12,修改final[7]=1,再根据加入的V7更新V0到其他点的最短距离,原本V0到V8的最短路径是17,但是由于V7的加入变成了12+4=16,故修改最短路径并将V8的前驱改为7? 则D数组的值为[0,1,4,7,5,8,10,12,16],final数组的值[1,1,1,1,1,1,1,1,1] P数组的值[0,0,1,4,2,4,3,6,7] ? 然后再从D数组里找一条距离V0权值最小的顶点,由于V0、V1、V2、V4、V3、V5、V6、V7已求出最短路径,则不参与最小权值的比较了,这样就可以得到剩下的里面权值最小的是16,对应的顶点下标8,即顶点V8加入,则说明求出了V0到V8的最短路径就是16,修改final[8]=1,此时所有的顶点均已求出最短路径,不用继续更新了 则D数组的值为[0,1,4,7,5,8,10,12,16],final数组的值[1,1,1,1,1,1,1,1,0] P数组的值[0,0,1,4,2,4,3,6,7] 至此,我们就求出了V0到其他点的最短路径了,保存在了D数组里,那P数组是什么呢,让我们来分析一下,就拿8来说,P[8]=7,说明从V0到V8的前驱顶点是V7,P[7]=6说明从V0到V7的最短路径前驱顶点是V6,P[6]=3说明从V0到V6的最短路径的前驱顶点是V3,P[3]=4,说明从V0到V3的前驱顶点是V4,P[4]=2说明从V0到V4的前驱顶点是V2,P[2]=1说明从V0到V2的前驱顶点是1,P[1]=0说明从V0到V1的前驱就是V0,这样我们就逆着推出了从V0到V8的最短路径经过的顶点V0->V1->V2->V4->V3->V6->V7->V8? ?算法实现:
5.2.Floyd算法该算法可用来所有顶点到所有顶点的最短路径,该算法是基于动态规划来求解的。 数组D[i][j]表示顶点i到j的最短路径,P[i][j]表示从顶点i到顶点j的最短路径的要经过前驱 直接用图的邻接矩阵将D数组初始化,而从顶点i到顶点j的前驱都是j本身,也就是相当于直接从Vi到达Vj。然后我们用一个三重循环来求最短路径,k代表的就是中转顶点的下标,v代表的就是起始顶点,w代表的就是结束顶点。对于顶点i,j,如果从i到k再到j的路径比直接从i到j的最短路径短,我们就更新从i到j的最短路径,起始与DijKstra根据已求出最短路径的顶点k去更新源点到其余顶点的最短路径一样,只不过该算法不一定是根据已求出的最短路径来更新,是任意顶点到任意顶点的最短路径都要用所有顶点作为中转顶点来更新一下。 这个P数组如何记录路径的呢,还是以V8为例,P[0][8]=1,表示从V0到V8要经过V1,然后用1取代0得到P[1][8]=2,说明从V1到V8要经过顶点V2,用2取代1得到P[2][8]=4,说明从V2到V8要经过顶点V4,。。。这样就很容易得到最终的路径值:V0->V1-V2-V4->V3->V6->V7->V8
5.3.边的权值相同的图的最短路径(广度优先搜索)对于无权图或者所有边的权值相同的如,求最短路径就是经过的边数,最短路径就是经过最少的边,因此可以考虑用广度优先搜索来实现,因为广度优先搜索是一层层来搜索,求从i到j的最短路径,只要中层序遍历的过程,遍历到了j,此时求得的路径就是最短的。一次只要稍微修改一下图的广度优先搜索就能找到i到j的最短路径啦,但是为了知道路径,我们要借助一个辅助数组front来保存遍历时经过的顶点的前驱其实就是上一层的顶点,所以每一个顶点到源点的最短路径其实就是上一层顶点到源点的顶点数+1(这里我们假设所有边的权值都是1); 以无权图为例来求最短路径,初始时front数组的值为-1,dis数组的值为-1,队列queue为空,假设我们要求V0到V7的最短路径。 ?首先,将V0入队,然后开始广度优先搜索,V0的前驱是0,使用front[0]=0,V0到V0的最短路径是0,所以dis[0]=0,队列queue=[0]; 然后当队列不空,就将队首元素出队,遍历队首的邻接点,将所有邻接点入队并修改邻接点的front和dis。V0出队,V0的邻接点V1,V5入队,且V1,V5的前驱是V0,V1和V5到V0的顶点数就是V0到V0的路径长度+1,front数组的值[0,0,-1,-1,-1,0,-1,-1,-1],dis数组的值[0,1,-1,-1,-1,1,-1,-1,-1] queue=[1,5]。因为1和5都不是我们要找的终点,所以继续。 ? ?队列不空,队首出队,1出队,遍历1所有邻接点,将未被访问过的邻接点V2,V8,V6入队,这几个顶点的前驱都是V1,所以这几个顶点到V0的路径就是V1到V0的路径加一,为2。front数组的值[0,0,1,-1,-1,-1,1,-1,1],dis数组的值[0,1,2,-1,-1,-1,2,-1,2] queue=[5,2,8,6]。因为2,8和6都不是我们要找的终点,所以继续。 ? 队列不空,将队首元素出队,即5出队,将5的所有未被访问的邻接点入队,即V7和V4入队,所以V7和V4的前驱都是V5,则V7和V4到V0的最短路径就是V5到V0的最短路径加一,即1+1=2此时?front数组的值[0,0,1,-1,5,-1,1,5,1],dis数组的值[0,1,2,-1,2,-1,2,2,2] queue=[2,8,6,7,4]。我们已经遍历到了我们要找的终点7,所以此次遍历结束,我们得到了V0到V7的最短路径就是D[7]=2。 那怎么从front数组得到遍历的过程的最短路径呢,就那V0到V7来说,front[7]=5,所以说明从V0到V7要经过的前驱是V5,再有front[5]=0可以知道,从V0到V5的最短路径的前驱就是V0,所以从V0到V7的最短路径是V0->V5->V7? 算法实现:
6.拓扑排序在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,称为AOV网。AOV网中的弧表示活动之间存在的某种制约关系。设G=<V,E>是一个具有n个顶点的有向图,V中的的顶点序列v1,v2,...,vn满足若从Vi到Vj有一条路径,则在顶点序列中顶点Vi必在顶点Vj之前。则我们称这样的顶点序列为一个拓扑序列 所谓拓扑排序就是对一个有向图构造拓扑序列的过程 基本思路:每次从AOV网中选择一个入度为0的顶点,输出后删除该顶点以及以此顶点为尾的弧,重复此步骤,直到输出全部顶点或者AOV网中不存在入度为0的顶点为止。 由于拓扑排序要删除顶点,所以我们用邻接表来存储,在原来的顶点表中增加一个入度,存储结构如下:
因为要从入度为0的顶点开始,为了不每次都遍历顶点表,用一个辅助栈来存放入度为0的顶点。 算法实现步骤:
算法实现:
?今天整理了数据结构图论的部分算法,希望能帮到在学数据结构的你呀~原创不易,如转载,注明出处,有错误也欢迎指正。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/26 10:15:32- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |