图是由定点的有穷非空集合和顶点之间的边的集合组成,通常表示为: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 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 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 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 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;

            }

        }

    }

你可能感兴趣的:(图)