图定义及概念
图是由若干给定的顶点及连接两顶点的边所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系。顶点用于代表事物,连接两顶点的边则用于表示两个事物间具有这种关系。
图中只包含两种类型的元素:顶点(Vertex)和边(Edge),所以图可以由顶点集合和边集合进行表示,即: G = ( V , E ) G=(V,E) G=(V,E)。根据边是否具有方向,可以将图分为有向图和无向图两种。
可以给边设置大小值即 权重,表示两个顶点之间连通的程度。
从一个顶点出发,到相邻顶点的边的个数称为该顶点的出度,以该顶点为终点的边的个数称为该顶点的入度。因为无向图的边不具有方向性,所以无向图中顶点的出度与入度相等。
从顶点集合 V V V 中选择 v 1 v_1 v1 作为起点, v 2 v_2 v2 作为终点,从起点出发到达终点的过程中,经过的边的集合称为路径,路径中边的个数称为路径长度。若路径中不重复经过一个顶点,则称为简单路径。若起点和终点是同一个顶点,则该路径也称之为回路。
对于无向图,若图中任意两个顶点之间存在路径,则该无向图为连通图。对于有向图,若图中任意两个顶点之间存在路径,则该有向图为强连通图。
极大连通图
- 连通图只有一个极大连通子图,就是它本身
- 非连通图有多个极大连通子图。(非连通图的极大连通子图叫做连通分量,每个分量都是一个连通图)
- 称为极大是因为如果此时加入任何一个不在图的点集中的点都会导致它不再连通。
极小连通图
- 连通图的极小连通子图也称之为生成树,即包含顶点集合 V V V,但是边的个数为 ∣ V ∣ − 1 |V|-1 ∣V∣−1。生成树可以有多个,经常提到的最小生成树,也就是带权连通图中权值之和最小的生成树。
- 之所以称为极小是因为此时如果删除一条边,就无法构成生成树,也就是说给极小连通子图的每个边都是不可少的
图的存储及表示方式
根据图的 稠密程度 选择存储结构,假设图有 N 个节点 E 条边,若:
E
<
<
N
2
E << N^2
E<<N2 则为交叉少的稀疏图使用邻接表存储只连接的节点,节省存储空间;使用邻接矩阵将要存储大量的 0
值,浪费空间。
E ≈ N 2 E ≈ N^2 E≈N2则为交叉多的稠密图,使用邻接矩阵将十分方便的查询连通性,较少的浪费存储空间。邻接表将查复杂度较高。
广度优先
广度优先遍历图的方式为,一次性访问当前顶点的所有未访问状态相邻顶点,并依次对每个相邻顶点执行同样处理。因为要依次对每个相邻顶点执行同样的广度优先访问操作,所以需要借助队列结构来存储当前顶点的相邻顶点。
广度优先遍历图的方式,是以一种类似波纹扩散的方式进行的,不断放大辐射半径,进而覆盖整张图。
- 实现方式
-
选择起始顶点放入队列,并标记为已访问;
-
当队列不为空时,从队列中取出顶点作为目标顶点,将目标顶点的所有相邻且未被访问过的顶点放入队列,并标记为已访问;
-
重复执行步骤 2。
广度优先遍历的形式为,选择目标顶点后,依次访问目标顶点的所有相邻顶点,再依次对每个相邻顶点,依次访问其相邻顶点,如此重复对顶点执行向外扩散的访问操作,直至图中所有顶点皆被访问,即存储顶点的队列为空,表示已经没有未被访问的顶点加入队列。
深度优先
深度优先遍历图的方式,同样会访问一个顶点的所有相邻顶点,不过深度优先的方式为,首先访问一个相邻顶点,并继续访问该相邻顶点的一个相邻顶点,重复执行直到当前正在被访问的顶点出度为零,或者不存在未访问状态的相邻顶点,则回退到上一个顶点继续按照该深度优先方式访问。因为存在回溯行为,所以需要借助栈结构保存顶点,或者直接利用递归调用产生的方法栈帧来完成回溯。
相对于广度优先访问,深度优先的方式更像是一条路走到黑,走不下去了再回到上个路口选择另外一条路。
- 实现方式
- 选择起始顶点入栈,并标记为已访问;
- 当栈不为空时,选择栈顶元素作为目标顶点,若目标顶点存在未访问状态的相邻顶点,则将该相邻顶点入栈,并标记为已访问;若不存在未访问状态的相邻顶点,则执行出栈操作;
- 重复执行步骤 2。
Go语言实现:https://github.com/TheLudlows/go-tour/tree/master/src/algorithm/graph