1. 程式人生 > >資料結構--圖(Java版)

資料結構--圖(Java版)

一、圖的基本概念

線性表和樹兩類資料結構,線性表中的元素是“一對一”的關係,樹中的元素是“一對多”的關係,本章所述的圖結構中的元素則是“多對多”的關係。

圖(Graph)是一種複雜的非線性結構,在圖結構中,每個元素都可以有零個或多個前驅,也可以有零個或多個後繼,也就是說,元素之間的關係是任意的。

無向圖:

無向圖是由頂點和邊構成。

有向圖:

有向圖是由頂點和有向邊構成。

完全圖:

如果任意兩個頂點之間都存在邊叫完全圖,有向的邊叫有向完全圖。如果無重複的邊或者頂點到自身的邊叫簡單圖。

二、圖的節點表示

/**
 * 無向簡單圖的節點
 * @author hoaven
 */
public class GraphNode<T> {
    T data;
    List<GraphNode<T>> neighborList;
    boolean visited;

    public GraphNode(T data){
        this.data = data;
        neighborList = new ArrayList<GraphNode<T>>();
        visited = false
; } public boolean equals(GraphNode<T> node){ return this.data.equals(node.data); } /** * 還原圖中所有節點為未訪問 */ public void restoreVisited(){ restoreVisited(this); } /** * 還原node的圖所有節點為未訪問 * @param node */ private void restoreVisited
(GraphNode<T> node){ if(node.visited){ node.visited = false; } List<GraphNode<T>> neighbors = node.neighborList; for(int i = 0; i < neighbors.size(); i++){ restoreVisited(neighbors.get(i)); } } }

三、圖的深度優先和廣度優先搜尋

1、深度優先

1.1、介紹

圖的深度優先搜尋(Depth First Search),和樹的先序遍歷比較類似。

思路:假設初始狀態是圖中所有頂點均未被訪問,則從某個頂點v出發,首先訪問該頂點,然後依次從它的各個未被訪問的鄰接點出發深度優先搜尋遍歷圖,直至圖中所有和v有路徑相通的頂點都被訪問到。 若此時尚有其他頂點未被訪問到,則另選一個未被訪問的頂點作起始點,重複上述過程,直至圖中所有頂點都被訪問到為止。顯然,深度優先搜尋是一個遞迴的過程。

1.2、無向圖深度優先搜尋圖解

這裡寫圖片描述

對上面的圖G1進行深度優先遍歷,從頂點A開始。
這裡寫圖片描述

  • 第1步:訪問A。
  • 第2步:訪問(A的鄰接點)C。在第1步訪問A之後,接下來應該訪問的是A的鄰接點,即”C,D,F”中的一個。但在本文的實現中,頂點ABCDEFG是按照順序儲存,C在”D和F”的前面,因此,先訪問C。
  • 第3步:訪問(C的鄰接點)B。在第2步訪問C之後,接下來應該訪問C的鄰接點,即”B和D”中一個(A已經被訪問過,就不算在內)。而由於B在D之前,先訪問B。
  • 第4步:訪問(C的鄰接點)D。
  • 第5步:訪問(A的鄰接點)F。
  • 第6步:訪問(F的鄰接點)G。
  • 第7步:訪問(G的鄰接點)E。

訪問順序是:A -> C -> B -> D -> F -> G -> E

1.3、無向圖深度優先搜尋圖解

這裡寫圖片描述

對上面的圖G2進行深度優先遍歷,從頂點A開始。

這裡寫圖片描述

訪問順序是:A -> B -> C -> E -> D -> F -> G

2、廣度優先

2.1、介紹

從圖中某頂點v出發,在訪問了v之後依次訪問v的各個未曾訪問過的鄰接點,然後分別從這些鄰接點出發依次訪問它們的鄰接點,並使得“先被訪問的頂點的鄰接點先於後被訪問的頂點的鄰接點被訪問,直至圖中所有已被訪問的頂點的鄰接點都被訪問到。如果此時圖中尚有頂點未被訪問,則需要另選一個未曾被訪問過的頂點作為新的起始點,重複上述過程,直至圖中所有頂點都被訪問到為止。換句話說,廣度優先搜尋遍歷圖的過程是以v為起點,由近至遠。

2.2、無向圖廣度優先搜尋圖解

這裡寫圖片描述

訪問順序是:A -> C -> D -> F -> B -> G -> E

2.3、有向圖廣度優先搜尋圖解

這裡寫圖片描述

訪問順序是:A -> B -> C -> E -> F -> D -> G

