
8.2.0 图的接口实现
public interface Graph<V> {
int edgesSize(); //边的数量
int verticesSize() ; //顶点数量
void addVertex(V v); //增加一个顶点
void addEdge(V from,V to); //增加一条边
void addEdge(V from,V to,int weight); //增加一条带权值的边
void removeEdge(V from,V to); //删除一条边
void removeVertex(V v); //删除某个顶点
void displayGraph(); //显示一个图
int degree(V v) ; //返回顶点v的度
}

测试用图
//图测试类的代码
public class GraphTest {
public static void main(String[] args) {
// Graph<Character> graph = new UnderetedMatrixGrpah<>(10); //邻接矩阵的测试
Graph<Character>graph = new UnderetedListGraph<>(10); //邻接表的测试
graph.addVertex('A');
graph.addVertex('B');
graph.addVertex('C');
graph.addVertex('D');
graph.addVertex('E');
graph.addVertex('F');
graph.addEdge('A', 'B');
graph.addEdge('A', 'D');
graph.addEdge('B', 'C');
graph.addEdge('B', 'E');
graph.addEdge('C', 'D');
graph.addEdge('C', 'F');
graph.addEdge('D', 'F');
graph.addEdge('E', 'F');
graph.displayGraph();
// graph.removeVertex('B');
// graph.displayGraph();
System.out.println("顶点C的度:" + graph.degree('C'));
}
}
8.2.1 邻接矩阵
邻接矩阵是表示顶点之间邻接关系的矩阵。设G=(V,E)是含有n(设n>0)个顶点的图,各顶点的编号为0~n-1,则G的邻接矩阵数组A是n阶方阵。

邻接矩阵存储

各种图(网)的邻接矩阵

测试样例的邻接矩阵完整描述
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/**
*
* @author chp
*
* @param
*
* 模拟实现一个无向图(邻接矩阵)
*/
public class UnderetedMatrixGrpah implements Graph {
private List vertexList; //保存顶点信息
private int[][] edgeMatrix; //保存邻接矩阵
private int edges; //边的数量
private int vertices; //顶点数量
private int size; //规模
public UnderetedMatrixGrpah(int size) {
this.size = size;
vertexList = new ArrayList<>();
edgeMatrix = new int[size][size];
this.edges = 0;
this.vertices = 0;
}
@Override
public int edgesSize() {
// TODO Auto-generated method stub
return this.edges;
}
@Override
public int verticesSize() {
// TODO Auto-generated method stub
return this.vertices;
}
@Override
public void addVertex(V v) {
// 加入一个顶点
this.vertexList.add(v);
this.vertices++;
}
@Override
public void addEdge(V from, V to) {
// TODO Auto-generated method stub
this.addEdge(from, to, 1);
}
@Override
public void addEdge(V from, V to, int weight) {
// TODO Auto-generated method stub
int i = this.vertexList.indexOf(from);
int j = this.vertexList.indexOf(to);
this.edgeMatrix[i][j] = weight;
this.edgeMatrix[j][i] = weight;
this.edges++;
}
@Override
public void removeEdge(V from, V to) {
// 删除一条边
int i = this.vertexList.indexOf(from);
int j = this.vertexList.indexOf(to);
this.edgeMatrix[i][j] = 0;
this.edgeMatrix[j][i] = 0;
this.edges--;
}
} @Override
public void removeVertex(V v) {
// 删除一个顶点
int index = this.vertexList.indexOf(v); // 被删除顶点的序号
for (int i = 0; i &lt; this.vertices; i++) {
if (this.edgeMatrix[index][i] != 0) { // 表示index与i之间有一条边
this.edges--;
}
}
// index后面的所有行前移一行
for (int i = index; i &lt; this.vertices - 1; i++) {
for (int j = 0; j &lt; this.vertices; j++) {
this.edgeMatrix[i][j] = this.edgeMatrix[i + 1][j];
}
}
// index后面的所有列前移一列
for (int i = 0; i &lt; this.vertices; i++) {
for (int j = index; j &lt; this.vertices - 1; j++) {
this.edgeMatrix[i][j] = this.edgeMatrix[i][j + 1];
}
}
//为了保证后面新增的顶点不收到之前存储值的影响,还要做哪些操作?请大家自行补充代码。
this.vertexList.remove(index);
this.vertices--;
}
@Override
public void displayGraph() {
// 显示当前图
System.out.println(&quot;这是一个无向图(邻接矩阵):&quot;);
System.out.println(&quot;顶点:&quot; + this.vertexList);
System.out.println(&quot;图的邻接矩阵是:&quot;);
for (int i = 0; i &lt; this.vertices; i++) {
for (int j = 0; j &lt; this.vertices; j++) {
System.out.print(this.edgeMatrix[i][j] + &quot; &quot;);
}
System.out.println();
}
}
@Override
public int degree(V v) {
// 返回图的顶点v的度
int index = this.vertexList.indexOf(v); // 获取顶点的序号
int count = 0;
for (int i = 0; i &lt; this.vertices; i++) {
if (this.edgeMatrix[index][i] != 0) { // 表示index与i之间有一条边
count++;
}
}
return count;
}
邻接矩阵的特点
图的邻接矩阵表示是唯一的。
对于含有n个顶点的图,采用邻接矩阵存储时,无论是有向图还是无向图,也无论边的数目是多少,其存储空间均为O(n2),所以邻接矩阵适合于存储边数较多的稠密图。
无向图的邻接矩阵一定是一个对称矩阵。因此在顶点个数n很大时可以采用对称矩阵的压缩存储方法减少存储空间。
对于无向图,邻接矩阵的第i行(或第i列)非空元素的个数正好是顶点i的度。
对于有向图,邻接矩阵的第i行(或第i列)非空元素的个数正好是顶点i的出度(或入度)。
用邻接矩阵方法存储图,确定任意两个顶点之间是否有边相连的时间为O(1)。
8.2.2 邻接表
每个顶点建立一个单链表,第i(0≤i≤n-1)个单链表中的结点表示依附于顶点i的边(有向图是顶点i出边)。
每个单链表上附设一个表头结点,将所有表头结点构成一个头结点数组。

