zoukankan      html  css  js  c++  java
  • Java实现无向图的建立与遍历

    一、基于邻接矩阵表示法的无向图

      邻接矩阵是一种利用一维数组记录点集信息、二维数组记录边集信息来表示图的表示法,因此我们可以将图抽象成一个类,点集信息和边集信息抽象成类的属性,就可以在Java中描述出来,代码如下:

    1 class AMGraph{
    2     
    3     private String[] vexs = null;    //点集信息
    4     
    5     private int[][] arcs = null;     //边集信息
    6     
    7 }

      每一个具体的图,就是该类的一个实例化对象,因此我们可以在构造函数中实现图的创建,代码如下:

     1 public AMGraph(int vexNum,int arcNum) {          //输入点的个数和边的个数
     2         
     3     this.vexs = new String[vexNum];
     4     this.arcs = new int[vexNum][vexNum];
     5         
     6     System.out.print("请依次输入顶点值,以空格隔开:");
     7     Scanner sc = new Scanner(System.in);
     8     for(int i = 0; i < vexNum; i++) {           //根据输入建立点集
     9         this.vexs[i] = sc.next();
    10     }
    11         
    12     for(int i = 0; i < vexNum; i++) {           //初始化边集
    13         for(int j = 0; j < vexNum; j++) {
    14             this.arcs[i][j] = 0;              //0表示该位置所对应的两顶点之间没有边
    15         }
    16     }
    17         
    18 start:for(int i = 0; i < arcNum; i++) {       //开始建立边集
    19     
    20         sc = new Scanner(System.in);
    21         int vex1Site = 0;
    22         int vex2Site = 0;
    23         String vex1 = null;
    24         String vex2 = null;
    25             
    26         System.out.print("请输入第" + (i+1) + "条边所依附的两个顶点,以空格隔开:");
    27         vex1 = sc.next();
    28         vex2 = sc.next();
    29         for(int j = 0; j < this.vexs.length; j++) {    //查找输入的第一个顶点的位置
    30             if (this.vexs[j].equals(vex1)) {
    31                 vex1Site = j;
    32                 break;
    33             }
    34             if (j == this.vexs.length - 1) {
    35                 System.out.println("未找到第一个顶点,请重新输入!");
    36                 i--;
    37                 continue start;
    38             }
    39         }
    40         for (int j = 0; j < this.vexs.length; j++) {   //查找输入的第二个顶点的位置
    41             if(this.vexs[j].equals(vex2)) {
    42                 vex2Site = j;
    43                 break;
    44             }
    45             if (j == this.vexs.length - 1) {
    46                 System.out.println("未找到第二个顶点,请重新输入!");
    47                 i--;
    48                 continue start;
    49             }
    50         }
    51         if(this.arcs[vex1Site][vex2Site] != 0) {      //检测该边是否已经输入
    52             System.out.println("该边已存在!");
    53             i--;
    54             continue start;
    55         }else {
    56             this.arcs[vex1Site][vex2Site] = 1;       //1表示该位置所对应的两顶点之间有边
    57             this.arcs[vex2Site][vex1Site] = 1;       //对称边也置1
    58         }
    59     }
    60     System.out.println("基于邻接矩阵的无向图创建成功!");
    61     sc.close();
    62 }

       创建好图后,我们还要实现图的遍历。由于图已经被我们抽象成一个类,因此我们可以将图的遍历定义成类的方法。对于连通图,调用遍历算法后即可访问所有结点,但对于非连通图,调用遍历算法后仍有一些结点没有被访问,需要从图中另选一个未被访问的顶点再次调用遍历算法。因此需要附设一个访问标志数组visited[n],来记录被访问的结点。增加了访问标志数组的类属性如下:

    1 class AMGraph{
    2     
    3     private String[] vexs = null;
    4     
    5     private int[][] arcs = null;
    6     
    7     private boolean[] visited = null;     //false表示该位置的顶点未访问,true表示已访问
    8 
    9 }

      图的遍历分为深度优先遍历和广度优先遍历,接下来我们来分别实现它们。深度优先遍历代码如下:

     1 public void dFSTraverse() {
     2         
     3     this.visited = new boolean[this.vexs.length];     //初始化访问标志数组
     4     for(int i = 0; i < this.visited.length; i++) {
     5         this.visited[i] = false;
     6     }
     7         
     8     for(int i = 0; i < this.visited.length; i++) {
     9         if(!this.visited[i]) {                //对未访问的顶点调用深度优先遍历算法
    10             dFS_AM(i);
    11         }
    12     }
    13 }
    1 public void dFS_AM(int site) {                //输入深度优先遍历的开始顶点
    2     System.out.println(this.vexs[site]);          //输出该顶点
    3     this.visited[site] = true;                //置访问标志为true
    4     for(int i = 0; i < this.vexs.length; i++) {      //依次查找未访问邻接点,并以该邻接点为始点调用深度优先遍历算法
    5         if(this.arcs[site][i] != 0 && !this.visited[i]) {
    6             this.dFS_AM(i);
    7         }
    8     }
    9 }

      广度优先遍历代码如下:

     1 public void bFSTraverse() {
     2         
     3     this.visited = new boolean[this.vexs.length];      //初始化访问标志数组
     4     for(int i = 0; i < this.visited.length; i++) {
     5         this.visited[i] = false;
     6     }
     7         
     8     for(int i = 0; i < this.visited.length; i++) {
     9         if(!this.visited[i]) {                 //对未访问的顶点调用广度优先遍历算法
    10             bFS_AM(i);
    11         }
    12     }
    13 }
     1 public void bFS_AM(int site) {                        //输入开始顶点
     2     System.out.println(this.vexs[site]);                  //输出该顶点
     3     this.visited[site] = true;                        //置访问标志为true
     4     LinkedList<Integer> linkedList = new LinkedList<Integer>();    //借助队列来实现广度优先遍历
     5     linkedList.offer(site);                          //将访问过的顶点入队
     6     while(!linkedList.isEmpty()) {
     7         int vexSite = linkedList.poll();                  //队头顶点出队
     8         for(int i = 0; i < this.vexs.length; i++) {
     9             if(this.arcs[vexSite][i] != 0 && !this.visited[i]) {   //依次查找未访问的邻接点进行访问后入队
    10                 System.out.println(this.vexs[i]);
    11                 this.visited[i] = true;
    12                 linkedList.offer(i);
    13             }
    14         }
    15     }
    16 }

      以上基于邻接矩阵表示法的无向图的类就定义好了,接着我们在客户端里使用即可:

     1 public class Main {
     2 
     3     public static void main(String[] args) {
     4                 
     5         Scanner sc =  new Scanner(System.in);
     6         int vexNum = 0;
     7         int arcNum = 0;
     8         while(true) {
     9             System.out.print("请输入要建立无向图的总顶点数和总边数,以空格隔开:");
    10             try {
    11                 vexNum = sc.nextInt();
    12                 arcNum = sc.nextInt();
    13                 break;
    14             } catch (Exception e) {
    15                 System.out.println("输入不合法!");
    16                 continue;
    17             }
    18         }
    19         
    20         AMGraph aMGraph = new AMGraph(vexNum, arcNum);
    21         System.out.println("由深度优先遍历得:");
    22         aMGraph.dFSTraverse();
    23         System.out.println("由广度优先遍历得:");
    24         aMGraph.bFSTraverse();
    25 
    26         sc.close();
    27     }
    28 
    29 }

    二、基于邻接表表示法的无向图

      邻接表是一种基于链式存储结构的表示法。在邻接表中,对图的每个顶点建立一个单链表,单链表的第一个结点存放顶点信息,称为点结点,其余结点存放边信息,称为边结点。此外,还需要一个顶点数组,存储对所有单链表的引用。因此我们需要定义三个类,第一个类为顶点类,用来生成点结点;第二个类为边类,用来生成边结点;第三个类为图类,里面定义有属性——顶点数组和方法——图的遍历。代码如下:

     1 class ALGraph_Head{                 //顶点类
     2     
     3     private String data = null;          //点结点值
     4     
     5     private ALGraph_Arc firstArc= null;     //第一条边的指针
     6     
     7     public String getData() {
     8         return data;
     9     }
    10     
    11     public ALGraph_Arc getFirstArc() {
    12         return firstArc;
    13     }
    14     
    15     public void setFirstArc(ALGraph_Arc firstArc) {
    16         this.firstArc = firstArc;
    17     }
    18     
    19     public ALGraph_Head(String data) {
    20         this.data = data;
    21     }
    22 }
     1 class ALGraph_Arc{                  //边类
     2     
     3     private int adjVexSite = 0;          //该边所连接的顶点的邻接点的位置
     4     
     5     private ALGraph_Arc nextArc = null;     //下一条边的指针
     6     
     7     public int getAdjVexSite() {
     8         return adjVexSite;
     9     }
    10     
    11     public ALGraph_Arc getNextArc() {
    12         return nextArc;
    13     }
    14     
    15     public ALGraph_Arc(int adjVexSite, ALGraph_Arc nextArc) {
    16         this.adjVexSite = adjVexSite;
    17         this.nextArc = nextArc;
    18     }
    19 }
    1 class ALGraph{                        //图类
    2     
    3     private ALGraph_Head[] aLGraph_Heads = null;    //顶点数组
    4 
    5 }

      同样的,我们在构造方法中进行图的建立,代码如下:

     1 public ALGraph(int vexNum,int arcNum) {            //输入顶点个数,边的个数
     2         
     3     this.aLGraph_Heads = new ALGraph_Head[vexNum];
     4         
     5     System.out.print("请依次输入顶点值,以空格隔开:");
     6     Scanner sc = new Scanner(System.in);
     7     for(int i = 0; i < vexNum; i++) {             //建立顶点数组存储点结点
     8         this.aLGraph_Heads[i] = new ALGraph_Head(sc.next());
     9     }
    10         
    11 start:for(int i = 0; i < arcNum; i++) {         //开始存储边信息
    12             
    13         sc = new Scanner(System.in);
    14         int vex1Site = 0;
    15         int vex2Site = 0;
    16         String vex1 = null;
    17         String vex2 = null;
    18             
    19         System.out.print("请输入第" + (i+1) + "条边所依附的两个顶点,以空格隔开:");
    20         vex1 = sc.next();
    21         vex2 = sc.next();
    22         for(int j = 0; j < this.aLGraph_Heads.length; j++) {        //查找第一个顶点的位置
    23             if (this.aLGraph_Heads[j].getData().equals(vex1)) {
    24                 vex1Site = j;
    25                 break;
    26             }
    27             if (j == this.aLGraph_Heads.length - 1) {
    28                 System.out.println("未找到第一个顶点,请重新输入!");
    29                 i--;
    30                 continue start;
    31             }
    32         }
    33         for (int j = 0; j < this.aLGraph_Heads.length; j++) {        //查找第二个顶点的位置
    34             if(this.aLGraph_Heads[j].getData().equals(vex2)) {
    35                 vex2Site = j;
    36                 break;
    37             }
    38             if (j == this.aLGraph_Heads.length - 1) {
    39                 System.out.println("未找到第二个顶点,请重新输入!");
    40                 i--;
    41                 continue start;
    42             }
    43         }
    44         ALGraph_Arc aLGraph_Arc = this.aLGraph_Heads[vex1Site].getFirstArc();     //获取点结点里的边指针
    45         while(aLGraph_Arc != null) {                              //判断边是否已存储
    46             if(aLGraph_Arc.getAdjVexSite() == vex2Site) {
    47                 System.out.println("该边已存在!");
    48                 i--;
    49                 continue start;
    50             }
    51             aLGraph_Arc = aLGraph_Arc.getNextArc();
    52         }
    53         this.aLGraph_Heads[vex1Site].setFirstArc(new ALGraph_Arc(vex2Site, this.aLGraph_Heads[vex1Site].getFirstArc()));    //将边结点加入单链表中
    54         this.aLGraph_Heads[vex2Site].setFirstArc(new ALGraph_Arc(vex1Site, this.aLGraph_Heads[vex2Site].getFirstArc()));    //对称边结点也加入单链表
    55     }
    56     System.out.println("基于邻接表的无向图创建成功!");
    57     sc.close();
    58 }

      接着实现图的遍历,同样需要附设一个访问标志数组,因此将图类属性修改如下:

    1 class ALGraph{
    2     
    3     private ALGraph_Head[] aLGraph_Heads = null;
    4     
    5     private boolean[] visited = null;          //访问标志数组
    6 
    7 }

      深度优先遍历:

     1 public void dFSTraverse() {
     2         
     3     this.visited = new boolean[this.aLGraph_Heads.length];    //建立并初始化访问标志数组
     4     for(int i = 0; i < this.visited.length; i++) {
     5         this.visited[i] = false;
     6     }
     7     
     8     for(int i = 0; i < this.visited.length; i++) {         //以未访问的点为始点调用深度优先遍历算法
     9         if(!this.visited[i]) {
    10             dFS_AM(i);
    11         }
    12     }
    13 }
     1 public void dFS_AM(int site) {
     2     System.out.println(this.aLGraph_Heads[site].getData());          //输出点值
     3     this.visited[site] = true;                           //置访问标志为true
     4     ALGraph_Arc aLGraph_Arc = this.aLGraph_Heads[site].getFirstArc();    //获取点结点中的边指针
     5     while(aLGraph_Arc != null) {
     6         if(!visited[aLGraph_Arc.getAdjVexSite()]) {              //如果该边所连接的顶点的邻接点未访问,则以该邻接点为始点调用深度优先遍历算法
     7             this.dFS_AM(aLGraph_Arc.getAdjVexSite());
     8         }
     9         aLGraph_Arc = aLGraph_Arc.getNextArc();                 //获取下一条边
    10     }
    11 }

      广度优先遍历:

     1 public void bFSTraverse() {
     2         
     3 this.visited = new boolean[this.aLGraph_Heads.length];    //建立并初始化访问标志数组
     4 for(int i = 0; i < this.visited.length; i++) {
     5     this.visited[i] = false;
     6     }
     7         
     8     for(int i = 0; i < this.visited.length; i++) {       //以未访问的点为始点调用广度优先遍历算法
     9         if(!this.visited[i]) {
    10             bFS_AM(i);
    11         }
    12     }
    13 }
     1 public void bFS_AM(int site) {
     2     System.out.println(this.aLGraph_Heads[site].getData());        //输出点值
     3     this.visited[site] = true;                          //置访问标志为true
     4     LinkedList<Integer> linkedList = new LinkedList<Integer>();      //利用队列实现广度优先遍历
     5     linkedList.offer(site);                           //点入队
     6     while(!linkedList.isEmpty()) {
     7         int vexSite = linkedList.poll();                   //点出队
     8         ALGraph_Arc aLGraph_Arc = this.aLGraph_Heads[vexSite].getFirstArc();    //获取点结点中的边指针
     9         while(aLGraph_Arc != null) {
    10             vexSite = aLGraph_Arc.getAdjVexSite();                    //获取该边所连接的顶点的邻接点
    11             if(!this.visited[vexSite]) {                          //如果该邻接点未访问,则访问
    12                 System.out.println(this.aLGraph_Heads[vexSite].getData());
    13                 this.visited[vexSite] = true;
    14                 linkedList.offer(vexSite);                        //被访问的点入队
    15             }
    16             aLGraph_Arc = aLGraph_Arc.getNextArc();                   //获取下一个邻接点
    17         }
    18     }
    19 }

      客户端代码如下:

     1 public class Main {
     2 
     3     public static void main(String[] args) {
     4         
     5         Scanner sc =  new Scanner(System.in);
     6         int vexNum = 0;
     7         int arcNum = 0;
     8         while(true) {
     9             System.out.print("请输入要建立无向图的总顶点数和总边数,以空格隔开:");
    10             try {
    11                 vexNum = sc.nextInt();
    12                 arcNum = sc.nextInt();
    13                 break;
    14             } catch (Exception e) {
    15                 System.out.println("输入不合法!");
    16                 continue;
    17             }
    18         }
    19         
    20         ALGraph aLGraph = new ALGraph(vexNum, arcNum);
    21         System.out.println("由深度优先遍历得:");
    22         aLGraph.dFSTraverse();
    23         System.out.println("由广度优先遍历得:");
    24         aLGraph.bFSTraverse();
    25 
    26         sc.close();
    27     }
    28 
    29 }

    三、小结

      以上虽然只实现了无向图,但其实有向图、无向网、有向网的建立和遍历都同理,只需将代码稍作修改,在边信息中增加权值信息,用对称边记录反方向的边即可。

  • 相关阅读:
    float、定位、inline-block、兼容性需注意的特性总结
    meta 标签 详细说明
    兼容探讨一
    javascript性能优化总结二(转载)
    javascript性能优化总结一(转载人家)
    特效合集(原生JS代码)适合初学者
    svg实现简单沙漏旋转
    SVG制作简单的图形
    SVG的简单介绍
    jQuery之效果
  • 原文地址:https://www.cnblogs.com/ysyasd/p/10992055.html
Copyright © 2011-2022 走看看