3、實現

/**
 * 圖的廣度優先搜尋和深度優先搜尋實現
 *
 * @author hoaven
 * @see GraphNode
 */
public class GraphSearch<T> {

    public StringBuffer searchPathDFS = new StringBuffer();
    public StringBuffer searchPathBFS = new StringBuffer();

    /**
     * 深度優先搜尋實現
     *
     * @param root
     */
    public void searchDFS(GraphNode<T> root) {
        if (root == null) {
            return;
        }

        // visited root
        if (searchPathDFS.length() > 0) {
            searchPathDFS.append("->");
        }
        searchPathDFS.append(root.data.toString());
        root.visited = true;

        for (GraphNode<T> node : root.neighborList) {
            if (!node.visited) {
                searchDFS(node);
            }
        }
    }

    /**
     * 廣度優先搜尋實現,使用佇列
     *
     * @param root
     */
    public void searchBFS(GraphNode<T> root) {
        IQueue<GraphNode<T>> queue = new Queue<GraphNode<T>>();

        // visited root
        if (searchPathBFS.length() > 0) {
            searchPathBFS.append("->");
        }
        searchPathBFS.append(root.data.toString());
        root.visited = true;

        // 加到佇列隊尾
        queue.enqueue(root);

        while (!queue.isEmpty()) {
            GraphNode<T> r = queue.dequeue();
            for (GraphNode<T> node : r.neighborList) {
                if (!node.visited) {
                    searchPathBFS.append("->");
                    searchPathBFS.append(node.data.toString());
                    node.visited = true;

                    queue.enqueue(node);
                }
            }
        }
    }
}

//測試用例
/**
 * GraphSearch測試
 * @author hoaven
 * @see GraphNode
 * @see GraphSearch
 */
public class GraphSearchTest {
    GraphNode<Integer> node1;
    GraphNode<Integer> node2;
    GraphNode<Integer> node3;
    GraphNode<Integer> node4;
    GraphNode<Integer> node5;
    GraphNode<Integer> node6;
    GraphNode<Integer> node7;
    GraphNode<Integer> node8;
    GraphNode<Integer> node9;
    GraphNode<Integer> node10;

    @Before
    public void before(){
        node1 = new GraphNode<Integer>(1);
        node2 = new GraphNode<Integer>(2);
        node3 = new GraphNode<Integer>(3);
        node4 = new GraphNode<Integer>(4);
        node5 = new GraphNode<Integer>(5);
        node6 = new GraphNode<Integer>(6);
        node7 = new GraphNode<Integer>(7);
        node8 = new GraphNode<Integer>(8);
        node9 = new GraphNode<Integer>(9);
        node10 = new GraphNode<Integer>(10);

        node1.neighborList.add(node2);
        node1.neighborList.add(node3);

        node2.neighborList.add(node4);
        node2.neighborList.add(node5);
        node2.neighborList.add(node6);

        node3.neighborList.add(node1);
        node3.neighborList.add(node6);
        node3.neighborList.add(node7);
        node3.neighborList.add(node8);

        node4.neighborList.add(node2);
        node4.neighborList.add(node5);

        node5.neighborList.add(node2);
        node5.neighborList.add(node4);
        node5.neighborList.add(node6);

        node6.neighborList.add(node2);
        node6.neighborList.add(node5);
        node6.neighborList.add(node3);
        node6.neighborList.add(node8);
        node6.neighborList.add(node9);
        node6.neighborList.add(node10);

        node7.neighborList.add(node3);

        node8.neighborList.add(node3);
        node8.neighborList.add(node6);
        node8.neighborList.add(node9);

        node9.neighborList.add(node6);
        node9.neighborList.add(node8);
        node9.neighborList.add(node10);

        node10.neighborList.add(node6);
        node10.neighborList.add(node9);
    }

    @Test
    public void searchDFSTest(){
        GraphSearch<Integer> graphSearch = new GraphSearch<Integer>();
        graphSearch.searchDFS(node1);

        String expectedSearchPath = "1->2->4->5->6->3->7->8->9->10";
        Assert.assertEquals(expectedSearchPath, graphSearch.searchPathDFS.toString());
    }

    @Test
    public void searchBFSTest(){
        GraphSearch<Integer> graphSearch = new GraphSearch<Integer>();
        graphSearch.searchBFS(node1);

        String expectedSearchPath = "1->2->3->4->5->6->7->8->9->10";
        Assert.assertEquals(expectedSearchPath, graphSearch.searchPathBFS.toString());
    }
}