图的邻接表存储结构

测试样例的邻接表完整描述
import java.util.LinkedList;
import java.util.Queue;
/**
*
* @author chp
*
* @param <V>
*
* 模拟一个无向图的邻接表实现
*/
public class UnderetedListGraph<V>implements Graph<V>{
Vertex<V>[] vertexList; // 邻接 数组
private int edges; // 边的数量
private int vertices; // 顶点的数量
public UnderetedListGraph(int size) {
vertexList = new Vertex[size];
this.edges = 0;
this.vertices = 0;
}
class Vertex<V>{
V data; // 顶点值
LinkedList<Integer>adj; // 邻接表
Vertex(V data) {
this.data = data; // 顶点值
this.adj = new LinkedList<>(); // 每个顶点的邻接点构成的邻接表
}
}
@Override
public int edgesSize() {
// TODO Auto-generated method stub
return this.edges;
}
@Override
public int verticesSize() {
// TODO Auto-generated method stub
return this.vertices;
}
@Override
public void addVertex(V v) {
// 增加一个顶点
this.vertexList[this.vertices++] = new Vertex<>(v);
}
@Override
public void addEdge(V from, V to) {
// 增加一条边
int i = this.getPosition(from);
int j = this.getPosition(to);
this.vertexList[i].adj.add(j);
this.vertexList[j].adj.add(i);
this.edges++;
}
private int getPosition(V v) {
// TODO Auto-generated method stub
for (int i = 0; i < this.vertices; i++) {
if (this.vertexList[i].data.equals(v)) {
return i;
}
}
return -1;
}
@Override
public void addEdge(V from, V to, int weight) {
// TODO Auto-generated method stub
}
@Override
public void removeEdge(V from, V to) {
// 删除一条边
int i = this.getPosition(from);
int j = this.getPosition(to);
this.vertexList[i].adj.remove(new Integer(j)); // 思考?
this.vertexList[j].adj.remove(new Integer(i)); // 删除的应该是一个对象,而不是根据索引index来进行删除
this.edges++;
}
@Override
public void removeVertex(V v) {
// 删除一个顶点
/*
* 1. 查询顶点v的序号index 2. 更新边的个数 3. 删除所有邻接点对应的index 4. 在数组中删除当前的顶点 5. 更新顶点的个数 6.
* 更新所有邻接表中序号值,将所有大于index的值全部减
*/
int index = this.getPosition(v);
this.edges = this.edges - this.degree(v);
for (int i : this.vertexList[index].adj) {
this.vertexList[i].adj.remove(new Integer(index));
}
for (int i = index; i < this.vertices - 1; i++) {
this.vertexList[i] = this.vertexList[i + 1];
}
this.vertices--;
for (int i = 0; i < this.vertices; i++) {
LinkedList<Integer>list = vertexList[i].adj;
for (int j = 0; j < list.size(); j++) {
if (list.get(j) > index) {
list.set(j, list.get(j) - 1);
}
}
}
}
@Override
public void displayGraph() {
// 显示当前图
System.out.println("这是一个使用邻接表存储的图:");
for (int i = 0; i < this.vertices; i++) {
System.out.print("顶点:" + this.vertexList[i].data);
System.out.println(",邻接表" + this.vertexList[i].adj);
}
}
@Override
public int degree(V v) {
// TODO Auto-generated method stub
return this.vertexList[getPosition(v)].adj.size();
}
}
邻接表的特点
邻接表表示不唯一。
对于有n个顶点和e条边的无向图,其邻接表有n个表头结点和2e个边结点;对于有n个顶点和e条边的有向图,其邻接表有n个表头结点和e个边结点。显然,对于边数目较少的稀疏图,邻接表比邻接矩阵要节省空间。
对于无向图,顶点i(0≤i≤n-1)对应的单链表的边结点个数正好是顶点i的度。
对于有向图,顶点i(0≤i≤n-1)对应的单链表的边结点个数仅仅是顶点i的出度。顶点i的入度是邻接表中adj中元素值为i的边结点个数。
用邻接表存储图时,确定任意两个顶点之间是否有边相连的时间为O(m)(m为最大顶点出度,m<n)。
视频资源: