【基础算法】(10)图的概念及相关算法

来源:互联网 发布:剑网3自动更新网络断开 编辑:程序博客网 时间:2024/05/17 23:25

【基础算法】(10)图的概念及相关算法(第一篇)

Auther: Thomas Shen
E-mail: Thomas.shen3904@qq.com
Date: 2017/12/20
All Copyrights reserved !

      • 基础算法10图的概念及相关算法第一篇
        • 图的存储结构
          • 1 邻接矩阵
          • 2 邻接表
        • 深度优先遍历DFS
          • 1 基本概念
          • 2 算法简述
            • 11 递归实现
            • 12 非递归实现
        • 广度优先遍历BFS
          • 1 基本概念
          • 2 算法简述
        • 代码实现
        • References


1. 图的存储结构:

1.1 邻接矩阵:

使用二维数组来存储图的边的信息和权重,如下图所示的4个顶点的无向图:

这里写图片描述

从上面可以看出,无向图的边数组是一个对称矩阵。所谓对称矩阵就是n阶矩阵的元满足aij = aji。即从矩阵的左上角到右下角的主对角线为轴,右上角的元和左下角相对应的元全都是相等的。

如果换成有向图,则如图所示的五个顶点的有向图的邻接矩阵表示如下:

这里写图片描述

1.2 邻接表:

邻接矩阵是一种不错的图存储结构,但是对于边数相对较少的图,这种结构存在空间上的极大浪费,因此找到一种数组与链表相结合的存储方法称为邻接表。
邻接表的处理方法是这样的:

  1. 图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,更加方便。
  2. 图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表

如下为无向图的邻接表表示:

这里写图片描述

从图中可以看出,顶点表的各个结点由data和firstedge两个域表示,data是数据域,存储顶点的信息,firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点。边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针。

有向图的邻接表表示:

这里写图片描述

2. 深度优先遍历(DFS):

2.1 基本概念:

它从图中某个结点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中的所有顶点都被访问到为止。

2.2 算法简述:

基本实现思想:

  1. 访问顶点v;
  2. 从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;
  3. 重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。
2.1.1 递归实现:
1. 访问顶点v;visited[v]=1//算法执行前visited[n]=02. w=顶点v的第一个邻接点;3. while(w存在)        if(w未被访问)          从顶点w出发递归执行该算法;       w=顶点v的下一个邻接点;
2.1.2 非递归实现:
1. 栈S初始化;visited[n]=02. 访问顶点v;visited[v]=1;顶点v入栈S3. while(栈S非空)    x=栈S的顶元素(不出栈);    if(存在并找到未被访问的x的邻接点w)        访问w;visited[w]=1;        w进栈;    else        x出栈;

3. 广度优先遍历(BFS):

3.1 基本概念:

它是一个分层搜索的过程和二叉树的层次遍历十分相似,它也需要一个队列以保持遍历过的顶点顺序,以便按出队的顺序再去访问这些顶点的邻接顶点。

基本实现思想:

  1. 顶点v入队列。
  2. 当队列非空时则继续执行,否则算法结束。
  3. 出队列取得队头顶点v;访问顶点v并标记顶点v已被访问。
  4. 查找顶点v的第一个邻接顶点col。
  5. 若v的邻接顶点col未被访问过的,则col入队列。
  6. 继续查找顶点v的另一个新的邻接顶点col,转到步骤(5)。
    直到顶点v的所有未被访问过的邻接点处理完。转到步骤(2)。

广度优先遍历图是以顶点v为起始点,由近至远,依次访问和v有路径相通而且路径长度为1,2,……的顶点。为了使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问,需设置队列存储访问的顶点。

3.2 算法简述:
1. 初始化队列Q;visited[n]=0;2. 访问顶点v;visited[v]=1;顶点v入队列Q;3. while(队列Q非空)           v=队列Q的对头元素出队;        w=顶点v的第一个邻接点;        while(w存在)             如果w未访问,则访问顶点w;            visited[w]=1;            顶点w入队列Q;            w=顶点v的下一个邻接点。

4. 代码实现:

采用邻接矩阵存储图的边信息:

