图建模
为了方便的使用图这个数据结构,我们需要使用编程语言对其抽象的内容进行具体化,我们在设计一个复杂的数据结构的时候可以参考Java集合框架的特点,首先定义一个接口,包含的是图的所有的常用的公共的操作,然后定义一个抽象类来部分地实现这个接口,最后根据具体的使用情况定义具体的类实现抽象类。
图的常用操作:
1、获取图中顶点个数
2、获取图中的顶点
3、返回指定顶点下标的顶点对象
4、返回指定顶点的下标
5、返回指定下标的顶点的邻居
6、打印边
7、清除图
8、添加顶点
9、添加边
通过接口,将这些图的常用操作定义封装在一起:
import java.util.List; /** * * @param <V> 参数V是一个泛型,表示图中顶点 * */ public interface Graph<V> { /** * * @return 返回值是图中的顶点个数 */ public int getSize(); /** * * @return 返回值是图中所有的顶点对象 */ public List<V> getVertices(); /** * * @param index 表示要获取的顶点的索引 * @return 返回指定下标的顶点对象 */ public V getVertex(int index); /** * * @param v 参数v表示要获取下标的顶点对象 * @return 返回值是指定下标的顶点的索引 */ public int getIndex(V v); /** * * @param index * @return 返回指定下标的顶点的邻居 */ public List<Integer> getNeighbors(int index); /** * * @param v * @return 返回指定顶点的度 */ public int getDegree(int v); /** * 打印所有的边 */ public void printEdges(); /** * 清楚图 */ public void clear(); /** * 向图中添加顶点vertex * @param vertex */ public boolean addVertex(V vertex); /** * 向图中添加边<u,v> * @param u * @param v */ public boolean addEdge(int u,int v); }
定义一个抽象类,部分实现了上面的接口,其实已经实现了接口中所有的方法,但是为了以后方便的在接口中增加新的方法,而这个方法不需要当前这个抽象类来实现,所以将这个类定义为抽象类,方便维护接口和类。
import java.util.ArrayList; import java.util.List; public abstract class AbstractGraph<V> implements Graph<V> { List<V> vertices = new ArrayList<>(); List<List<Edge>> neighbors = new ArrayList<>(); public AbstractGraph() { } /** * 由一个顶点数组和边数组构建一个图 * @param vertices * @param edges */ public AbstractGraph(V[] vertices,int[][] edges ) { for (V vertex : vertices) { addVertex(vertex); } createAdjacencyLists(edges); } protected void createAdjacencyLists(int[][] edges){ for (int i = 0; i < edges.length; i++) { addEdge(edges[i][0],edges[i][1]); } } /** * 由一个顶点列表和一个边列表构建图 * @param edges */ public AbstractGraph(List<V> vertices,List<Edge> edges) { for (V vertex : vertices) { addVertex(vertex); } createAdjacencyLists(edges); } protected void createAdjacencyLists(List<Edge> edges){ for (Edge edge : edges) { addEdge(edge.getU(),edge.getV()); } } @Override public int getSize() { return vertices.size(); } @Override public List getVertices() { return vertices; } @Override public V getVertex(int index) { return vertices.get(index); } @Override public int getIndex(V v) { return vertices.indexOf(v); } @Override public List<Integer> getNeighbors(int index) { ArrayList<Integer> res = new ArrayList<>(); for (Edge edge : neighbors.get(index)) { res.add(edge.getV()); } return res; } @Override public int getDegree(int v) { return neighbors.get(v).size(); } @Override public void printEdges() { for (int i = 0; i < neighbors.size(); i++) { System.out.println(getVertex(i) + "(" + i + "):" ); for (Edge edge : neighbors.get(i)) { System.out.println("(" + getVertex(edge.getU()) + "," + getVertex(edge.getV()) + ")"); } System.out.println(); } } @Override public void clear() { vertices.clear(); neighbors.clear(); } @Override public boolean addVertex(V vertex) { if (!vertices.contains(vertex)){ vertices.add(vertex); neighbors.add(new ArrayList<Edge>()); return true; } return false; } @Override public boolean addEdge(int u, int v) { return addEdge(new Edge(u,v)); } private boolean addEdge(Edge edge) { if (!neighbors.get(edge.getU()).contains(edge)){ neighbors.get(edge.getU()).add(edge); } return false; } }
需要注意的是,上面的那个变量neighbors表示的就是邻接边线性表,这个线性表的每一个元素都是一个列表类型,而这个列表中的元素类型都是Edge对象类型,即存储的都是一个个边对象。它们数据结构如下图所示:
neighbors[i]是表示包含顶点i的所有邻接边列表,这里边的都是以i作为出发点的,<i,x>