zoukankan      html  css  js  c++  java
  • 数据结构(十):图

     

    一、 图概述

      日常生活中使用的地图导航,每个城市看做一个顶点,城市与城市间连通的线路看做联通的边,就组成了图。除了导航,迷宫,电路板等等也是图,需要用图这种数据结构去解决很多连通问题。

    二、 图的特性

      图的定义:图是有一组顶点和一组能将顶点相连的边组成的数据结构

       

       

       

      特殊的图:

      平行边:连接同一对顶点的多条边

      自环:一条出口和入口都来自同一个顶点的边

       

      图的分类:

      无向图:连接顶点的边是无方向无意义的图

      有向图:连接顶点的边具有方向的图

      相邻顶点:两个顶点通过一条边相连时为相邻顶点

      度:顶点边的数量

      子图:图所有边和相连顶点的子集

      路径:A顶点到B顶点若干顺序连接的边

      环:起点和终点相同,并且至少含有一条边的路径

      连通图:图中每一个顶点都存在一条边到达另外一个顶点,称为连通图

       

      连通子图:一幅非连通图由若干连通部分组成,该连通部分称为连通子图

       

    三、 图的实现

      3.1 邻接矩阵

      图的数据结构可以通过二维数组来表示,即邻接矩阵,如下图生成一个8*8的二维数组,通过graph[7][8]=1和graph[8][7]=1来表示顶点7和8的连通。

       

      

      但该方式存在着内存空间使用率低的问题,N个顶点图中,得初始化N^2规模的二维数组,空间复杂度是不可接受的。

      3.2 邻接表

      使用一个顶点数量相同大小的数组Queue[],索引作为图的顶点,索引对应的队列存放与顶点相连通的其他顶点,用这种方式来表示图称为邻接表。邻接表相比邻接矩阵可以大大的节省内存空间。

             

      图的邻接表实现:

    /**

     * 图的实现

     * @author jiyukai

     */

    public class Graph {

           //定点个数

           public int V;

          

           //边的数量

           public int E;

          

           //图的邻接表

           public Queue<Integer>[] qTable;

          

           public Graph(int v) {

                  this.V = v;

                 

                  this.E = 0;

                 

                  //初始化邻接表,数组中的索引为顶点,值为已队列,存放相邻的顶点

                  qTable = new Queue[v];

                  for(int i=0;i<v;i++) {

                         qTable[i] = new Queue<Integer>();

                  }

           }

          

           /**

            * 向图中添加一条边

            * @param v

            * @param w

            */

           public void addEdge(int v,int w) {

                  //顶点v添加w的指向

                  qTable[v].enqueue(w);

                 

                  //顶点w添加v的指向

                  qTable[w].enqueue(v);

                 

                  //边加1

                  E++;

           }

          

           /**

            * 返回当前顶点的数量

            * @return

            */

           public int V() {

                  return V;

           }

          

           /**

            * 返回当前边的数量

            * @return

            */

           public int E() {

                  return E;

           }

          

           /**

            * 获取与顶点V相邻的顶点

            * @param V

            * @return

            */

           public Queue adjoin(int V) {

                  return qTable[V];

           }

    }


    四、
    深度优先搜索

      图的搜索算法中,深度优先搜索指的是搜索到的顶点既有子结点又有兄弟结点,优先搜索子结点。如下演示为图的深度优先搜索顺序

       

       

      为了不对已搜索过的顶点重复搜索,我们需要有个布尔类型的数组来标记顶点是否被搜索过,搜索过的就标记为true,不再进行深度搜索,提高效率的同时,该数组也能判断顶点A到B是否相通

      因为相通的代表搜索过,会被标记为true。

    /**

     * 深度优先搜索

     * @author jiyukai

     */

    public class DepthSearch {

           //标记顶点x是否有被搜索过

           public boolean[] flags;

          

           public int count;

          

           /**

            * 深度优先搜索,找出与顶点V想通的所有顶点

            * @param G

            * @param V

            */

           public DepthSearch(Graph G,int V) {

                  //长度置0

                  this.count = 0;

                 

                  //创建一个与顶点数量相同的标记数组,标记每个顶点是否被搜索过

                  flags = new boolean[G.V()];

                 

                  //深度优先搜索

                  dfs(G, V);

           }

          

           /**

            * 深度优先搜索实现

            * @param G

            * @param V

            */

           public void dfs(Graph G,int V) {

                  //被搜的顶点V标记为搜索过

                  flags[V] = true;

                 

                  for(int w : G.qTable[V]) {

                         if(!flags[w]) {

                                System.out.println("搜索的顶点:"+w);

                                dfs(G, w);

                         }

                  }

                 

                  //相通的点+1

                  count++;

           }

          

           /**

            * 返回与顶点V相通的所有顶点

            * @return

            */

           public int count() {

                  return count;

           }

          

           /**

            * 判断顶点w是否与v相通

            * @param w

            * @return

            */

           public boolean isConnected(int w) {

                  return flags[w];

           }

    }


    五、
    广度优先搜索

      图的搜索算法中,广度优先搜索指的是搜索到的顶点既有子结点又有兄弟结点,优先搜索兄弟结点。如下演示为图的广度优先搜索顺序

             

      为了不对已搜索过的顶点重复搜索,我们需要有个布尔类型的数组来标记顶点是否被搜索过,搜索过的就标记为true,不再进行广度搜索,提高效率的同时,该数组也能判断顶点A到B是否相通

      因为相通的代表搜索过,会被标记为true。

    /**

     * 广度优先搜索

     *

     * @author jiyukai

     */

    public class WeightSearch {

           // 标记顶点x是否有被搜索过

           public boolean[] flags;

           // 联通的点数量

           public int count;

           // 用来存储待搜索邻接表的点

           private Queue<Integer> waitSearch;

           /**

            * 深度优先搜索,找出与顶点V想通的所有顶点

            *

            * @param G

            * @param V

            */

           public WeightSearch(Graph G, int V) {

                  // 长度置0

                  this.count = 0;

                  // 创建一个与顶点数量相同的标记数组,标记每个顶点是否被搜索过

                  flags = new boolean[G.V()];

                  // 初始化待搜索顶点队列

                  waitSearch = new Queue<>();

                  // 深度优先搜索

                  wfs(G, V);

           }

           /**

            * 深度优先搜索实现

            *

            * @param G

            * @param V

            */

           public void wfs(Graph G, int V) {

                  // 被搜的顶点V标记为搜索过

                  flags[V] = true;

                  // 将待搜索的元素入队列

                  for (int w : G.qTable[V]) {

                         waitSearch.enqueue(w);

                  }

                  while (!waitSearch.isEmpty()) {

                         int searchKey = waitSearch.dequeue();

                         if (!flags[searchKey]) {

                                wfs(G, searchKey);

                         }

                  }

                  // 相通的点+1

                  count++;

           }

           /**

            * 返回与顶点V相通的所有顶点

            *

            * @return

            */

           public int count() {

                  return count;

           }

           /**

            * 判断顶点w是否与v相通

            *

            * @param w

            * @return

            */

           public boolean isConnected(int w) {

                  return flags[w];

           }

    }


    六、
    路径查找

      导航中常用的一个场景就是起点s到重点v之间是否存在一条可连通的路径,若存在,需要走什么样的路到达。

       

      基于深度搜索来实现图的搜索,新增一个数组conn[]来记录各个连通点之间的关系,索引为顶点,值为指向顶点的相邻顶点,假设0为起点,在conn数组构建完毕后,我们通过索引0找到顶点4

      在通过索引4找到顶点5,依次找下去,并将找到的顶点依次入栈,则最后能找到和0相通的其他顶点和中间经过的路径。

       

      将找到的顶点依次入栈

       

    /**

     * 基于深度优先搜索实现的路径查找

     * @author jiyukai

     */

    public class DepthSearchPath {

           // 标记顶点x是否有被搜索过

           public boolean[] flags;

           // 联通的点数量

           public int count;

          

           // 初始化起点

           public int s;

           // 连通顶点的关系

           public int conn[];

           /**

            * 深度优先搜索,找出与顶点V想通的所有顶点

            * @param G

            * @param V

            */

           public DepthSearchPath(Graph G, int s) {

                  // 长度置0

                  this.count = 0;

                  // 创建一个与顶点数量相同的标记数组,标记每个顶点是否被搜索过

                  flags = new boolean[G.V()];

                  // 初始化连通顶点的关系数组

                  conn = new int[G.V()];

                 

                  // 初始化起点

                  this.s = s;

                  // 深度优先搜索

                  wfs(G, s);

           }

           /**

            * 深度优先搜索实现

            * @param G

            * @param V

            */

           public void wfs(Graph G, int v) {

                  // 被搜的顶点V标记为搜索过

                  flags[v] = true;

                  for (int w : G.qTable[v]) {

                         if (!flags[w]) {

                                conn[w] = v;

                                wfs(G, w);

                         }

                  }

                  // 相通的点+1

                  count++;

           }

           /**

            * 返回与顶点V相通的所有顶点

            * @return

            */

           public int count() {

                  return count;

           }

           /**

            * 判断顶点w是否与v相通

            * @param w

            * @return

            */

           public boolean isConnected(int w) {

                  return flags[w];

           }

          

           /**

            * 找出s到顶点v的路径

            * @param v

            * @return

            */

           public Stack<Integer> pathTo(int v){

                 

                  //判断是否连接

                  if(!isConnected(v)) {

                         return null;

                  }

                 

                  //创建存放连通图的栈

                  Stack<Integer> stack = new Stack<Integer>();

                 

                  //找出与起点s连通路径上的顶点

                  for(int x=v;x!=s;x=conn[x]) {

                         stack.push(x);

                  }

                 

                  //起点入栈

                  stack.push(s);

                 

                  return stack;

           }

    }

  • 相关阅读:
    Java日期时间API系列16-----Jdk8中java.time包中的新的日期时间API类,java日期计算3,日期中年月日时分秒的属性值修改等
    Java日期时间API系列15-----Jdk8中java.time包中的新的日期时间API类,java日期计算2,年月日时分秒的加减等
    Java日期时间API系列14-----Jdk8中java.time包中的新的日期时间API类,java日期计算1,获取年月日时分秒等
    Java日期时间API系列12-----Jdk8中java.time包中的新的日期时间API类,日期格式化,常用日期格式大全
    Java日期时间API系列11-----Jdk8中java.time包中的新的日期时间API类,使用java8日期时间API重写农历LunarDate
    Java日期时间API系列10-----Jdk8中java.time包中的新的日期时间API类的DateTimeFormatter
    Java日期时间API系列9-----Jdk8中java.time包中的新的日期时间API类的Period和Duration的区别
    Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析
    Java日期时间API系列7-----Jdk8中java.time包中的新的日期时间API类的优点
    MySql 小表驱动大表
  • 原文地址:https://www.cnblogs.com/jiyukai/p/14087585.html
Copyright © 2011-2022 走看看