图的表示方法:
邻接矩阵法(太浪费空间,但确实把所有信息都表示了出来,计算度或者各种操作的话很方便,当然用无向图的对称性、只存放非0结点可以优化)
邻接表(算比较好的一种方法,但是对于有向图计算入度必须遍历,用逆邻接表、十字链表优化)
十字链表(和邻接表太类似)
遍历算法:
图是标准的多对多结构,因此不构成环并且不重复访问vertex需要一个标志数组visited,循环这个保证全部都访问,遍历过程中可以有DFS和BFS
DFS:递归方式,首先访问vertex,循环找邻接节点,找到并且该vertex没有被访问过,将顶点当成参数进入下一层。
BFS:需要堆栈配合,找到自己邻接的顶点如果没有被访问过则访问并将其入栈,然后用stack非空控制循环直到全部访问过。
还有一些计算连通分量个数、度的问题,就是遍历算法的应用。
代码(结构写的有点乱,回头看的时候可以优化下):
- package nuaa.ds;
- public class Graph {
- private String[] vertexes;//邻接矩阵法所
- private int[][] vr; //使用的成员变量
- private CreateGraph g;
- private VertexNode[] vertexNodes;//邻接表所使用的成员变量
- /**
- * 图的构成实在太多,写个标准无向图熟悉算法
- * 顶点ABCD(其中BC不连)彼此互连,EF彼此互连
- */
- public Graph(CreateGraph c){
- this.g = c;
- switch(c){
- case AdjacencyMatrix://邻接矩阵
- vertexes = new String[]{"A","B","C","D","E","F"};//ABCDEF6个顶点
- vr = new int[6][6];
- vr[0] = new int[]{0,1,1,1,0,0};//邻接矩阵法表示边,相连值为1,不连值为0
- vr[1] = new int[]{1,0,0,1,0,0};//优点是所有信息全部显示出来
- vr[2] = new int[]{1,0,0,1,0,0};//这样顶点的度等计算或者对图的所有操作都比较容易
- vr[3] = new int[]{1,1,1,0,0,0};//缺点也很明显
- vr[4] = new int[]{0,0,0,0,0,1};//存储空间极大的浪费
- vr[5] = new int[]{0,0,0,0,1,0};//当然通过使用对称或者只存储非0元素可以优化
- break;
- case AdjacencyTable://邻接表表示,省了很多存储空间,
- //但有时会带来不便,比如求有向图的某个节点的入度
- //结点数组的初始化
- vertexNodes = new VertexNode[6];
- String[] temp = new String[]{"A","B","C","D","E","F"};
- for(int i=0;i<vertexNodes.length;i++){
- vertexNodes[i] = new VertexNode(temp[i]);
- }
- //结点数组连接的边结点的初始化
- //写de好麻烦,还不如由输入流输进来循环构造。但每次测试重新输入图结构也很烦。
- //顶点A的初始化
- vertexNodes[0].arcNode = new ArcNode(1);
- vertexNodes[0].arcNode.arcNode = new ArcNode(2);
- vertexNodes[0].arcNode.arcNode.arcNode = new ArcNode(3);
- //顶点B的初始化
- vertexNodes[1].arcNode = new ArcNode(0);
- vertexNodes[1].arcNode.arcNode = new ArcNode(3);
- //顶点C的初始化
- vertexNodes[2].arcNode = new ArcNode(0);
- vertexNodes[2].arcNode.arcNode = new ArcNode(3);
- //顶点D的初始化
- vertexNodes[3].arcNode = new ArcNode(0);
- vertexNodes[3].arcNode.arcNode = new ArcNode(1);
- vertexNodes[3].arcNode.arcNode.arcNode = new ArcNode(2);
- //顶点E的初始化
- vertexNodes[4].arcNode = new ArcNode(5);
- //顶点F的初始化
- vertexNodes[5].arcNode = new ArcNode(4);
- }
- }
- //求连通分量个数或者连通生成树也是遍历算法
- public void traversalByDFS(){
- this.traversalByDFS(this.g);
- }
- private void traversalByDFS(CreateGraph c){//深度优先算法访问图
- boolean[] visited;
- if(c==CreateGraph.AdjacencyTable){
- visited = new boolean[vertexNodes.length];//java默认都为false
- }else{
- visited = new boolean[vertexes.length];//java默认都为false
- }
- switch(c){
- case AdjacencyMatrix://图是用邻接表表示的深度优先遍历算法
- for(int i=0;i<vertexes.length;i++){
- if(!visited[i]){
- mTraversalByDFS(visited,i);
- }
- }
- break;
- case AdjacencyTable:
- for(int i=0;i<vertexNodes.length;i++){
- if(!visited[i]){
- tTraversalByDFS(visited,i);
- }
- }
- }
- }
- private void mTraversalByDFS(boolean[] visited,int index){
- visit(vertexes[index]);//和二叉树一样的问题,访问图结点可能会做些其他操作
- //还是从外部当成函子传入进来比较合适
- //对应的vertexes数组也要改成Object[],这样的封装更好
- visited[index] = true;
- for(int i=0;i<vr[index].length;i++){//循环找到下一个结点
- if(vr[index][i]==1&&!visited[i]){//有连接的未访问的结点
- mTraversalByDFS(visited,i);//访问该结点
- }
- }
- }
- private void tTraversalByDFS(boolean[] visited,int index){
- visit(vertexNodes[index]);
- visited[index] = true;
- ArcNode p = vertexNodes[index].arcNode;
- while(null!=p){
- if(!visited[p.serialNumber]){
- tTraversalByDFS(visited,p.serialNumber);
- }
- p = p.arcNode;
- }
- }
- public void traversalByBFS(){
- this.traversalByBFS(g);
- }
- private void traversalByBFS(CreateGraph c){
- boolean[] visited;
- if(c==CreateGraph.AdjacencyTable){
- visited = new boolean[vertexNodes.length];//java默认都为false
- }else{
- visited = new boolean[vertexes.length];//java默认都为false
- }
- Stack<VertexNode> stack = new Stack<VertexNode>();
- switch(c){
- case AdjacencyMatrix://图是用邻接表表示的深度优先遍历算法
- for(int i=0;i<vertexes.length;i++){
- if(!visited[i]){
- mTraversalByBFS(visited,i);
- }
- }
- break;
- case AdjacencyTable:
- for(int i=0;i<vertexNodes.length;i++){
- if(!visited[i]){
- tTraversalByBFS(stack,visited,i);
- }
- }
- }
- }
- private void tTraversalByBFS(Stack<VertexNode> stack,
- boolean[] visited,int index){
- stack.push(vertexNodes[index]);
- visit(vertexNodes[index]);
- visited[index] = true;
- ArcNode p ;
- while(!stack.isEmpty()){
- p = stack.pop().arcNode;
- while(p!=null){
- if(!visited[p.serialNumber]){
- stack.push(vertexNodes[p.serialNumber]);
- visit(vertexNodes[p.serialNumber]);
- visited[p.serialNumber] = true;
- }
- p = p.arcNode;
- }
- }
- }
- private void mTraversalByBFS(boolean[] visited,int index){
- Stack<Integer> stack = new Stack<Integer>();
- stack.push(index);
- visit(vertexes[index]);
- visited[index] = true;
- while(!stack.isEmpty()){
- for(int i=0;i<vr[index].length;i++){
- if(vr[index][i]==1&&!visited[i]){
- stack.push(i);
- visit(vertexes[i]);
- visited[i] = true;
- }
- }
- index = stack.pop();
- }
- }
- private void visit(String vertex){
- System.out.print(vertex+" ");
- }
- private void visit(VertexNode node){
- System.out.println(node.vertex);
- }
- class VertexNode{
- String vertex;
- ArcNode arcNode;
- public VertexNode(String vertex){
- this.vertex = vertex;
- }
- }
- class ArcNode{
- int serialNumber;//存放边连着的节点在vertexNode数组里面的位置
- ArcNode arcNode;
- public ArcNode(int serialNumber){
- this.serialNumber = serialNumber;
- }
- }
- }
一些盲点: 十字链表表示法太相似没有写,邻接矩阵法用广义表的方式对空间进行优化,然后计算连通分量、度可以用来复习下遍历算法。