樹和圖詳解
(1)基本概念
1、圖:一種表示“多對多”關係的複雜資料結構。
2、圖的組成:圖G由一個非空的有限頂點集合V(G)和一個有限邊集合E(G)組成,定義為G=(V,E)。
3、無向圖:若圖的每條邊都沒有方向,則稱該圖為無向圖。
4、有向圖:若圖的每條邊都有方向,則稱該圖為有向圖。
5、對於無向圖,頂點的度表示以該頂點作為一個端點的邊的數目。
6、對於有向圖,頂點的度分為入度和出度。入度是以該頂點為終點的入邊數目,出度是以該頂點為起點的出邊數目,該頂點的度等於其入度和出度之和。
7、路徑:在圖G中,存在一個頂點序列(Vp,Vi1,Vi2,Vi3…,Vin,Vq),使得(Vp,Vi1),(Vi1,Vi2),…,(Vim,Vq)均屬於邊集E(G),則稱頂點Vp到Vq存在一條路徑。
8、路徑長度:一條路徑上經過的邊的數量。
9、環:某條路徑包含相同的頂點兩次或兩次以上。
10、有向無環圖:沒有環的有向圖,簡稱DAG。
11、帶權有向圖的最短路徑長度:源點Vm到終點Vn的所有路徑中,權值和最小的路徑是最短路徑,其長度是最短路徑長度。
12、完全圖:任意兩個頂點都相連的圖稱為完全圖,又分為無向完全圖和有向完全圖。
13、連通圖:在無向圖中,若任意兩個頂點vivi與vjvj都有路徑相通,則稱該無向圖為連通圖。
14、強連通圖:在有向圖中,若任意兩個頂點vivi與vjvj都有路徑相通,則稱該有向圖為強連通圖。
15、連通網:帶權值的連通圖叫做連通網。
16、生成樹:將圖中所有頂點以最少的邊連通的子圖。生成樹包含全部n個頂點,有且僅有n-1條邊,在新增邊則必定成環。(因為每個結點(除根結點)都可以向上找到唯一的父節點,所有是樹)。
17、最小生成樹:在所有生成樹中,權值和最小的生成樹就是最小生成樹。
18、樹與圖的關係:樹的定義:有且只有一個結點的入度為0,其他節點的入度為1。樹是一個無向連通圖,其中任何兩個頂點只通過一條路徑連線。 換句話說,一個任何沒有簡單環路的連通圖都是一棵樹。
(2)圖的表示
1、鄰接矩陣:使用一個二維陣列 G[N][N]儲存圖,如果頂點 Vi 和 頂點 Vj 之間有邊,則 G[Vi][Vj] = 1 或 weight。鄰接矩陣是對稱的。
2、鄰接表:圖的一種鏈式儲存結構:對於圖G
中每個頂點Vi
,把所有鄰接於Vi
的頂點Vj
鏈成一個單鏈表,這個單鏈表稱為頂點Vi
的鄰接表。
鄰接表與鄰接矩陣的區別:鄰接表佔用空間少,適合儲存稀疏圖;鄰接矩陣適合儲存稠密圖。若判斷任意兩個結點之間是否有邊連線,可用鄰接矩陣。
(3)圖的遍歷
1、基於鄰接矩陣
public class Graph { //頂點數量 private int vertexSize; //頂點陣列 private int[] vertexs; private int[][] matrix; //設定不可到達的權值為1000 private static final int max=1000; private boolean[] isVisited; public Graph(int vertexSize){ this.vertexSize=vertexSize; matrix=new int[vertexSize][vertexSize]; vertexs=new int[vertexSize]; for(int i=0;i<vertexSize;i++){ vertexs[i]=i; } isVisited=new boolean[vertexSize]; } public int[] getVertexs() { return vertexs; } public void setVertexs(int[] vertexs) { this.vertexs = vertexs; } //獲取某個頂點的出度 public int getOutDegree(int index){ int degree=0; for(int j=0;j<matrix[index].length;j++){ int weight=matrix[index][j]; if(weight!=0&&weight!=MAX_WEIGHT){ degree++; } } return degree; } //獲取兩個頂點之間的權值 public int getWeight(int v1,int v2){ return matrix[v1][v2]==0?0:(matrix[v1][v2]==MAX_WEIGHT?-1:matrix[v1][v2]); } // 獲取某個頂點的第一個鄰接點 public int getFirstNeigbour(int index){ for(int j=0;j<vertexSize;j++){ if(matrix[index][j]>0&&matrix[index][j]<MAX_WEIGHT){ return j; } } return -1; } // 圖的深度優先遍歷演算法 private void depthFirstSearch(int i){ isVisited[i]=true; int w=getFirstNeigbour(i); while(w!=-1){ if(!isVisited[w]){ //需要遍歷該頂點 System.out.println("訪問到了:"+w+"頂點"); depthFirstSearch(w); } w=getNextNeighbour(i, w); } } public void depthFirstSearch(){ isVisited=new boolean[vertexSize]; for(int i=0;i<vertexSize;i++){ if(!isVisited[i]){ System.out.println("訪問到了:"+i+"頂點"); depthFirstSearch(i); } } isVisited=new boolean[vertexSize]; } public static void main(String[] args) { Graph graph=new Graph(9); int[] a1=new int[]{0,10,max,max,max,11,max,max,max}; int[] a2=new int[]{10,0,18,max,max,max,16,max,12}; int[] a3=new int[]{max,max,0,22,max,max,max,max,8}; int[] a4=new int[]{max,max,22,0,20,max,max,16,21}; int[] a5=new int[]{max,max,max,20,0,26,max,7,max}; int[] a6=new int[]{11,max,max,max,26,0,176,max,max}; int[] a7=new int[]{max,16,max,max,max,17,0,19,max}; int[] a8=new int[]{max,max,max,16,7,max,19,0,max}; int[] a9=new int[]{max,12,8,21,max,max,max,max,0}; graph.matrix[0]=a1; graph.matrix[1]=a2; graph.matrix[2]=a3; graph.matrix[3]=a4; graph.matrix[4]=a5; graph.matrix[5]=a6; graph.matrix[6]=a7; graph.matrix[7]=a8; graph.matrix[8]=a9; int degree=graph.getOutDegree(1); System.out.println("出度:"+degree); int weight=graph.getWeight(0, 4); System.out.println("權值:"+weight); graph.depthFirstSearch(); } }
2、基於鄰接表
1)圖的資料結構
package graph; import java.util.*; public class graphNode { public int val; public List<graphNode> neighbors; public boolean visited; public graphNode() { val = 0; neighbors = new ArrayList<graphNode>(); } public graphNode(int _val) { val = _val; neighbors = new ArrayList<graphNode>(); } public graphNode(int _val, ArrayList<graphNode> _neighbors) { val = _val; neighbors = _neighbors; } }
2)圖的深度優先遍歷和廣度優先遍歷
package graph; import java.util.*; public class create_And_Visited_Graph { //鄰接表如下 /* 1->2-5 2->1-5-4 3->2-4 4->2-5-3 5->4-1-2 */ public static void main(String[] args) { //建立圖節點 graphNode n1=new graphNode(1); graphNode n2=new graphNode(2); graphNode n3=new graphNode(3); graphNode n4=new graphNode(4); graphNode n5=new graphNode(5); //建立鄰接表 n1.neighbors.add(n2); n1.neighbors.add(n5); n2.neighbors.add(n1); n2.neighbors.add(n5); n2.neighbors.add(n4); n3.neighbors.add(n2); n3.neighbors.add(n4); n4.neighbors.add(n2); n4.neighbors.add(n5); n4.neighbors.add(n3); n5.neighbors.add(n4); n5.neighbors.add(n1); n5.neighbors.add(n2); //深度優先遍歷 DFS(n1); // System.out.println(); //廣度優先遍歷 // BFS(n1); } //深度優先遍歷 public static void DFS(graphNode root){ if (root == null) { return; } root.visited=true; System.out.print(root.val+" "); for (graphNode node : root.neighbors) { if (!node.visited) { DFS(node); } } } //廣度優先遍歷 public static void BFS(graphNode root) { Queue<graphNode> queue = new LinkedList<>(); queue.offer(root); root.visited=true; while (!queue.isEmpty()) { graphNode r = queue.poll(); System.out.print(r.val+" "); for (graphNode node : r.neighbors) { if (!node.visited) { queue.offer(node); node.visited=true; } } } } }