在数学的分支图论中,图(Graph)用于表示物件与物件之间的关系,是图论的基本研究对象。一张图由一些小圆点(称为顶点或结点)和连结这些圆点的直线或曲线(称为边)组成。西尔维斯特在1878年首次提出“图”这一名词。
图有多种变体,包括简单图、多重图、有向图、无向图等,但大体上有以下两种定义方式。
一张图 是一个二元组,其中称为顶点集,称为边集。它们亦可写成和。的元素是一个二元组数对,用表示,其中。
一张图 是一个三元组,其中称为顶集(Vertices set),称为边集(Edges set),与不相交;称为关联函数,将中的每一个元素映射到。如果那么称边连接顶点,而则称作的端点,此时关于相邻。同时,若两条边有一个公共顶点,则称关于相邻。
如果给图的每条边规定一个方向,那么得到的图称为有向图,其边也称为有向边。在有向图中,与一个节点相关联的边有出边和入边之分,而与一个有向边关联的两个点也有始点和终点之分。相反,边没有方向的图称为无向图。
一个图如果
若允许两结点间的边数多于一条,又允许顶点通过同一条边和自己关联,则为多重图的概念。它只能用“三元组的定义”。
一个不带权图中若两点不相邻,邻接矩阵相应位置为0,对带权图(网),相应位置为∞。一个图的邻接矩阵表示是唯一的,但其邻接表表示不唯一。在邻接表中,对图中每个顶点建立一个单链表(并按建立的次序编号),第i个单链表中的结点表示依附于顶点vi的边(对于有向图是以顶点vi为尾的弧)。每个结点由两个域组成:邻接点域(Adjvex),用以指示与vi邻接的点在图中的位置,链域(Nextarc)用以指向依附于顶点vi的下一条边所对应的结点。如果用邻接表存放网(带权图)的信息,则还需要在结点中增加一个存放权值的域(Info)。每个顶点的单链表中结点的个数即为该顶点的出度(与该顶点连接的边的总数)。无论是存储图或网,都需要在每个单链表前设一表头结点,这些表头结点的第一个域data用于存放结点vi的编号i,第二个域firstarc用于指向链表中第一个结点。
图的遍历方法有深度优先搜索法和广度(宽度)优先搜索法。
深度优先搜索法是树的先根遍历的推广,它的基本思想是:从图G的某个顶点v0出发,访问v0,然后选择一个与v0相邻且没被访问过的顶点vi访问,再从vi出发选择一个与vi相邻且未被访问的顶点vj进行访问,依次继续。如果当前被访问过的顶点的所有邻接顶点都已被访问,则退回到已被访问的顶点序列中最后一个拥有未被访问的相邻顶点的顶点w,从w出发按同样的方法向前遍历,直到图中所有顶点都被访问。其递归算法如下:
1 Boolean visited; // 访问标志数组 2 Status (*VisitFunc)(int v); // VisitFunc是访问函数,对图的每个顶点调用该函数 3 void DFSTraverse (Graph G, Status(*Visit)(int v)) { 4 VisitFunc = Visit; 5 for (v = 0; v < G.vexnum; ++v) 6 visited = FALSE; // 访问标志数组初始化 7 for (v = 0; v < G.vexnum; ++v) 8 if (!visited) 9 DFS(G, v); // 对尚未访问的顶点调用DFS10 }11 void DFS(Graph G, int v) { // 从第v个顶点出发递归地深度优先遍历图G12 visited = TRUE;13 VisitFunc(v); // 访问第v个顶点14 for (w = FirstAdjVex(G, v); w >= 0; w = NextAdjVex(G, v, w))15 // FirstAdjVex返回v的第一个邻接顶点,若顶点在G中没有邻接顶点,则返回空(0)。16 // 若w是v的邻接顶点,NextAdjVex返回v的(相对于w的)下一个邻接顶点。17 // 若w是v的最后一个邻接点,则返回空(0)。18 if (!visited)19 DFS(G, w); // 对v的尚未访问的邻接顶点w调用DFS20 }