1. 程式人生 > 實用技巧 >python 面向物件-繼承、封裝和多型

python 面向物件-繼承、封裝和多型

一、圖的基本介紹●➢

●圖是一種資料結構,其中節點可以具有零個或多個相鄰元素。兩個節點之間的連線稱為邊。節點也可稱為頂點

圖的概念


二、圖的表示方式

●圖的表示方式有兩種:二維陣列表示(鄰接矩陣)、【陣列+連結串列】表示(鄰接表)

1.鄰接矩陣

●鄰接矩陣是表示圖形中頂點之間相鄰關係的矩陣,對於n個頂點的圖而言,矩陣是的row和col表示的是1...n個點。

2.鄰接表

●鄰接矩陣需要為每個頂點都分配n個邊的空間,其實有很多邊都是不存在,會造成空間的一定損失.
●鄰接表的實現只關心存在的邊,不關心不存在的邊。因此沒有空間浪費,鄰接表由【陣列+連結串列】組成

3.圖遍歷介紹

●所謂圖的遍歷,即是對結點的訪問。一個圖有那麼多個結點,如何遍歷這些結點,需要特定策略,一般有兩種訪問策略: (1)深度優先遍歷(2)廣度優先遍歷.

深度優先遍歷基本思想

●圖的深度優先搜尋(Depth First Search)。

➢深度優先遍歷,從初始訪問結點出發,初始訪問結點可能有多個鄰接結點,深度優先遍歷的策略就是首先訪問第一個鄰接結點,然後再以這個被訪問的鄰接結點作為初始結點,訪問它的第一個鄰接結點, 可以這樣理解: 每次都在訪問完當前結點後首先訪問當前結點的第一個鄰接結點。
➢我們可以看到,這樣的訪問策略是優先往縱向挖掘深入,而不是對一個結點的所有鄰接結點進行橫向訪問。
➢顯然,深度優先搜尋是一個遞迴的過程

深度優先遍歷演算法步驟

➢1)訪問初始結點v,並標記結點v為已訪問。
➢2)查詢結點v的第一一個鄰接結點w。
➢3)若w存在,則繼續執行4,如果w不存在,則回到第1步,將從v的下一個結點繼續。
➢4)若w未被訪問,對w進行深度優先遍歷遞迴(即把w當做另一個v, 然後進行步驟123)。
➢5)查詢結點v的w鄰接結點的下一個鄰接結點,轉到步驟3。

圖的廣度優先搜尋

圖的廣度優先搜尋(Broad First Search)。類似於一個分層搜尋的過程,廣度優先遍歷需要使用一個佇列以保持訪問過的結點的順序,以便按這個順字來訪問這些結點的鄰接結點

廣度優先遍歷演算法步驟

1)訪問初始結點v並標記結點v為已訪問。
2)結點v入佇列

  1. 當佇列非空時,繼續執行,否則演算法結束。
    4)出佇列, 取得隊頭結點u。
    5)查詢結點u的第一個鄰接結點w。
  2. 若結點u的鄰接結點w不存在,則轉到步驟3; 否則迴圈執行以下三個步驟:

6.1若結點w尚未被訪問,則訪問結點w並標記為已訪問。
6.2結點w入佇列
6.3查詢結點u的繼w鄰接結點後的下一個鄰接結點w,轉到步驟6。

package com.xudong.DataStructures;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

public class GraphDemo {

    private ArrayList<String> vertexList; //儲存頂點的集合
    private int[][] edges;//儲存圖對應的鄰接矩陣
    private int numOfEdges;//表示邊的數目
    private boolean[] isVisted;//記錄節點是否被訪問

    public static void main(String[] args) {
        int n = 5;
        String Vertexs[] = {"A","B","C","D","E"};
        //建立圖物件
        GraphDemo graph = new GraphDemo(n);
        //新增頂點
        for (String vertex : Vertexs){
            graph.insertVertex(vertex);
        }
        //新增邊
        graph.insertEdge(0,1,1);
        graph.insertEdge(0,2,1);
        graph.insertEdge(1,2,1);
        graph.insertEdge(1,3,1);
        graph.insertEdge(1,4,1);

        graph.showGraph();

        System.out.println("深度優先遍歷:");
        graph.dfs();
        System.out.println();
        System.out.println("廣度優先遍歷:");
        graph.bfs();
    }

    //構造器
    public GraphDemo(int n){
        edges = new int[n][n];
        vertexList = new ArrayList<String>(n);
        numOfEdges = 0;
        isVisted = new boolean[5];
    }

    //得到第一個鄰接節點的下標
    public int getFirstNeighbor(int index){
        for (int j = 0; j < vertexList.size(); j++) {
            if (edges[index][j] > 0){
                return j;
            }
        }
        return -1;
    }
    //得到下一個鄰接節點的下標
    public int getNextNeighbor(int v1,int v2){
        for (int j = v2 + 1; j < vertexList.size(); j++) {
            if (edges[v1][j] > 0){
                return j;
            }
        }
        return -1;
    }

    //深度優先遍歷演算法
    private void dfs(boolean[] isVisted,int i){
        //首先訪問該節點,輸出
        System.out.print(getValueByIndex(i) + " -> ");
        //將節點設定為已經訪問
        isVisted[i] = true;
        //查詢節點i的第一個鄰接節點w
        int w = getFirstNeighbor(i);
        while (w != -1){
            if (!isVisted[w]){//如果當前節點沒有被訪問過
                dfs(isVisted,w);
            }
            //如果訪問過
            w = getNextNeighbor(i,w);
        }
    }
    //對dfs過載,遍歷所有節點,並進行dfs
    public void dfs(){
        isVisted = new boolean[vertexList.size()];
        //遍歷所有節點,進行回溯
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisted[i]){
                dfs(isVisted,i);
            }
        }
    }

    //對一個節點進行廣度優先遍歷的方法
    private void bfs(boolean[] isVisted,int i){
        int u;//表示佇列頭節點對應的下標
        int w;//鄰接節點w
        //佇列,記錄節點訪問的順序
        LinkedList queue = new LinkedList();
        //訪問節點,輸出節點資訊
        System.out.print(getValueByIndex(i) + " -> ");
        //標記為已訪問
        isVisted[i] = true;
        //將節點加入佇列
        queue.addLast(i);
        while (!queue.isEmpty()){
            //取出佇列頭結點的下標
            u = (Integer) queue.removeFirst();
            //得到第一個鄰接節點的下標w
            w = getFirstNeighbor(u);
            while (w != -1){
                //是否訪問過
                if (!isVisted[w]){
                    System.out.print(getValueByIndex(w) + " -> ");
                    //標記為已訪問
                    isVisted[w] = true;
                    //入佇列
                    queue.addLast(w);
                }
                //以u為前驅節點,找w後面的下一個鄰接節點
                w = getNextNeighbor(u,w);//體現出廣度優先
            }
        }
    }
    //對bfs過載,遍歷所有節點,並進行bfs
    public void bfs(){
        isVisted = new boolean[vertexList.size()];
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisted[i]){
                bfs(isVisted,i);
            }
        }
    }


    //------------常用方法------------------
    //返回節點的個數
    public int getNumOfVertex(){
        return vertexList.size();
    }
    //顯示圖對應的矩陣
    public void showGraph(){
        for (int[] link : edges){
            System.err.println(Arrays.toString(link));
        }
    }
    //得到邊的數目
    public int getNumOfEdges(){
        return numOfEdges;
    }
    //返回節點i的下標
    public String getValueByIndex(int i){
        return vertexList.get(i);
    }
    //返回v1和v2的權值
    public int getWeight(int v1,int v2){
        return edges[v1][v2];
    }


    //插入節點
    public void insertVertex(String vertex){
        vertexList.add(vertex);
    }

    /**新增邊
     * @param v1 頂點的下標
     * @param v2 第二個頂點的下標
     * @param weight 權值
     */
    public void insertEdge(int v1,int v2,int weight){
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges++;
    }
}