java數據結構----圖
1.圖:.在計算機程序設計中,圖是最常用的數據結構之一。對於存儲一般的數據問題,一般用不到圖。但對於某些(特別是一些有趣的問題),圖是必不可少的。圖是一種與樹有些相像的數據結構,從數學意義上來講,樹是圖的一種。而在計算機程序設計中,圖的應用方式與樹不同。圖通常有一個固定的形狀,這是由物理或抽象的問題所決定的。例如圖中節點表示城市,而邊可能表示城市間的班機航線。當討論圖時,節點通常叫做頂點,
2.一些概念:
圖:
說明:為引入概念,我們用圖13.1a來表示美國某處的簡化高速公路網,圖13.1b來表示模擬這些告訴公路的圖。在圖中,圓圈代表高速公路的交匯點,連接圓圈的直線代表告訴公路段,圓圈是頂點,線是邊。頂點通常用一些方法來標識----正如圖中表示的那樣,用字母表中的字母來表示。每條邊由兩個頂點作為兩邊。圖並不是要試圖反映地圖上的地理位置。它只是反映了頂點和邊的關系----即那些邊連接著哪些頂點。它本身不涉及物理的遠近和方向。而且一條邊可能表示幾條可能的公路。兩個交叉點之間的連通性(或不連通性)是重要的,而實際的路線並不重要。
a.鄰接:如果兩個頂點被同一條邊連接,就稱這兩個頂點是鄰接的。圖13.1中,頂點I 和G是鄰接的,但I 和 F 就不是。和某個指定頂點鄰接的頂點有時叫它的鄰居。如G 的鄰居是I,H和F.
b.路徑:路徑是邊的序列,圖13.1顯示了一條從頂點B到頂點J的路徑,這條路徑通過了A,E,這條路徑稱作BAEJ。這兩個頂點之間還有其他路徑,如BCDJ.
c.連通圖:如果至少有一條路徑,可以連接起所有的頂點,那麽這個圖被稱作連通的,如圖13.2a。如果不能從這裏到那裏,那麽這個圖就是非連通的。如13.2b。非連通圖包含幾個連通子圖。在圖13.2b中,A和B是一個連通子圖,C和D也是一個連通子圖。
d.有向圖和帶權圖:圖13.1和圖13.2中的圖是無向圖,這說明圖中的邊沒有方向----可以從任意一邊到另為一邊。所以可以從頂點A到頂點B,或者從頂點B到頂點A。兩者是等價的(無向圖很好的反映了高速公路網,因為在一條公路上可以按兩個方向行駛),然而圖還經常被用來模擬另一種情況,即只能沿著邊向一個方向行駛。這樣的圖被稱作是有向的。允許移動的方向通常是用一端帶有箭頭的方向表示。在某些圖中,邊被賦予一個權值,權值是一個數字,他能代表兩個頂點間的物理距離,或從一個頂點到另一個頂點的時間,或者是兩點間的花費(如飛機航班),這樣的圖稱為帶權圖。
3.程序中如何表示圖:
3.1.頂點:在非常抽象的圖的問題中,知識簡單的把頂點編號,從0->n-1,不需要任何變量類型來存儲頂點,因為他們的用處來自於他們之間的相互關系,但大多數情況下,頂點表示某個真實世界的對象。頂點對象能放在數組中,然後用下標指示。
3.2.邊:圖並不像樹,擁有幾種固定的結構,二叉樹中頂點最多有兩個子節點,但圖的每個頂點可以與任意多個頂點相連。為此,一般用兩個方法表示圖,鄰接矩陣和鄰接表。(如果一條邊連接兩個頂點,那麽這兩個頂點就是鄰接的)
3.2.1.鄰接矩陣:它是一個二維數組,數據項表示兩點間是否存在邊,如果圖有N個頂點,鄰接矩陣就是N * N 的數組。表13.1顯示了圖13.1a中圖的鄰接矩陣。
圖:
說明:頂點被用作行和列的標題,這兩個頂點間右邊則標識為1,無邊則標識為0.(也可用boolean值表示),如圖所示,頂點A 和另為3個頂點鄰接,B和A,D鄰接,C只與A鄰接。而D與A和B鄰接。從A-A到D-D稱為主對角線,頂點和自身的連接設為0。也可設為1.
3.2.2.鄰接表:表指的是鏈表,實際上,鄰接表是一個鏈表數組。每個單獨的鏈表表示了有哪些頂點和當前頂點鄰接。表13.2顯示了13.2a中圖的鄰接表。
圖:
說明:本表中符號-->表示鏈表中的一個節點,鏈表中每個節點都是一個頂點,鏈表中的每個節點都是一個頂點,在這裏的每個鏈表中,頂點按字母順序排列,不過這並不是必須的。不要把鄰接表的內容與路徑混淆,鄰接表表示了當前節點與那些節點相連------即,兩個節點間存在邊,而不是表示頂點間的路徑。
4.圖的搜素:圖中實現的最基本的操作之一就是搜索從一個指定頂點可以到達那些頂點。有兩種常用的方法來搜索樹,深度優先搜索(DFS)和廣度優先搜索(BFS),他們最終都會到達所有連通的頂點,深度優先搜索通過棧來實現,而廣度優先搜索通過隊列來實現。
4.1.深度優先搜索:找到一個起始點---本例為頂點A,需要做三件事情,首先訪問該節點,然後把該點放入棧中,以便記住它,最後標記該點,這樣就不會再訪問它了。
4.1.1.圖解:
4.1.2.主要規則:
4.1.2.1.規則1:如果可能,訪問一個鄰接的未訪問頂點,標記它,並把他放入棧中。
4.1.2.2.規則2:當不能執行規則1時,如果棧非空,就從棧中彈出一個頂點。
4.1.2.3.規則3:如果不能執行規則1和規則2,就完成了整個搜索過程。
4.2.廣度優先搜索:首先訪問起始頂點的所有鄰接點,然後再訪問較遠的區域,用隊列來實現它。(A是起始點,所以訪問它,並標記為當前頂點,然後應用規則)
4.2.1圖解:
4.2.2.主要規則:
4.2.2.1.規則1:訪問下一個問來訪問的鄰接點(如果存在),這個頂點必須是當前頂點的鄰接點,標記它,並把它插入到隊列中。
4.2.2.2.規則2:如果因為已經沒有未訪問頂點而不能執行規則1,那麽從隊列頭取一個頂點(如果存在),並使其成為當前頂點。
4.2.2.3.規則3:如果因為隊列為空而不能執行規則2,則搜索結束。
5.深度優先搜索實現:
5.1.StackX.java
1 package com.cn.graph; 2 /** 3 * 圖的深度優先搜索底層---棧類 4 * @author Administrator 5 * 6 */ 7 public class StackX { 8 private final int SIZE = 20; 9 private int[] st; 10 private int top; 11 public StackX(){ 12 st = new int[SIZE]; 13 top = -1; 14 } 15 public void push(int j){ 16 st[++ top] = j; 17 } 18 public int pop(){ 19 return st[top --]; 20 } 21 public int peek(){ 22 return st[top]; 23 } 24 public boolean isEmpty(){ 25 return top == -1; 26 } 27 }View Code
5.2.Vertex.java
1 package com.cn.graph; 2 /** 3 * 圖的頂點類 4 * @author Administrator 5 * 6 */ 7 public class Vertex { 8 public char lable; 9 public boolean wasvisited; 10 public Vertex(char lab){ 11 lable = lab; 12 wasvisited = false; 13 } 14 }View Code
5.3.Graph.java
1 package com.cn.graph; 2 /** 3 * 深度優先實現的圖 4 * @author Administrator 5 * 6 */ 7 public class Graph { 8 private final int MAX_VERTS = 20; 9 private Vertex[] vertexList; 10 private int adjMat[][]; 11 public int nVerts; 12 private StackX theStack; 13 public Graph(){ 14 vertexList = new Vertex[MAX_VERTS]; 15 adjMat = new int[MAX_VERTS][MAX_VERTS]; 16 nVerts = 0; 17 for (int i = 0; i < MAX_VERTS; i++) { 18 for (int j = 0; j < MAX_VERTS; j++) { 19 adjMat[i][j] = 0; 20 } 21 } 22 theStack = new StackX(); 23 } 24 public void addVertex(char lab){ 25 vertexList[nVerts ++] = new Vertex(lab); 26 } 27 public void addEdage(int start,int end){ 28 adjMat[start][end] = 1; 29 adjMat[end][start] = 1; 30 } 31 public void displayVertex(int v){ 32 System.out.print(vertexList[v].lable+" "); 33 } 34 public void dfs(){ 35 vertexList[0].wasvisited = true; 36 displayVertex(0); 37 theStack.push(0); 38 while (! theStack.isEmpty()){ 39 int v = getAdjUnvisitedVertex(theStack.peek()); 40 if (v == -1) 41 theStack.pop(); 42 else{ 43 vertexList[v].wasvisited = true; 44 displayVertex(v); 45 theStack.push(v); 46 } 47 } 48 for (int i = 0; i < nVerts; i++) 49 vertexList[i].wasvisited = false; 50 } 51 public int getAdjUnvisitedVertex(int v){ 52 for (int i = 0; i < nVerts; i++) 53 if (adjMat[v][i] == 1 && vertexList[i].wasvisited == false) 54 return i; 55 return -1; 56 } 57 }View Code
5.4.GTest.java
1 package com.cn.graph; 2 /** 3 * 深度優先搜索測試類 4 * @author Administrator 5 * 6 */ 7 public class GTest { 8 public static void main(String[] args) { 9 Graph g = new Graph(); 10 g.addVertex(‘A‘); 11 g.addVertex(‘B‘); 12 g.addVertex(‘C‘); 13 g.addVertex(‘D‘); 14 g.addEdage(0, 1); 15 g.addEdage(2, 1); 16 g.addEdage(2, 3); 17 // g.displayVertex(0); 18 g.dfs(); 19 } 20 }View Code
6.廣度優先搜索實現:
6.1.Queue.java
1 package com.cn.graph; 2 /** 3 * 圖的廣度優先搜索---隊列類 4 * @author Administrator 5 * 6 */ 7 public class Queue { 8 private final int SIZE = 20; 9 private int queueArray[]; 10 private int front; 11 private int rear; 12 public Queue(){ 13 queueArray = new int[SIZE]; 14 front = 0; 15 rear = -1; 16 } 17 public void insert(int j){ 18 if (rear == SIZE - 1) 19 rear = -1; 20 queueArray[++ rear] = j; 21 } 22 public int remove(){ 23 int temp = queueArray[front ++]; 24 if (front == SIZE) 25 front = 0; 26 return temp; 27 } 28 public boolean isEmpty(){ 29 return (rear + 1 == front || front + SIZE - 1 == rear); 30 } 31 }View Code
6.2.Vertex.java
1 package com.cn.graph; 2 /** 3 * 圖的頂點類 4 * @author Administrator 5 * 6 */ 7 public class Vertex { 8 public char lable; 9 public boolean wasvisited; 10 public Vertex(char lab){ 11 lable = lab; 12 wasvisited = false; 13 } 14 }View Code
6.3.QGraph.java
1 package com.cn.graph; 2 /** 3 * 廣度優先搜索----隊列實現 4 * @author Administrator 5 * 6 */ 7 public class QGraph { 8 final int MAX_VERTS = 20; 9 private Vertex[] vertexList; 10 private int adjMat[][]; 11 public int nVerts; 12 private Queue thequeue; 13 public QGraph(){ 14 vertexList = new Vertex[MAX_VERTS]; 15 adjMat = new int[MAX_VERTS][MAX_VERTS]; 16 nVerts = 0; 17 for (int i = 0; i < MAX_VERTS; i++) 18 for (int j = 0; j < MAX_VERTS; j++) 19 adjMat[i][j] = 0; 20 thequeue = new Queue(); 21 } 22 public void addVertex(char lab){ 23 vertexList[nVerts ++] = new Vertex(lab); 24 } 25 public void addEdage(int start,int end){ 26 adjMat[start][end] = 1; 27 adjMat[end][start] = 1; 28 } 29 public void displayVertex(int v){ 30 System.out.print(vertexList[v].lable+" "); 31 } 32 public void bfs(){ 33 vertexList[0].wasvisited = true; 34 displayVertex(0); 35 thequeue.insert(0); 36 int v2; 37 while (! thequeue.isEmpty()){ 38 int v = thequeue.remove(); 39 while ((v2 = getAdjUnvisitedVertex(v)) != -1){ 40 vertexList[v2].wasvisited = true; 41 displayVertex(v2); 42 thequeue.insert(v2); 43 } 44 } 45 for (int i = 0; i < nVerts; i++) 46 vertexList[i].wasvisited = false; 47 } 48 public int getAdjUnvisitedVertex(int v){ 49 for (int i = 0; i < nVerts; i++) 50 if (adjMat[v][i] == 1 && vertexList[i].wasvisited == false) 51 return i; 52 return -1; 53 } 54 55 }View Code
6.4.QGTest.java
1 package com.cn.graph; 2 /** 3 * 廣度優先搜索實現 4 * @author Administrator 5 * 6 */ 7 public class QGTest { 8 public static void main(String[] args) { 9 QGraph q = new QGraph(); 10 q.addVertex(‘a‘); 11 q.addVertex(‘b‘); 12 q.addVertex(‘c‘); 13 q.addVertex(‘d‘); 14 q.addEdage(0, 1); 15 q.addEdage(2, 1); 16 q.addEdage(0, 3); 17 q.bfs(); 18 19 } 20 21 }View Code
7.最小生成樹(MST):最小生成樹邊E的數量總比頂點V 的數量小1。E = V - 1;
8.深度優先實現MST的核心代碼:
Graph + .
1 public void mst(){ 2 vertexList[0].wasvisited = true; 3 theStack.push(0); 4 while (! theStack.isEmpty()){ 5 int currentVertex = theStack.peek(); 6 int v = getAdjUnvisitedVertex(currentVertex); 7 if (v == -1) 8 theStack.pop(); 9 else{ 10 vertexList[v].wasvisited = true; 11 theStack.push(v); 12 displayVertex(currentVertex); 13 displayVertex(v); 14 System.out.print(" "); 15 } 16 } 17 for (int i = 0; i < nVerts; i++) { 18 vertexList[i].wasvisited = false; 19 } 20 }View Code
9.有向圖的應用場景案例:
9.1.場景圖示:
9.1.1.有向圖:
9.1.2.圖13.12對應的鄰接矩陣:
9.1.3.拓撲排序:假設存在一個課程列表,包含了要得到學位必修的所有課程,就像圖13.11所示的課程一樣,下面按照課程的先後關系排列它們。得到學位是列表的最後一項,這就得到BAEDGCFH的序列。這種方式的排列叫做為圖進行拓撲排序。同樣,CFBAEDGH也滿足優先關系。當使用算法生成一個拓撲序列時,使用的方法和代碼細節決定了生成那種拓撲序列。
9.1.4.拓撲排序算法的核心步驟:a.找到一個沒有後繼的節點。b.從圖中刪除這個頂點,在列表的前面插入頂點的標記。重復步驟a,b 直到所有頂點都從圖中刪除。這時列表顯示的頂點順序就是拓撲排序結果。拓撲排序可以用於連通圖也可以用於非連通圖。
10.wallshall算法:需要很快找到是否一個頂點可以從其他頂點到達。可以構建一個表,這個表以O(1)的時間復雜度告知一個頂點到另一個頂點是否可達,這樣的表可以通過修改圖的鄰接矩陣實現。wallshall算法的核心思想:如果能從頂點L到達M,並且能從頂點M到達N,那麽可以從L 到 N.
java數據結構----圖