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 }

三、小結

  以上雖然只實現了無向圖,但其實有向圖、無向網、有向網的建立和遍歷都同理,只需將代碼稍作修改,在邊信息中增加權值信息,用對稱邊記錄反方向的邊即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM