美文网首页程序员数据结构和算法分析
图的邻接多重表的Java实现(包含删除操作)

图的邻接多重表的Java实现(包含删除操作)

作者: fanyank | 来源:发表于2017-12-09 18:59 被阅读251次
    优点 缺点
    邻接矩阵 实现简单,能同时求任意顶点的出度和入度 在存储稀疏图时会造成空间浪费
    邻接表 使用数组链表实现,不会造成空间浪费 不能同时求出任意顶点的出度和入度, 除非同时构建邻接表和逆邻接表,对边 进行操作时需要操作两次
    十字链表 使用数组链表实现,不会造成空间浪费 同时可以求出某个顶点的入度和出度 对边进行操作时需要操作两次
    邻接多重表 对边的操作进行了优化,操作边的次数由两次 减少到了一次 删除操作较为复杂

    最近关于图的实现我总结了一下,总共有4种实现。

    优点 缺点
    邻接矩阵 实现简单,能同时求任意顶点的出度和入度 在存储稀疏图时会造成空间浪费
    邻接表 使用数组链表实现,不会造成空间浪费 不能同时求出任意顶点的出度和入度, 除非同时构建邻接表和逆邻接表,对边 进行操作时需要操作两次
    十字链表 使用数组链表实现,不会造成空间浪费 同时可以求出某个顶点的入度和出度 对边进行操作时需要操作两次
    邻接多重表 对边的操作进行了优化,操作边的次数由两次 减少到了一次 删除操作较为复杂

    本文节选了邻接多重表的实现。虽然网上有很多实现,但是大多数并没有删除操作,这里给出了删除操作的实现,如果有更好的方法欢迎提出。
    更多上述实现的代码已托管在https://github.com/Fish-Fan/DataStructure/tree/master/src/com/company
    系作者原创,转载请务必注明出处.

    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.List;
    
    /**
     * Created by yanfeng-mac on 2017/12/9.
     * 无向图的邻接多重表的实现
     * 优点: 使用邻接表在对边进行操作时(如删除),需要两次操作,因为一条边在两个链表里面存储,
     * 而邻接多重表的优点就在于对边操作时只需要一次操作,这就意味着边只存储一次
     */
    public class GraphMutiplyTable {
        /**
         * vName-->顶点名称
         * firstEdge-->顶点边链表的头结点
         */
        private static class Vertex {
            private String vName;
            private Edge firstEdge;
    
            public Vertex() {}
            public Vertex(String name) {
                this.vName = name;
            }
        }
    
        /**
         * iVex-->边的其中一个顶点A
         * iLink-->边中顶点A的边链表的指针
         * jVex-->边的另一个顶点B
         * jLink-->边中顶点B的边链表的指针
         */
        private static class Edge {
            private int iVex;
            private Edge iLink;
            private int jVex;
            private Edge jLink;
    
            public Edge() {}
            public Edge(int iVex,int jVex) {
                this.iVex = iVex;
                this.jVex = jVex;
            }
        }
    
        private static class GraphEdge {
            private String vex;
            private String otherVex;
    
            public GraphEdge(String vexName,String otherVexName) {
                this.vex = vexName;
                this.otherVex = otherVexName;
            }
        }
    
        private List<Vertex> vertexArr;
    
        public void init(String[] vexArr,List<GraphEdge> edgeList) {
            initVexArr(vexArr);
            initEdge(edgeList);
        }
    
        private void initVexArr(String[] vexArr) {
            vertexArr = new ArrayList<Vertex>(vexArr.length);
            for(int i = 0;i < vexArr.length;i++) {
                Vertex vertex = new Vertex(vexArr[i]);
                vertexArr.add(vertex);
            }
        }
    
        private void initEdge(List<GraphEdge> edgeList) {
            for(int i = 0;i < edgeList.size();i++) {
                GraphEdge graphEdge = edgeList.get(i);
                if(contains(graphEdge.vex) && contains(graphEdge.otherVex)) {
                    //获取顶点的下标
                    int vIndex = getVexIndex(graphEdge.vex);
                    int oIndex = getVexIndex(graphEdge.otherVex);
                    //获取顶点
                    Vertex vertex = vertexArr.get(vIndex);
                    Vertex oVertex = vertexArr.get(oIndex);
                    //构造两个顶点的边
                    Edge edge = new Edge(vIndex,oIndex);
                    //头插法插入vertex的边
                    if(vertex.firstEdge == null) {
                        vertex.firstEdge = edge;
                    } else {
                        Edge vexNextEdge = vertex.firstEdge;
                        edge.iLink = vexNextEdge;
                        vertex.firstEdge = edge;
                    }
                    //头插法插入oVertex的边
                    if(oVertex.firstEdge == null) {
                        oVertex.firstEdge = edge;
                    } else {
                        Edge oVexNextEdge = oVertex.firstEdge;
                        edge.jLink = oVexNextEdge;
                        oVertex.firstEdge = edge;
                    }
    
                 
                }
    
            }
        }
    
        private boolean contains(String vName) {
            for(Vertex vertex : vertexArr) {
                if(vertex.vName.equals(vName))
                    return true;
            }
            return false;
        }
    
        private int getVexIndex(String vName) {
            for(int i = 0;i < vertexArr.size();i++) {
                if(vertexArr.get(i).vName.equals(vName))
                    return i;
            }
            return -1;
        }
    
        public void print() {
            for(Vertex vertex : vertexArr) {
                System.out.println("顶点 " + vertex.vName + " 的所有边: ");
                int vIndex = getVexIndex(vertex.vName);
                Edge cursor = vertex.firstEdge;
                while (cursor != null) {
                    System.out.print(cursor.iVex + "---" + cursor.jVex + " ||");
                    if(cursor.iVex == vIndex) {
                        cursor = cursor.iLink;
                    } else {
                        cursor = cursor.jLink;
                    }
                }
    
                System.out.println();
            }
        }
    
        //删除边
        /**
         * 删除边的整体思路
         * 1.找到边上的两个顶点A和B
         * 2.分别遍历AB的边链表,直到找到要删除的边S
         * 3.分别将边S及边S的前驱边PS的数据存储起来,这里A的要删除的边为cursor,前驱边为preCursor,B的要删除的边为oCursor,前驱边为oPreCursor
         * 4.调整链表结构
         * @param graphEdge
         */
        public void remove(GraphEdge graphEdge) {
            String vName = graphEdge.vex;
            String oName = graphEdge.otherVex;
            int vIndex = getVexIndex(vName);
            int oIndex = getVexIndex(oName);
            //处理边的第一个顶点A
            Vertex vertex = vertexArr.get(vIndex);
            Edge cursor = vertex.firstEdge;
            //指针的前驱
            Edge preCursor = null;
    
            while (cursor != null) {
                if(cursor.iVex == oIndex || cursor.jVex == oIndex) {
                    //通过遍历顶点A的所有边,找到边的前驱指针
                    break;
                }
                preCursor = cursor;
                if(cursor.iVex == vIndex) {
                    cursor = cursor.iLink;
                } else {
                    cursor = cursor.jLink;
                }
            }
    
            //处理边的第二个顶点B
            Vertex oVertex = vertexArr.get(oIndex);
            Edge oCursor = oVertex.firstEdge;
            //指针的前驱
            Edge oPreCursor = null;
    
            while (oCursor != null) {
                if(oCursor.iVex == vIndex || oCursor.jVex == vIndex) {
                    //通过遍历顶点B的所有边,找到边的前驱指针
                    break;
                }
                oPreCursor = oCursor;
                if(oCursor.iVex == oIndex) {
                    oCursor = oCursor.iLink;
                } else {
                    oCursor = oCursor.jLink;
                }
            }
    
            if(preCursor != null) {
                if(preCursor.iVex == vIndex && cursor.iVex == vIndex) {
                    preCursor.iLink = cursor.iLink;
                } else if(preCursor.iVex == vIndex && cursor.jVex == vIndex) {
                    preCursor.iLink = cursor.jLink;
                } else if(preCursor.jVex == oIndex && cursor.iVex == vIndex) {
                    preCursor.jLink = cursor.iLink;
                } else {
                    preCursor.jLink = cursor.jLink;
                }
            } else {
                if(cursor.iVex == vIndex) {
                    vertex.firstEdge = cursor.iLink;
                } else {
                    vertex.firstEdge = cursor.jLink;
                }
            }
    
            if(oPreCursor != null) {
                if(oPreCursor.iVex == oIndex && oCursor.iVex == oIndex) {
                    oPreCursor.iLink = oCursor.iLink;
                } else if(oPreCursor.iVex == oIndex && oCursor.jVex == oIndex) {
                    oPreCursor.iLink = oCursor.jLink;
                } else if(oPreCursor.jVex == oIndex && oCursor.iVex == oIndex) {
                    oPreCursor.jLink = oCursor.iLink;
                } else {
                    oPreCursor.jLink = oCursor.jLink;
                }
            } else {
                if(oCursor.iVex == oIndex) {
                    oVertex.firstEdge = oCursor.iLink;
                } else {
                    oVertex.firstEdge = oCursor.jLink;
                }
            }
    
    
        }
    
        public static void main(String[] args) {
            GraphMutiplyTable graph = new GraphMutiplyTable();
            String[] vexArr = {"V0","V1","V2","V3"};
            GraphEdge edge = new GraphEdge("V0","V1");
            GraphEdge edge1 = new GraphEdge("V0","V2");
            GraphEdge edge2 = new GraphEdge("V0","V3");
            GraphEdge edge3 = new GraphEdge("V1","V2");
            GraphEdge edge4 = new GraphEdge("V2","V3");
    
            List<GraphEdge> edgeList = new ArrayList<GraphEdge>();
            edgeList.add(edge);
            edgeList.add(edge1);
            edgeList.add(edge2);
            edgeList.add(edge3);
            edgeList.add(edge4);
    
            graph.init(vexArr,edgeList);
            graph.remove(edge3);
            graph.print();
        }
    
    
    
    
    }
    

    相关文章

      网友评论

        本文标题:图的邻接多重表的Java实现(包含删除操作)

        本文链接:https://www.haomeiwen.com/subject/cmdpixtx.html