/*  * 定义图的结构  */  class Graph {      static final int MaxNum=20;        //最大节点数目      static final int MaxValue=65535;      char[] Vertex = new char[MaxNum];         //定义数组,保存顶点信息      int GType;   //图的类型0:无向图  1:有向图      int VertxNum;              //顶点的数量      int EdgeNum;         //边的数量      int[][] EdgeWeight = new int[MaxNum][MaxNum];     //定义矩阵保存顶点信息      int[] isTrav = new int[MaxNum];            //遍历标志  }  
//创建邻接矩阵图      static void createGraph(Graph g){          int i ,  j  ,  k;          int weight;     //权          char EstartV,  EndV;      //边的起始顶点          System.out.println("输入途中各顶点的信息");          for(i=0; i < g.VertxNum; i ++)          {              System.out.println("第" + (i+1) + "个顶点");              g.Vertex[i] = (scan.next().toCharArray() )[0];          }          System.out.println("输入构成个遍的顶点和权值");          for(k=0;k<g.EdgeNum;k++)          {              System.out.println("第" + (k+1) + "条边:");              EstartV = scan.next().charAt(0);              EndV = scan.next().charAt(0);              weight = scan.nextInt();              for(i=0; EstartV!=g.Vertex[i] ; i++);           //在已有顶点中查找开始节点              for(j=0; EndV != g.Vertex[j]; j++);             //在已有节点上查找终结点              g.EdgeWeight[i][j] = weight;       //对应位置保存权重,表示有一条边              if(g.GType == 0)               //如果是无向图,在对角位置保存权重                  g.EdgeWeight[j][i] = weight;          }      }  
//清空图      static void clearGraph(Graph g){          int i,j;          for(i=0; i< g.VertxNum; i++)              for(j =0; j<g.VertxNum; j++)                  g.EdgeWeight[i][j] = Graph.MaxValue;           //设置矩阵中各院素的值为MaxValue      }  
//输出邻接矩阵      static void OutGraph(Graph g){          int i,j;          for(j = 0; j < g.VertxNum;j ++)              System.out.print("\t" + g.Vertex[j]);      //在第一行输入顶点信息          System.out.println();          for(i =0 ;i <g.VertxNum; i ++)          {              System.out.print( g.Vertex[i]);              for(j = 0;j < g.VertxNum; j++)              {                  if(g.EdgeWeight[i][j] == Graph.MaxValue)    //若权值为最大值                      System.out.print("\tZ");    //Z 表示无穷大                  else                      System.out.print("\t" + g.EdgeWeight[i][j]);  //输出边的权重              }              System.out.println();          }      }  
//遍历图      static void DeepTraOne(Graph g,int n){//从第n个节点开始遍历          int i;          g.isTrav[n] = 1;              //标记为1表示该顶点已经被处理过          System.out.println("—>" + g.Vertex[n]); //输出节点数据          //添加处理节点的操作          for(i = 0; i< g.VertxNum; i++)          {              //if(g.EdgeWeight[n][i] != g.MaxValue && g.isTrav[n] == 0)                          if(g.EdgeWeight[n][i] != g.MaxValue && g.isTrav[i] == 0)                  {                      DeepTraOne(g, i);     //递归进行遍历                  }          }      }  
//深度优先遍历      static void  DeepTraGraph(Graph g){          int i;          for(i = 0; i< g.VertxNum; i++)          {              g.isTrav[i]= 0;          }          System.out.println("深度优先遍历:");          for(i = 0; i< g.VertxNum ; i++)          {              if(g.isTrav[i] == 0)                  DeepTraOne(g,i);          }          System.out.println();      }  

主函数:

public static void main(String[] args) {          // TODO Auto-generated method stub          Graph g = new Graph();          System.out.println("输出生成图的类型:");          g.GType = scan.nextInt();  //图的种类          System.out.println("输入图的顶点数量:");          g.VertxNum = scan.nextInt();          System.out.println("输入图的边数量:");          g.EdgeNum = scan.nextInt();          clearGraph(g);          //清空图          createGraph(g);      //生成邻接表结构的图          System.out.println("该图的邻接矩阵数据如下:");          OutGraph(g);        //输出图          DeepTraGraph(g);    //深度优先遍历图      }  

References. :

  • [ 1 ]. Coursera | Java程序设计 | PKU
  • [ 2 ]. 图的概念和关于图的几个算法
  • [ 3 ]. 《图论》——图的存储与遍历(Java)
  • [ 4 ]. http://blog.csdn.net/hguisu/article/details/7712813
  • [ 5 ]. http://www.cnblogs.com/dolphin0520/archive/2011/07/13/21052
    36.html