图
1.图的定义和表示
图的定义
图G由集合V和集合E组成,记作G=(V,E),其中:
1、V是顶点元素的有限集合;
2、E是顶点间关系——边的有限集合。
3、边是顶点的无序对或有序对。
无向图和有向图:
无向图
由没有方向的边构成的图。
无向图中的边由顶点的无序对组成。(用圆括号表示)
邻接点:无向图中,若存在一条边(Vi,Vj),则称Vi和Vj互为邻接点。
上图中的边是无方向的,即(V1,V2)和(V2,V1)表示同一条边。
有向图
由有方向的边构成的图。
弧:有向图中的边由顶点的有序对组成,也称为弧。(用尖括号表示)
有向图中,顶点的有序对<Vi,Vj>表示从Vi指向Vj的一条有向边,其中Vi是起点,Vj是终点。
上图中的边是有方向的,<V1,V2>和<V2,V1>表示两条不同的边。
图的表示
上图G1中:
V(G1)={1,2,3,4,5,6,7}
E(G1)={(1,2),(1,3),(2,3),(2,4),(2,5),(5,6),(5,7)}
上图G2中:
V(G2)={1,2,3,4,5,6}
E(G2)={<1,2>,<2,1>,<2,3>,<2,4>,<3,5>,<5,6>,<6,3>}
完全图与子图
完全图:图中任意两项点间均有边直接相连。
无向完全图:有n个顶点和n(n-1)/2条边的无向图。
有向完全图:有n个顶点和n(n-1)条弧的有向图。
子图:对于图G=(V,E)和图G'=(V',E'),若存在V'∈V且E'∈E,则称图G'是图G的子图。
2.图的相关概念
顶点的度
无向图中,顶点的度是与每个顶点相连的边数。
有向图中,顶点的度分成入度与出度。
入度:以该顶点为终点的弧的数目
出度:以该顶点为起点的弧的数目
路径与回路
路径:从Vi出发,经过一系列的边或弧能够到达Vj,则称Vi到Vj所经过的顶点序列为从Vi到Vj的路径。
路径长度:路径经过的边的数目或各边权值之和。
回路:起点和终点相同的路劲。
简单路径:序列中顶点不重复出现的路径。
简单回路:除了起点和终点外,其余顶点不重复出现的回路。
连通图
连通:顶点Vi到Vj有一条路径(不要求直达)则Vi和Vj是连通的。
连通图:图中任意两个顶点都是连通的。
连通分量:非连通图的每一个极大连通子图。
强连通图:有向图中任意不同的顶点Vi和Vj,从Vi到Vj和从Vj到Vi都存在路劲。
连通图
强连通图
非连通图的两个连通分量
邻接矩阵
表示顶点间相邻关系的矩阵。
设G=(V,E)是有n(n>0)个顶点的图,G的邻接矩阵A是具有以下性质的n阶方阵:
特点:
无向图的邻接矩阵沿对角线对称。
有向图的邻接矩阵不一定对称。
示例:
无向图的邻接矩阵
有向图的邻接矩阵
权与网
权:图中边或弧上附带的数值称为权(w)。
网:带权的图称为网。
3.图的存储结构与实现
邻接表
邻接表:有顶点表和边表组成,是链式存储结构。
顶点表
存放图中每个顶点的信息以及指向该顶点边表的头指针。顶点表通常采用顺序存储结构。
顶点表的结点结构:data——head
顶点域data存放顶点信息,head为边表头指针。
边表
为图中的每个顶点建立的单链表,单链表中存放与同一个顶点相邻接的邻接点,相当于邻接矩阵中的一行。
边表的结点结构:adjVex——next
邻接点域adjVex存放邻接点在顶点表中的序号,next为指向下一个邻接点的指针。
示例:无向图的邻接表
逆邻接表
结构与邻接表完全相同,只是边表中每个 结点存放的是每条弧的弧尾顶点。
4.图的遍历方法
深度优先遍历(DFS)
1、从图的某一顶点v出现(起点任选),访问此顶点;
2、选择一个与顶点v相邻且未被访问过的顶点w,再从w出发进行深度优先遍历(递归),直至图中所有和v连通的顶点都被访问过为止;
3、若此时图中尚有顶点未被访问,则另选图中一个未被访问的顶点作起点,重复上述过程,直至图中所有顶点都被访问为止。
深度优先遍历过程
为图的顶点设置一个访问标记数组visited[n],其中相应顶点的元素值为0表示为访问,为1表示已访问。
该图的深度优先搜索的输出序列为:ABCFEGDHI
示例:深度优先遍历无向图
深度遍历:V1->V2->V4->V8->V5->V6->V3->V7
示例:深度优先遍历有向图
深度遍历:V1->V2->V4->V8->V3->V6->V7->V5
算法实现
#define MAXV 7 //最大顶点个数
typedef int weight; //邻接矩阵元素类型
typedef char ElemType; //顶点数据元素类型
//邻接矩阵类型
typedef struct {
weight arcs[MAXV][MAXV]; //邻接矩阵
ElemType data[MAXV]; //一维数组顶点表
int n; //顶点个数
} MGraph, *AdjMatrix;
//以顶点v为起点,深度优先遍历图
void DFS(AdjMatrix g, int v, int visited[])
{
int u;
cout << " " << g->data[v]; //访问起点v
visited[v] = 1; //访问标志记为1,代表已访问
u = GetFirst(g, v); //取顶点v的第一个邻接点u
while (u != -1) {
if (visited[u] == 0) { //如果顶点u未被访问
DFS(g, u, visited); //以顶点u为起点,继续深度优先遍历(递归)
}
u = GetNext(g, v, u); //回到顶点u,取顶点v的位于顶点u之后的下一个邻接点,如有,继续循环过程
}
}
广度优先遍历(BFS)
1、从图的某一项顶点V出发(起点任选),访问顶点;
2、访问V的所有未被访问过的邻接点V1,V2,...,Vt,按照V1,V2,...,Vt的次序,访问每一个顶点的所有未被访问过的邻接点。依此类推,直至图中所有和V连通的顶点都未被访问过为止;
3、若此时图中尚有顶点未被访问,则另选图中一个未被访问的顶点作为起点,重复上述过程,直至图中所有顶点都被访问为止。
广度优先遍历过程
该图的广度优先搜索的输出序列为:ABEDCGFHI
示例:广度优先遍历无向图
广度遍历:V1->V2->V3->V4->V5->V6->V7->V8
示例:广度优先遍历有向图
广度遍历:V1->V2->V3->V4->V6->V7->V8->V5
5.图的应用
最小生成树的概念
连通图
图中任意两个顶点都是连通的。
对于连通图,从任意一个顶点出发进行深度优先遍历或广度优先遍历,都可以访问图中所有的其他顶点。
生成树
包含连通图全部顶点的极小连通子图。即以最少的边连接连通图中所有的顶点。
有n个顶点的连通图,它的生成树包含n个顶点和n-1条边。若加上一条边则构成环,若减去一条边则是非连通图。
最小生成树
带权连通图的所有生成树中权值之和最小的生成树。
在实际问题中的应用:管道的铺设问题。
n个小区只需铺设n-1条管线就能连通,各条管线的投资成本不同,如何使得总的投资成本最低(最小生成树)
最小生成树的两种构造算法
普里姆(Prim)算法
假设N=(V,E)是连通网,TE是N上最小生成树中边的集合。
初始时:U={v0}(v0∈V),TE={},重复执行下述操作:
在所有v∈U,k属于V-U的边(v,k)∈E中找一条权值最小的边(v,k)并入集合TE,同时k并入U,直至U=V为止。此时TE中必有n-1条边,则T=(V,TE)为N的最小生成树。
求解过程
1、初始化U={v}。v到其他顶点的所有边为候选边;
2、重复以下步骤n-1次,使得其他n-1个顶点被加入到U中:
(1)从候选边中挑选权值最小的边输出,设该边在V-U中的顶点是k,将k加入U中;
(2)考察当前V-U中所有顶点j,修改候选边:若(k,j)的权值小于原来和顶点j关联的候选边,则用(k,j)取代后者作为候选边。
示例:
以a为起点时
克鲁斯卡尔(Kruskal)算法
假设N=(V,E)时连通网,另最小生成树的初始状态为包含全部n个顶点,但没有边的非连通图T=(V,{}),图中每个顶点自成一个连通分量。
在E中选择权值最小的边,若该边依附的顶点落在T中不同的连通分量上,则将次边加入到T中,否则舍去此边而选择下一条权值最小的边。依此类推,直至所有顶点都在同一连通分量上为止。
最短路径(单源点最短路径)
最短路径
带权图中从源点到终点的所有路径中,所经过边的权值之和最小的路径称为最短路径。
图的最短路径:
(1)单源点最短路径:从一个顶点到其余各项顶点的最短路径;
(2)每对顶点间的最短路径。
狄克斯特拉(Dijkstra)算法求解单源点最短路径
设G=(V,E)是带权有向图,吧顶点集合V分成两组:
(1)第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点v,以后每求的一条最短路径v,...,u,就将u加入到集合S中,直到全部顶点都加入到S中。)
(2)第二组为其余未确定最短路径的顶点集合(用U表示)
每个顶点对应一个距离,S中顶点的距离就是从源点到此顶点的最短路径长度,U中顶点的距离是从源点到此顶点(只包括S中的顶点作为中间顶点)的当前最短路径长度。
按照最短路径长度的递增次序依次把(U=V-S)组的顶点加入S中。加入时,总保持从源点到S中各顶点的最短路径长度小于等于从源点到U中各顶点的当前最短路径长度。
当前最短路径的更新
假设u点加入S前,源点v到顶点j的当前最短路径为:(v,j)
确定源点v到顶点u的最短路径后,则把u点加入S;
如果发现:(v,u)+(u,j)<(v,j)。
则更新源点v到顶点j的当前最短路径为:(v,u,j)
拓扑排序
设G=(V,E)是一个具有n个顶点的有向图。V中顶点序列v1,v2,...,vn称为一个拓扑序列,当且仅当该顶点序列满足下列条件:
若<vi,vj>是图中的边(即从顶点vi到vj有一条路径),则在序列中顶点vi必须排在顶点vj之前。
在一个有向图中找一个拓扑序列的过程称为拓扑排序。
拓扑排序方法
1、从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它;
2、从网中删去该顶点,并且删去从该顶点发出的全部有向边;
3、重复上述两步,直到剩余的网中不再存在没有前驱的顶点为止。
注意:拓扑序列不一定唯一。