前言:
我们所讨论的图模型中,边仅仅是两个顶点之间的连接。一般使用0-(V-1)来表示一张含有V个顶点的图中的各个顶点。用v-w的记法表示v点和w点之间的边(又可以写成w-v)。
特殊的两种图:
自环: 一条连接一个顶点和其自身的边。
平行边: 连接同一对顶点的两条边。
以下所说的图两个顶点之间仅含有一条边。
相关术语:
当边连接两个顶点时,我们说顶点彼此 相邻。
顶点的度数为依附于他的边的总数。
子图是一幅图所有被边的一个子集(包括他们所依附的顶点)。
路径是由边连接的顶点序列,没有重复的边。
简单路径是没有重复顶点的路径。
环是一条至少含有一条边且起点和终点相同的路径。
简单环是一条(除了起点和终点必须相同之外)不含重复顶点和边的环。
路径或环的长度为其包含的边数。
如果从任意一个顶点都存在一条路径到达另一个任意顶点,则称这幅图是连通图。
一个非连通图由若干连通图组成,它们都是极大连通子图。
无环图是一个不包含环的图。
树是一副无环连通图。互不相连的树组成森林。连通图的生成树是他的一副子图,它含有图中的所有顶点且是一颗树。图的生成森林是他的所有连通子图的生成树的集合。
关于树的定义,以下五种说法是等价的:(一副含有V个顶点的图G)
1:G有V-1条边并且不含环。
2:G有V-1条边并且是联通的
3:G是连通的且删除任意一条边都会使他不连通。
4:G是无环图且添加任意一条边都会使他产生一条环。
5:G中任意一对顶点间仅存在一条简单路径。
我们定义以下图的基本操作API:
Public class Graph
Graph(int V) //创建一个含有V个顶点但不含边的图
Graph(InputStream in) //从标准输入流中读取一个图
int V() //顶点数
int E() //边数
void addEdge(int v,int w) //添加边V-W
Iterable<Integer> adj(int v) //遍历与v相邻的顶点
String toString() // @Override
相关实现:
计算V的度数:
public static int degree(Graph g,int v)
{ int degree=0;
for(int w: g.adj(v)) degree++;
return degree;
}
计算所有顶点的最大度数:
public static int maxDegree(Graph g)
{ int max=0;
for(int v=0;v<g.v();v++)
if(degree(g,v)>max)
max=degree(g,v);
return max; }
计算自环的个数:
public static int numberOfSefLoops(Graph g)
{ int count=0;
for(int v=0;v<g.v();v++)
for(int w:g.adj(v))
if(w==v) count++;
return count/2;
}
//覆写toString方法@Override
public String toString()
{
String s=V+”vertices”+E+” edges\n”;//建议你用StringBuilder
for(int v=0;v<g.v();v++)
{ s+=v+”: ”;
for(int w:this.adj(v))
s+=“\n”;
}
return s;
}
图的几种表示方法:
要求: 1、必须为各种情况下遇到的图预留足够空间。
2、快。
邻接矩阵:
用一个V * V的Boolean矩阵表示,1代表相连。
//不满足第一种要求。
边的数组:
使用一个Edge类,含有两个int变量。
/不满足第二个要求,比如调用adj方法。
邻接表数组:(推荐)
使用以顶点为索引的列表数组,其中每个元素都是和该顶点相连的顶点列表。如下图:
要连接一条v到w的边,我们要将w添加到v的邻接表中并把v添加到w的临界表中。因此这种结构中每条边都会出现两次。
代码实现:
//我们使用前面介绍过的Bag,Stack实现,相关代码请看数据结构之队列、栈。
1 public class Graph { 2 3 private final int V; 4 private int E; 5 private Bag<Integer> adj[]; 6 7 @SuppressWarnings("unchecked") 8 public Graph(int V) 9 { if (V < 0) throw new IllegalArgumentException("Number of vertices must be nonnegative"); 10 this.V=V; 11 this.E=0; 12 adj= (Bag<Integer>[])new Bag[V]; 13 for(int i=0;i<V;i++) 14 { 15 adj[i]=new Bag<Integer>(); 16 } 17 } 18 19 public Graph(Graph G) { 20 this(G.V()); 21 this.E = G.E(); 22 for (int v = 0; v < G.V(); v++) { 23 24 Stack<Integer> reverse = new Stack<Integer>(); 25 for (int w : G.adj[v]) { 26 reverse.push(w); 27 } 28 for (int w : reverse) { 29 adj[v].add(w); 30 } 31 } 32 } 33 34 public int V() 35 { 36 return this.V; 37 } 38 39 public int E() 40 { 41 return this.E; 42 } 43 44 private void validateVertex(int v) { 45 if (v < 0 || v >= V) 46 throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1)); 47 } 48 49 public void addEdge(int v,int w) 50 { validateVertex(v); 51 validateVertex(w); 52 adj[v].add(w); 53 adj[w].add(v); 54 E++; 55 } 56 57 public Iterable<Integer> adj(int v) { 58 validateVertex(v); 59 return adj[v]; 60 } 61 62 public int degree(int v) { 63 validateVertex(v); 64 return adj[v].size(); 65 } 66 67 public String toString() { 68 StringBuilder s = new StringBuilder(); 69 s.append(V + " vertices, " + E + " edges " ); 70 for (int v = 0; v < V; v++) { 71 s.append(v + ": "); 72 for (int w : adj[v]) { 73 s.append(w + " "); 74 } 75 s.append("\r"); 76 } 77 return s.toString(); 78 } 79 80 }