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

            }

        }

    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350