作者: 暮想sun | 来源:发表于2019-12-27 18:12 被阅读0次

    图是由定点的有穷非空集合和顶点之间的边的集合组成,通常表示为:G(V,E),其中G表示一个图,V是图G中顶点的边集,E是图中边的集合。
    图按照有无方向分为有向图和无向图。无向图由顶点和边构成,有向图由顶点和弧构成。开始点为弧尾,结束点为弧头。
    图按照边的多少分稀疏图和稠密图。如果任意两个顶点之间都存在边叫完全图,有向的叫做有向完全图。若无重复的边或顶点到自身的边叫做简单图。
    图的边或弧相关的数叫做权,这种带权的图通称为网。
    图中顶点间存在路径,两顶点存在路径则说明是连通的,如果路径最终回到起始点则称为环,当中不重复叫简单路径。若任意两顶点都是连通的,则图就是连通图,有向则称为强连通图。图中有子图,若子图极大连通则就是连通分量,有向的则称强连通分量。

    ———————————————————图的存储结构—————————————————
    1.邻接矩阵

    用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组存储图中边或弧的信息


    /**
       * 一维数组,存储顶点
       */
      private Object[] elementData;
    
      /**
       * 二维数组存储边
       */
      private int[][] edges;
    
      /**
       * 顶点数量
       */
      private int size;
      public AdjacencyMatrixGraph(int initSize) {
    
          size = 0;
          elementData = new Object[initSize];
          edges = new int[initSize][initSize];
          visited = new Boolean[initSize];
      }
    
     //插入顶点
      public void insertV(Object o) {
          visited[size] = false;
          elementData[size] = o;
          size++;
      }
      //删除顶点
      public void deleteV(Object o) {
          int index = indexOf(o);
          if (index == -1) {
              throw new RuntimeException("顶点数据不存在");
          }
    
          /**
           * 查找存在包含该顶点的边
           */
          for (int i = 0; i < size; i++) {
              deleteEdge(i, index);
              deleteEdge(index, i);
          }
    
          visited[index] = false;
          size--;
    
      }
    
      public int indexOf(Object o) {
          for (int i = 0; i < size; i++) {
              if (o.equals(elementData[i])) {
                  return i;
              }
          }
          return -1;
      }
      //删除边
      public void deleteEdge(int v1, int v2) {
          judge(v1, v2);
          edges[v1][v2] = 0;
          edges[v2][v2] = 0;
      }
    
      private void judge(int v1, int v2) {
          if (v1 < 0 || v2 < 0 || v1 >= size || v2 >= size) {
              throw new RuntimeException("顶点索引不正确");
          }
      }
      //插入边
      public void insertEdge(int v1, int v2) {
          judge(v1, v2);
          edges[v1][v2] = 1;
          edges[v2][v1] = 1;
    
      }
    
      public int getEdge(int v1, int v2) {
          judge(v1, v2);
          return edges[v1][v2];
      }
    
    

    2.邻接表

    (1)图中顶点用一个一维数组存储
    (2)图中每个顶点vi的所有邻接点构成一个线性表。


        private static class EdgeNode {
    
            /**
             * 该节点的下标位置
             */
            private int adjvex;
    
            /**
             * 权值
             */
            private int weight;
    
            /**
             * 下一个边节点的指针
             */
            private EdgeNode edgeNode;
    
            public EdgeNode(int adjvex, int weight, EdgeNode edgeNode) {
                this.adjvex = adjvex;
                this.weight = weight;
                this.edgeNode = edgeNode;
            }
    
            public EdgeNode(int adjvex, int weight) {
                this.adjvex = adjvex;
                this.weight = weight;
            }
    
            public EdgeNode(int adjvex) {
                this.adjvex = adjvex;
            }
    
            public EdgeNode() {
            }
        }
    
        private static class VertexNode {
            private Object data;
    
            private EdgeNode edgeNode;
    
            public VertexNode() {
            }
    
            public VertexNode(Object data) {
                this.data = data;
            }
    
            public VertexNode(Object data, EdgeNode edgeNode) {
                this.data = data;
                this.edgeNode = edgeNode;
            }
        }
    
        private VertexNode[] nodes;
    
        /**
         * 实际顶点数量
         */
        private int size;
        public AdjListGraph(int initSize) {
            nodes = new VertexNode[initSize];
            size = 0;
            visited = new Boolean[initSize];
        }
        //插入顶点
        public void insertV(Object o) {
            VertexNode vertexNode = new VertexNode(o);
            nodes[size] = vertexNode;
            visited[size] = false;
            size++;
        }
        //删除顶点
        public void removeV(int v) {
            if (v < 0 || v >= size) {
                throw new RuntimeException("顶点索引不正确");
            }
    
            for (int i = 0; i < size; i++) {
                deleteE(i, v);
                deleteE(v, i);
            }
    
        }
         //插入边
        public void insertE(int v1, int v2, int weight) {
            judge(v1, v2);
    
            handleVertexNode(v1, v2, weight);
    
            handleVertexNode(v2, v1, weight);
    
        }
    
        private void handleVertexNode(int v1, int v2, int weight) {
            VertexNode v1VertexNode = nodes[v1];
    
            if (v1VertexNode.edgeNode == null) {
                v1VertexNode.edgeNode = new EdgeNode(v2, weight);
            } else {
                v1VertexNode.edgeNode = new EdgeNode(v2, weight, v1VertexNode.edgeNode);
            }
        }
        //删除边
        public void deleteE(int v1, int v2) {
            judge(v1, v2);
    
            fastRemoveE(v1, v2);
    
            fastRemoveE(v2, v1);
    
        }
    
        private void fastRemoveE(int v1, int v2) {
    
            //如果是顶点连接的第一个邻接节点,需将顶点的邻接重置
            EdgeNode edgeNode = nodes[v1].edgeNode;
            if (edgeNode != null && edgeNode.adjvex == v2) {
                nodes[v1].edgeNode = edgeNode.edgeNode;
            } else {
    
                //判断是不是链表上非第一个
                EdgeNode previous = null;
                edgeNode = edgeNode.edgeNode;
                while (edgeNode != null && edgeNode.adjvex != v2) {
    
                    previous = edgeNode;
                    edgeNode = edgeNode.edgeNode;
                }
    
                if (edgeNode != null) {
                    previous.edgeNode = edgeNode.edgeNode;
    
                }
            }
    
        }
    
        private void judge(int v1, int v2) {
            if (v1 < 0 || v2 < 0 || v1 >= size || v2 >= size) {
                throw new RuntimeException("顶点索引不正确");
            }
        }
    

    3.十字链表

    4.邻接多重表

    5.边集数组

    由两个一维数组构成。一个存储顶点的信息,另一个存储边的信息,这边数组每个数据元素由一条边的起点下标、终点下标和权值组成。


    ———————————————————图的遍历———————————————————
    1.深度优先遍历

    从图中某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。

    邻接矩阵深度优先遍历:递归

        /**
         * 节点是否被遍历
         */
        private Boolean[] visited;
      /**
         * 邻接矩阵的深度优先算法
         * 选定一个顶点进行遍历,判断是否被遍历到
         * 如果为遍历到,则遍历这个节点相关联的边节点
         */
        public void dfs() {
    
            for (int i = 0; i < size; i++) {
    
                if (!visited[i]) {
                    dfs(i);
                }
            }
    
        }
    
        /**
         * 从第i个节点进行深度遍历
         *
         * @param i
         */
        private void dfs(int i) {
            visited[i] = true;
            System.out.println(elementData[i] + "   ");
    
            for (int j = 0; j < size; j++) {
                if (edges[i][j] == 1 && !visited[j]) {
                    dfs(j);
                }
            }
    
        }
    

    邻接矩阵深度优先遍历:非递归(使用栈)

    private Boolean[] visited;
     private Stack<Object> stack = new Stack<>();
     public void stackDfs() {
    
            visited[0] = true;
            System.out.println(elementData[0]);
    
            stack.push(elementData[0]);
    
            int index = 0;
            while (!stack.isEmpty()) {
                int v = getNextIndex(index);
                if (v == -1) {
                    stack.pop();
                } else {
                    visited[v] = true;
    
                    System.out.println(elementData[v]);
    
                    stack.push(elementData[v]);
    
                    index = v;
    
                }
            }
    
        }
    
        public int getNextIndex(int index) {
    
            for (int i = 0; i < size; i++) {
                if (edges[index][i] == 1 && !visited[i]) {
                    return i;
                }
            }
    
            return -1;
        }
    

    邻接表深度优先遍历:递归

        private Boolean[] visited;
    
       /**
         * 邻接链表的深度优先算法
         *
         * 判断相邻的边的下标是否被遍历到,如果未遍历,则从这个节点开始遍历
         *
         *
         * 递归算法实现
         */
        public void dfs() {
    
            for (int i = 0; i < size; i++) {
                if (!visited[i]) {
                    dfs(i);
                }
            }
    
        }
    
        public void dfs(int i) {
    
            visited[i] = true;
            System.out.println(nodes[i].data);
    
            EdgeNode edgeNode = nodes[i].edgeNode;
            while (edgeNode != null) {
                if (!visited[edgeNode.adjvex]) {
                    dfs(edgeNode.adjvex);
                }
                edgeNode = edgeNode.edgeNode;
            }
        }
    

    邻接表深度优先遍历:栈

        private Boolean[] visited;
    
        private Stack<VertexNode> vertexNodeStack = new Stack<>();
    
        /**
         * 使用栈对链表进行遍历
         */
        public void stackDfs() {
    
            visited[0] = true;
    
            System.out.println(nodes[0].data);
            vertexNodeStack.push(nodes[0]);
    
            //栈不空,则判断栈顶元素的向邻接节点
            while (!vertexNodeStack.isEmpty()) {
                int adjListIndex = getIndex(vertexNodeStack.peek());
                if(adjListIndex == -1){
                    vertexNodeStack.pop();
                }else {
                    visited[adjListIndex] = true;
                    System.out.println(nodes[adjListIndex].data);
    
                    vertexNodeStack.push(nodes[adjListIndex]);
                }
    
            }
    
        }
    
        public int getIndex(VertexNode v) {
            EdgeNode edgeNode = v.edgeNode;
            while (edgeNode != null) {
                if (!visited[edgeNode.adjvex]) {
                   return edgeNode.adjvex;
                }
                edgeNode = edgeNode.edgeNode;
            }
    
            return  -1;
        }
    
    

    2.广度优先遍历

    一层一层的顶点访问

    邻接矩阵广度优先遍历:

      private Queue<Object> objectQueue = new ArrayDeque<>();
    
        /**
         * 队列实现广度优先遍历算法
         *
         * 类似层序遍历
         *
         * 先入队列,则先出队列
         */
        public void bfs() {
            visited[0] = true;
    
            objectQueue.add(elementData[0]);
            System.out.println(elementData[0]);
    
            int index = 0;
            while (!objectQueue.isEmpty()) {
    
                objectQueue.remove();
    
                for (int i = 0; i < size; i++) {
                    if (edges[index][i] == 1 && !visited[i]) {
    
                        visited[i] = true;
    
                        objectQueue.add(elementData[i]);
    
                        System.out.println(elementData[i]);
                    }
                }
    
            }
    
        }
    

    邻接表广度优先遍历:

       private Queue<VertexNode> vertexNodeQueue = new ArrayDeque<>();
    
        /**
         * 队列实现广度优先遍历算法
         *
         * 类似层序遍历
         *
         * 先入队列,则先出队列
         */
        public void bfs() {
            visited[0] = true;
    
            vertexNodeQueue.add(nodes[0]);
            System.out.println(nodes[0].data);
    
            int index = 0;
            while (!vertexNodeQueue.isEmpty()) {
    
                vertexNodeQueue.remove();
    
                EdgeNode edgeNode = nodes[index].edgeNode;
                while (edgeNode !=null){
                    if(!visited[edgeNode.adjvex]){
                        vertexNodeQueue.add(nodes[edgeNode.adjvex]);
    
                        visited[edgeNode.adjvex] = true;
                        System.out.println(nodes[edgeNode.adjvex].data);
                    }
    
                    edgeNode = edgeNode.edgeNode;
    
                }
    
            }
    
        }
    

    相关文章

      网友评论

        本文标题:

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