1. 程式人生 > 實用技巧 >樹和圖詳解

樹和圖詳解

(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;
                }
            }
        }
    }
}