6 图
6.1 图概述
6.1.1 图的基本概念
图是由顶点(Vertex)集V和边(Edge)集E组成,记为G=(V,E)。V是有穷非空集合,称为顶点集,v∈V称为顶点。E是有穷集合,称为边集,e∈E称为边。e=(u,v)或e=<u,v>;u,v∈V,其中,(u,v)表示顶点u与顶点v的一条无向边,简称为边,即(u,v)没有方向,这时(u,v)(v,u)是等同的;而<u,v>表示从顶点u到顶点v的一条有向边,简称为弧,u为始点或弧尾,v为终点或弧头。需要说明的是,E可以是空集,此时图G只有顶点没有边,称为零图。
1. 无向图
全部由无向边构成的图称为无向图(Undirected Graph)
2. 有向图
全部由有向边构成的图称为有向图(Directed Graph)。
3. 权和网
在 一个图中,每条边可以标上具有某种含义的数值,此数值成为该边上的权(Weight),通常一个权是一个非负数。权可以表示从一个顶点到另一个顶点的距离,时间或代价等含义。边上标识权的图成为网。
4. 完全图
在具有n个顶点的无向图G中,当边数达到最大值n(n-1)/2时,称图G为无向完全图(Undirected Complete Graph)。
在具有n个顶点的有向图G中,当边数达到最大值n(n-1)时,称图G为有向完全图(Directed Complete Graph)。
5. 稠密图和稀疏图
在具有n个顶点、e条边的图G中,若含有较少的边(例如e<nlog2(n)),则称图G为稀疏图(Sparse Graph),反之则称为稠密图(Dense Graph)。
6. 子图
设有两个图G=(V,E)和G‘=(V’,E‘),若V’是V的子集,并且E'shi E的子集,则称G’为G的子图(Subgraph),记为G‘∈G。若G’为G的子图,并且V‘=V,则称G’为G的生成子图(Spanning Subgraph)。
7. 邻接点
在一个无向图中,若存在一条边(u,v),则称顶点u与v互为邻接点。边(u,v)是顶点u和v关联的边,顶点 u和v是边(u,v)关联的顶点。
在一个有向图中,若存在一条弧<u,v>,则称顶点u邻接到v,顶点v邻接自u。弧(u,v)和顶点u、v关联。
8. 顶点的弧
顶点的度(Degree)是图中与该顶点相关联的数目。顶点v的度记为D(v)。在有向图中,顶点v的度有入度和出度之分,以v为终点的弧的数目称为入度(In Degree);以v为起点的弧的数目称为出度(Out Degree),记为OD(v),顶点的度等于它的入度和出度之和,即D(v)=ID(v)+OD(v)。
若一个图有n个顶点和e条边,则改图所有顶点的度之和与边数e满足如下关系:
e=0.5*[D(V0)+D(V1)+D(V2)+...+D(Vn-1)]
9. 路径与回路
在一个图中,路径(Path)是从顶点u到顶点v所经过的顶点序列,路径长度是指该路径上边的数目。第一个顶点和最后一个顶点相同的路径称为回路或环。序列中顶点不重复出现的路径称为初等路径。除了第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路称为初等回路。
10. 连通图和连通分量
在无向图中,若从顶点u到顶点v有路径,则称u和v是连通的。若图中的任意两个顶点均是连通的,则称该图是连通图,否则称为非连通图。无向图中的极大连通子图称为连通分量。
11. 强连通图和强连通分量
在有向图中,若任意两个顶点均连通,则称该图是强连通图。有向图中的极大强连通子图称为强连通分量,强连通图只有一个强连通分量,即其本身;非强连通图有多个强连通分量。
12. 生成树和生成森林
生成树是一种特殊的生成子图,它包含图中的全部顶点,但只有构成一棵树的n-1条边。
对于非连通图,每个连通分量可行成一棵生成树,这些生成树组成了该非连通图的生成森林。
6.1.2 图的抽象数据类型
图由顶点集和边集组成,因此对图的操作也集中在对顶点和边的操作上,主要有以下的一些操作。1)createGraph():创建一个图
2)getVexNum():返回图中的定点数
3)getArcNum():返回图中的边数
4)getVex(v):给定顶点的位置v,返回其对应的顶点值,其中,0<=v<vexNum(vexNum为顶点数)。
5)locateVex(vex):给定顶点的值vex,返回其在图中的位置,如果图中不包含此项点,则返回-1。
6)firstAdjVex(v):返回v的第一个邻接点,若v没有邻接点,则返回-1,其中,0<=v<vexNum(vexNum为顶点数)。
7)nextAdjVex(v,w):返回v相对于w的下一个邻接点,若w是v的最后一个邻接点,则返回-1,其中,0<=v,w<vexNum。
6.2 图的存储结构
图的类型主要有4中:无向图、有向图、无向网和有向网。可以用枚举表示如下:
public enum GraphKind {
UDG, // 无向图(UnDirected Graph)
DG, // 有向图(Directed Graph)
UDN, // 无向网(UnDirected Network)
DN;// 有向网(Directed Network)
}图的存储结构除了存储图中各个顶点的信息外,还要存储与顶点相关联的边的信息,图的常见存储结构有邻接矩阵、邻接表、临界多重表、十字链表等,每种存储结构都能表示上面所讲的4种类型的图,这儿主要介绍邻接矩阵和邻接表。
6.2.1 邻接矩阵
图的邻接矩阵(Adjacency Matrix)是用来表示顶点之间相邻关系的矩阵。
图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息。
设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为:
【例】下图中无向图G 5 和有向图G 6 的邻接矩阵分别为A l 和A 2 。
无向图的邻接矩阵是对称阵,因此一般可以采用压缩存储;有向图的邻接矩阵一般不对称。用邻接矩阵存储图,所需要的存储空间只与顶点数有关。
对于网G,则邻接矩阵可定义为:
其中:W ij 表示边上的权值;
∞表示一个计算机允许的、大于所有边上权值的数。
【例】下面带权图的两种邻接矩阵分别为A 3 和A 4 。
用邻接矩阵表示图,很容易判断任意两个顶点之间是否有边,同样很容易求出各个顶点的度。对于无向图,邻接矩阵的第i行或第i列的非零元素的个数正好是第i个顶点Vi的度;对于有向图,邻接矩阵的第i行的非零元素的个数正好是第i个顶点Vi的出度,第i列的非零元素的个数正好是第i个顶点Vi的入度。
2. 图的邻接矩阵类的描述
对于一个具有n个顶点的图G,可以将图G的邻接矩阵存储在一个二维数组中,图的邻接矩阵类MGraph的描述如下:
package ljjz;
import chapter6.GraphKind;
public class MGraph {
public final static int INFINITY = Integer.MAX_VALUE;
private GraphKind kind;// 图的种类标志
private int vexNum, arcNum;// 图的当前顶点数和边数
private Object[] vexs;// 顶点
private int[][] arcs;// 邻接矩阵
public MGraph() {
this(null, 0, 0, null, null);
}
public MGraph(GraphKind kind, int vexNum, int arcNum, Object[] vexs,
int[][] arcs) {
this.kind = kind;
this.vexNum = vexNum;
this.arcNum = arcNum;
this.vexs = vexs;
this.arcs = arcs;
}
// 创建图
public void createGraph() {
}
// 创建无向图
private void createUDG() {
}
// 创建有向图
private void createDG() {
}
// 创建无向网
private void createUDN() {
}
// 创建有向网
private void createDN() {
}
// 返回顶点数
public int getVexNum() {
return vexNum;
}
// 返回边数
public int getArcNum() {
return arcNum;
}
// 给定顶点的值vex,返回其在图中的位置,如果图中不包含此顶点,则返回-1
public int locateVex(Object vex) {
return 0;
}
// 返回v表示结点的值,0<=v<vexNum
public Object getVex(int v) throws Exception {
if (v < 0 || v >= vexNum) {
throw new Exception("第" + v + "个顶点不存在!");
}
return vexs[v];
}
// 返回v的第一个邻接点,若v没有邻接点则返回-1,0<=v<vexNum
public int firstAdjVex(int v) throws Exception {
return 0;
}
// 返回v相对于w的下一个邻接点,若w是v的最后一个邻接点,则返回-1,其中,0<=v,w<vexNum
public int nextAdjVex(int v, int w) {
return 0;
}
public GraphKind getKind() {
return kind;
}
public int[][] getArcs() {
return arcs;
}
public Object[] getVexs() {
return vexs;
}
}
3. 图的邻接矩阵类基本操作的实现
1)图的创建
public void createGraph() {
Scanner sc = new Scanner(System.in);
GraphKind kind = GraphKind.valueOf(sc.next());
switch (kind) {
case UDG:
createUDG();// 构造无向图
return;
case DG:
createDG();// 构造有向图
return;
case UDN:
createUDN();// 构造无向网
return;
case DN:
createDN();// 构造有向网
return;
}
}
// 创建无向网
private void createUDN() {
Scanner sc = new Scanner(System.in);
System.out.println("请分别输入图的顶点数、图的边数");
vexNum = sc.nextInt();
arcNum = sc.nextInt();
vexs = new Object[vexNum];
System.out.println("请分别输入图的各个顶点");
for (int v = 0; v < vexNum; v++) {
vexs[v] = sc.next();
}
arcs = new int[vexNum][vexNum];
for (int v = 0; v < vexNum; v++) {
for (int u = 0; u < vexNum; u++) {
arcs[v][u] = INFINITY;
}
}
System.out.println("请输入各个变的两个顶点及其权值");
for (int k = 0; k < arcNum; k++) {
int v = locateVex(sc.next());
int u = locateVex(sc.next());
arcs[v][u] = arcs[u][v] = sc.nextInt();
}
} // 创建有向网
private void createDN() {
Scanner sc = new Scanner(System.in);
System.out.println("请分别输入图的顶点数、图的边数");
vexNum = sc.nextInt();
arcNum = sc.nextInt();
vexs = new Object[vexNum];
System.out.println("请分别输入图的各个顶点");
for (int v = 0; v < vexNum; v++) {
vexs[v] = sc.next();
}
arcs = new int[vexNum][vexNum];
for (int v = 0; v < vexNum; v++) {
for (int u = 0; u < vexNum; u++) {
arcs[v][u] = INFINITY;
}
}
System.out.println("请输入各个变的两个顶点及其权值");
for (int k = 0; k < arcNum; k++) {
int v = locateVex(sc.next());
int u = locateVex(sc.next());
arcs[v][u] = sc.nextInt();
}
}2)顶点定位
顶点定位的结拜呢要求:根据顶点信息vex,取得其在顶点数组中的位置,若图中无此顶点,则返回-1.
public int locateVex(Object vex) {
for(int v = 0;v<vexNum;v++){
if(vexs[v].equals(vex)){
return v;
}
}
return -1;
}3)查找第一个邻接点
查找第一个邻接点的基本要求是:已知图中的一个顶点v,返回v的第一个邻接点,若v没有邻接点,则返回-1,其中,0<=v<vexNum。
public int firstAdjVex(int v) throws Exception {
if (v < 0 || v >= vexNum) {
throw new Exception("第" + v + "个顶点不存在!");
}
for (int j = 0; j < vexNum; j++) {
if (arcs[v][j] != 0 && arcs[v][j] < INFINITY) {
return j;
}
}
return -1;
}4)查找下一个邻接点
查找下一个邻接点的基本要求是:已知图中的一个顶点v以及v的一个邻接点w,返回v相对于w的下一个邻接点,若w是v最后一个邻接点,则返回-1,其中0<=v,w<vexNum。
public int nextAdjVex(int v, int w) throws Exception {
if (v < 0 || v >= vexNum) {
throw new Exception("第" + v + "个顶点不存在!");
}
for (int j = w + 1; j < vexNum; j++) {
if (arcs[v][j] != 0 && arcs[v][j] < INFINITY) {
return j;
}
}
return -1;
} 用邻接矩阵存储图,虽然能很好地确定图中的任意两个顶点之间是否右边,但是不论是求任意顶点的度,还是查找任一顶点的邻接点,都需要访问对应的一行或一列中的所有的数据元素,其时间复杂度为O(n)。而确定图中有多少条边,则必须按行对每个数据元素进行检测,花费的时间代价较大,其时间复杂度为O(n^2)。从空间上看,无论图中的顶点之间是否有边,都要在邻接矩阵中保存空间,其空闲复杂度为O(n^2),空间效率较低,这也是邻接矩阵的局限性。
6.2.2 邻接表
1. 图的邻接表存储结构
邻接表(Adjacency List)是图的一种链式存储方法,邻接表表示类似于树的孩子链表表示。邻接表是由一个顺序存储的顶点表和n个链式存储的边表组成的。其中,顶点表由顶点结点组成;边表是由边(或弧)及结点组成的一个单链表,表示所有依附于顶点Vi的边(对于有向图就是所有以Vi为始点的弧)。
顶点结点包括data和firstArc两个域。data表示顶点信息;firstArc表示指向边表中的第一个边(或弧)结点。顶点节点类VNode的描述如下:
边界点包括adjVex、nextArc和value共3个域,其中,adjVex表示与顶点Vi邻接的顶点在图中的位置;nextArc指向下一个边结点;value存储与边相关的信息,例如权值等。
本文介绍了图的基本概念,包括无向图、有向图、完全图、稠密图和稀疏图等。还讨论了图的邻接矩阵和邻接表这两种存储结构,邻接矩阵适合判断两点间是否有边,但空间效率低,而邻接表空间利用率高,适用于边数量少的情况。
1万+

被折叠的 条评论
为什么被折叠?



