基本概念
基本概念
图的定义:图(Graph)一般由两个集合共同构成,一个是非空但是有限的顶点集合V(Vertex),另一个是描述顶点之间连接关系的边集合E(Edge),边集合可以为空集。G = (V, E)
端点和邻接点
顶点的度;入度和出度
完全图
子图
路径
简单路径
回路或环简单回路=简单路径+回路
连通、连通图、连通子图和连通分量
强连通图、强连通分量
权和网
连通图的连通分量只有一个(本身),而非连通图的连通分量不止一个;强连通图同理
存储结构
存储结构
邻接矩阵
存储元素:1,w,0,无穷
特点
唯一性
对于n个点顶的图,无论有无方向,都需n的平方个存储空间,所以邻接矩阵更适合存储边的数目较多的稠密图
无向图的邻接矩阵是一个对称矩阵,可以采用矩阵压缩的思想
对于无向图,第i行或第i列的非零元素和非无穷元素的个数是顶点i的度数
对于有向图,第i行(第i列)的非零元素和非无穷元素的个数是顶点i的出度数(入度数)
判断两个顶点的边是否存在或求取权值的执行效率是O(1),如果算法需要提取边的权值则建议采用该存储结构
邻接表
一种数组和链式相结合的存储方法
特点
表示不唯一
稀疏图更适合使用邻接表而不是邻接矩阵
逆邻接表
十字链表
邻接表与逆邻接表的结合,是有向图的另一种存储结构
步骤
1.每个顶点对应一个头节点结果为(data,firstIn,firstOut)
2.图中的每条边对应一个结点,结构为(起点,终点,起点相同的下一个边结点,终点相同的下一个边结点,边的信息(权值))
3.构造横向链表
4.构造纵向列表
优点:容易找到以v为起点或终点的边,也就是出度和入度
邻接多重表
无向图的另一种存储结构,与十字表类似
步骤
1.无向图的每个顶点对应一个头节点,结构为(data,firstarc)
2.每条边对应一个边结点,结构为(mark,i,ikink,j,jlink,weight),此时边<i,j>和<i,i>只有一个边结点
3.构造链表
图的遍历
图的遍历
定义:从任意顶点出发,按照某种算法沿着图的边访问图的所有结点,且每个节点只访问一次
深度优先遍历
从某个初始点V0出发,首先访问初始点V0,然后选择一个相邻的且没有访问过的点访问,再以该点为初始点进行深度优先遍历,直到与原始顶点V0的所有邻接点访问完为止,总的来说这是一个递归的过程
时间复杂度
邻接表:由于要遍历顶点的所有邻接点为O(n+e)
邻接矩阵:由于要遍历一个顶点的一行数据,为O(n*n)
广度优先遍历
首先访问初始节点,接着访问所有未被访问的邻接点,访问每个邻接点后,再访问该邻接点的所有未被访问的邻接点,依此类推
算法实现类似于二叉树的层次遍历,需要使用队列
时间复杂度
邻接表:由于要遍历顶点的所有邻接点为O(n+e)
邻接矩阵:由于要遍历一个顶点的一行数据,为O(n*n)
非连通图的遍历
无向图
在每个连通分量选择起始点分别进行遍历
有向图
重复选择不一样的初始点进行遍历,直到遍历完成
作用:可以判断该无向图是否连通
对于无权图来讲,广度遍历一定是最短路径,而深度不一定
生成树
生成树和最小生成树
主要针对无向图
生成树
有N-1 条边(其中N是顶点数)且每个顶点有且仅有一条路径相连,也就是包含原图全部N个顶点的极小连通子图
任意添加一条边必然构成一个环
分类
深度优先生成树
广度优先生成树
最小生成子树
给一个无向图的边都加上权值(网图)现在要求生成树边的权值总和最小
准则
1.必须使用原图的边来构造树
2.必须有且使用n-1条边来连接n个顶点
3.不能使用产生回路的边
算法
普利姆算法
从任意一个顶点开始,不断成长为一棵树,每次都会选择尽可能小的方向去进行延伸
时间复杂度为O(n*n),n为顶点个数,所以该算法的执行时间与e边数无关,适合用于稠密图求解最小生成树
克鲁斯卡尔算法
核心思想就是主动去选择权值较小的边
时间复杂度为elog2(e),所以该算法的执行时间与e边数有关和顶点数无关,适合稀疏图
最短路径
最短路径
单源最短路径
狄克斯特拉(Dijkstra)算法
时间复杂度O(n*n)
多源最短路路径
弗洛伊德算法
时间复杂度O(n*n*n)
关键路径
关键路径
顶点代表某个任务(事件),活动包括时间用边来表示,将边作为活动的图称为AOE图,每个事件对应着多个活动(多条边)
定义:在AOE网中,从源点到汇点的所有路径中,具有最大路径长度的路径
关键路径上的活动称为关键活动,关键活动不存在富裕的时间,而非关键的活动可能存在富裕的的时间
拓扑排序
拓扑排序
始终由前置条件来解锁后续,所以说整个图中是不可以出现循环的,构建出来的这种图称为有向无环图(DAG),就是个流程图,像这种顶点表示活动或任务的图也称为AOV图
拓扑排序(Topological Order)是指,将一个有向无环图(Directed Acyclic Graph)进行排序进而得到一个有序的线性序列
步骤
1.在有向图中找到入度为零的点输出
2.从图中删除该点和该点出发的所有有向边
3.重复上述两步,直到剩余图中不存在没有前驱的顶点
具体实现可以使用队列来写代码
实现代码:数据结构与算法: 记录学习笔记,包括代码和思维导图