图的邻接矩阵的实现
来源:互联网 发布:qq偷菜软件下载 编辑:程序博客网 时间:2024/06/11 21:08
对于图的一些基本概率和术语的内容汗牛充栋,故本文不会做过多解释。仅仅总结下笔者学习图的邻接矩阵的相关知识
邻接矩阵的实现原理
邻接矩阵(adjacency matrix)是图ADT最基本的实现方式,使用二维数组A[n][n]来表示由n个顶点构成的图。二维数组中的A[i][j]表示一条从来顶点i为起点到顶点j的弧。
对于无权图,存在(不存在)从顶点u到v的边,当且仅当A[u][v] =1(0)。上图a,b即为无向图和有向图的邻接矩阵实例。图c所示,矩阵各单元可从布尔型改为整型或浮点型,记录所对应边的权重。对于不存在的边,通常统一取值null。
节点的实现
public class Node<T> { private T data; // 存储的数据 private int outDegree; // 出度 private int inDegree; // 入度 //如下四个属性用与遍历中 private int status = 0; //状态 0 undiscovered 1 discovered 2 visited //status应该使用枚举的,这里笔者偷懒了 private int parent = -1; private int dTime = -1; //开始遍历的时间 private int fTime = -1; //结束遍历的时间 public Node(T t) { this.data = t; this.outDegree = 0; this.inDegree = 0; } protected void addInDegree() { this.inDegree++; } protected void addOutDegree() { this.outDegree++; } protected void delInDegree() { this.inDegree--; } protected void delOutDegree() { this.outDegree--; }//节约篇幅,省略getter setter...}
弧的实现
public class Edge<T> { private T data; // 弧数据 private int weight; // 权值 private int type; //应该使用枚举的,这里笔者偷懒了 //弧类型:0 CROSS 跨边 1 TREE(支撑树) //2 BACKWARD(该弧的起点和终点在支撑树中存在终点到起点的路径) //3 FORWARD (该弧的起点和终点在支撑树中存在其他路径依然可以从起点到终点) public Edge(T data) { this(data, 1); } public Edge(T data, int weight) { this.data = data; this.weight = weight; } //节约篇幅,省略getter setter...}
邻接矩阵的存储实现
public class AdjacencyMatrix { private Edge[][] nodeGraphs; private Node[] allNodes; private Integer size=0; private int DEFAULT_CAPACITY = 10; //创建默认长度的邻接矩阵 public AdjacencyMatrix() { nodeGraphs = new Edge[DEFAULT_CAPACITY][DEFAULT_CAPACITY]; allNodes = new Node[DEFAULT_CAPACITY]; } //创建指定长度的邻接矩阵 public AdjacencyMatrix(int numbers) { nodeGraphs = new Edge[numbers][numbers]; allNodes = new Node[numbers]; } //添加新节点,并返回新节点索引 public int addNode(Node node) { int returnIndex = size; //判断当前数组的容量,来决定是否扩容 ensureCapacityInternal(size + 1); allNodes[size++] = node; return returnIndex; } private void ensureCapacityInternal(int minCapacity) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { //判断是否需要扩容 if(minCapacity - allNodes.length > 0) { grow(minCapacity); } } private void grow(int minCapacity) { int oldCapacity = allNodes.length; //新数组的长度为(原数组长度+原数组长度/2) //右移n为即为除2的n次方 int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //将原数组复制到扩容后的数组中 allNodes = Arrays.copyOf(allNodes, newCapacity); nodeGraphs = Arrays.copyOf(nodeGraphs, newCapacity); //对于二维中的数组执行扩容 for(int i=0;i<nodeGraphs.length;i++) { if(nodeGraphs[i] != null) { nodeGraphs[i] = Arrays.copyOf(nodeGraphs[i], newCapacity); }else { nodeGraphs[i] = new Edge[newCapacity]; } } } //获取出度 public int getInDeep(int index) { if(allNodes[index] == null) { return -1; } return allNodes[index].getInDegree(); } //获取入度 public int getOutDeep(int index) { if(allNodes[index] == null) { return -1; } return allNodes[index].getOutDegree(); } //删除第index边及其相关边 public Node removeNode(int deleteIndex) { Node node = allNodes[deleteIndex]; if(node == null) { throw new RuntimeException("节点为空"); } //先删除节点 allNodes[deleteIndex] = null; //删除所有相关边 for(int i=0;i<size;i++) { //该节点作为一条弧的弧头(即指向该节点的弧) if(nodeGraphs[deleteIndex][i] != null) { nodeGraphs[deleteIndex][i] = null; allNodes[i].delInDegree(); } //该节点作为一条弧的弧尾(即该节点出去的弧) if(nodeGraphs[i][deleteIndex] != null) { nodeGraphs[i][deleteIndex] = null; allNodes[i].delOutDegree(); } } return node; } //设置一个从节点row为起点到col的弧 public void setMatrix(int row, int col, Edge val) { nodeGraphs[row][col] = val; allNodes[row].addOutDegree(); allNodes[col].addInDegree(); } //重置状态 public void reload() { for(int i=0;i<size;i++) { allNodes[i].setStatus(0); allNodes[i].setParent(-1); } }}
性能
时间性能:
按照代码的实现方式,各顶点的编号可直接转换为其在邻接矩阵中对应的顶点,从而使得图中所有的静态操作接口,均只需O(1)时间。弧的静态和动态操作也仅需O(1)时间,其代价是邻接矩阵的空间冗余。
邻接矩阵的不足主要体现在,顶点的动态操作接口均十分耗时。为了插入新的顶点,顶点集向量allNodes[]需要添加一个元素;弧集向量nodeGraphs[][]也需要增加一行,且每行都需要添加一个元素。顶点删除操作,亦与此类似。不难看出,这些恰恰也是数组结构固有的不足。
但在通常的算法中,顶点的动态操作远少于其它操作。而且,即便计入向量扩容的代价,就分摊意义而言,单次操作的耗时亦不过O(n)
空间性能:
上述实现方式所用空间,主要消耗于邻接矩阵,亦即其中的二维弧集向量nodeGraphs[][]。每个Edge对象虽需记录多项信息,但总体不过常数。根据我们扩容的算法根据其数组长度始终不低于50%,故空间总量渐进地不超过O(n*n)= O(n^2)
当然,对于无向图而言,仍有改进的余地。如上图a所示,无向图的邻接矩阵必为对称阵,其中除自环以外的每条边,都被重复地存放了两次。也就是说,近一半的单元都是冗余的。为消除这一缺点,可采用压缩存储等技巧,进一步提高空间利用率。
使用邻接矩阵实现一个图
现在使用刚刚笔者定义的Node,Edge和AdjacencyMatrix定义一个图
public static void main(String[] args) { int aIndex = matrix.addNode(new Node<String>("a")); //索引为0 int bIndex = matrix.addNode(new Node<String>("b")); //索引为1 int cIndex = matrix.addNode(new Node<String>("c")); //索引为2 int dIndex = matrix.addNode(new Node<String>("d")); //索引为3 int eIndex = matrix.addNode(new Node<String>("e")); //索引为4 int fIndex = matrix.addNode(new Node<String>("f")); //索引为3 int gIndex = matrix.addNode(new Node<String>("g")); //索引为4 matrix.setMatrix(aIndex, bIndex, new Edge<String>("这是边a到b的弧",1)); matrix.setMatrix(aIndex, cIndex, new Edge<String>("这是边a到c的弧",1)); matrix.setMatrix(bIndex, dIndex, new Edge<String>("这是边b到d的弧",1)); matrix.setMatrix(bIndex, eIndex, new Edge<String>("这是边b到e的弧",1)); matrix.setMatrix(cIndex, fIndex, new Edge<String>("这是边c到f的弧",1)); matrix.setMatrix(cIndex, gIndex, new Edge<String>("这是边c到g的弧",1)); //打印节点的出度和入度 System.out.println("a:"+matrix.getInDeep(aIndex)); System.out.println("a:"+matrix.getOutDeep(aIndex)); System.out.println("b:"+matrix.getInDeep(bIndex)); System.out.println("b:"+matrix.getOutDeep(bIndex)); System.out.println("c:"+matrix.getInDeep(cIndex)); System.out.println("c:"+matrix.getOutDeep(cIndex)); System.out.println("d:"+matrix.getInDeep(dIndex)); System.out.println("d:"+matrix.getOutDeep(dIndex)); }
- 图的邻接矩阵实现
- 图的邻接矩阵实现
- 图的邻接矩阵实现
- 图的邻接矩阵实现
- 图的邻接矩阵实现
- 图的邻接矩阵实现
- 图的邻接矩阵的实现
- 图的ADT邻接矩阵实现
- 数据结构:图的实现--邻接矩阵
- 数据结构:图的实现--邻接矩阵
- 图的邻接矩阵代码实现
- 图的邻接矩阵实现_MGraph
- 图的邻接矩阵类实现
- 数据结构---图的邻接矩阵实现
- 图的邻接矩阵存储实现
- dj的邻接矩阵实现
- 图的邻接矩阵的C++实现
- 图的实现 邻接矩阵+无向图
- protobuf开发环境搭建
- Android Studio 打开工程卡在‘Gradle:Resolvedependencies':app:_debugCompile' 问题
- Centos生成火车(提升Linux兴趣)
- Unable to parse template "Annotation" Error message: This template did not produce a Java class or a
- 新手村 简单字符串 ISBN号码
- 图的邻接矩阵的实现
- 1034. 有理数四则运算(20) PAT乙级真题
- golang之string标准库(二)
- Fresco图片加载框架
- openCV学习之路【3】--一篇文章读懂OpenCV3
- HDOJ 1018 数学(N的阶乘)
- Ubuntu下安装并配置VS Code编译C++
- 2017秋招之面试整理(3)
- GreenDao,注解,RecyclerView,EventBus,Retrofit展示列表