1. 程式人生 > >圖的基礎知識

圖的基礎知識

targe fin 指針 估計 jks 大小 實現 每次 ner

1、概念

: 是一種復雜的非線性數據結構。
圖的二元組定義
圖 G 由兩個集合 V 和 E 組成,記為:
G=(V, E) 其中: V 是頂點的有窮非空集合,
E 是 V 中頂點偶對(稱為邊)的有窮集。

通常,也將圖 G 的頂點集和邊集分別記為 V(G) 和 E(G) 。 E(G) 可以是空集。若 E(G) 為空,則圖 G 只有頂點而沒有邊。

有向圖: 若圖 G 中的每條邊都是有方向的,則稱 G 為有向圖 (Digraph) 。
無向圖: 若圖 G 中的每條邊都是沒有方向的,則稱 G 為無向圖 (Undigraph) 。
完全圖:若無向圖中的每個頂點之間存在著一條邊,有向圖中的每兩個定點之間都存在著方向相反的兩條邊,則稱此圖為完全圖。
稠密圖

: 當一個圖接近完全圖時,則稱它為稠密圖。
稀疏圖:當一個圖含有較少邊數(即e<< n(n-1),雙小於號表示非常小於的含義)時,則稱它為稀疏圖。
簡單圖:圖中每條邊的頂點均不相同。

鄰接點
有向圖:如果 <u, v>∈ E, 則稱v為u的鄰接點,u為v的逆鄰接點。邊<u, v>與頂點u和v相關聯,從u出發的邊稱為u的出邊或鄰接邊,而指向頂點u的邊稱為u的入邊或逆鄰接邊。
無向圖:如果(u, v)∈ E, 則稱u與v互為鄰接點。
頂點的度、入度與出度
依附與某頂點v的邊數 稱為度;
在有向圖中,頂點v的 入度: 指以頂點為終點的邊的數目。
出度:指以頂點起始點的邊的數目;
無向圖中出度入度相等。

通路或路徑
:由m+1個頂點與m條邊交替構成的一個序列。
環路:如路徑的第一個頂點和最後一個頂點相同,稱之為環路。
可達分量:有向圖中,若頂點s到v有一條通路,稱v是從s可達的。對於s,從s可達的所有頂點的集合叫可達分量。
連通圖:無向圖中,若一個頂點到另一個頂點有路徑,則稱這2個頂點是連通的。如果圖中任意2頂點都是連通的,則稱該圖是連通圖。
連通分量:指無向圖的極大連通子圖。
:與圖中邊有關的實數。
帶權圖或網:邊上具有權值的圖。

強連通分量(有向圖)
:頂點s在圖G中的可達分量 與 s在鏡像圖R(G)中的可達分量的交集。
最小生成樹:從圖中的不同頂點或從同一頂點按不同的優先搜素過程可以得到不同的樹。而對於一個連通網(連通帶權圖)來說,生成的樹不同,每棵樹的代價(樹中每條邊上權值之和)也可能不同,代價最小的生成樹為圖的最小生成樹。

圖的遍歷:具體實現:http://blog.csdn.net/ochangwen/article/details/50729993

技術分享

圖的深度優先遍歷:1->2->4->6->5->3
圖的廣度優先遍歷:1->2->3->4->5->6

深度優先就是由開始點向最深處遍歷,沒有了就回溯到上一級頂點
廣度就是先把開始點的鄰接的所有點都遍歷了,沒有了就開始遍歷鄰接點的第一個點,直到所有的遍歷完成

最短路徑—Dijkstra算法和Floyd算法:具體實現http://blog.csdn.net/ochangwen/article/details/50730937

技術分享

1-->2 的最短路徑是:7
1-->3 的最短路徑是:9
1-->4 的最短路徑是:20
1-->5 的最短路徑是:20
1-->6 的最短路徑是:11

單源最短路徑算法,用於計算一個節點到其他!!所有節點!!的最短路徑
1-->2-->3-->6-->4-->5-->

圖的最小生成樹:具體實現-->

技術分享

一個連通圖的生成樹是一個極小的連通子圖,它含有圖中全部頂點,但只有足以構成一棵樹的n-1條邊。那麽我們把構造連通網的最小代價生成樹稱為最小生成樹。
找連通網的最小生成樹,經典的有兩種算法,普裏姆算法和克魯斯卡爾算法。下面分別介紹兩種算法。
1、普裏姆(Prim)算法
普裏姆算法,圖論中的一種算法,可在加權連通圖裏搜索最小生成樹。意即此算法搜索到的邊子集所構成的樹中,不但包括連通圖裏的所有頂點,且其所有邊的權值之和亦為最小。

1).頂點D被任意選為起始點;
2).頂點A、B、E和F通過單條邊與D相連。A是距離D最近的頂點,因此下一個是A。
3).然後是F --> B --> E-->C-->G。最小生成樹的權值之和為39。

鄰接矩陣實現的普裏姆算法的時間復雜度為O(n2)。

2、克魯斯卡爾(Kruskal)算法
kruskal算法充分體現了貪心算法的精髓,該算法總共選擇n- 1條邊,(共n個點)所使用的貪婪準則是:從剩下的邊中選擇一條不會產生環路的具有最小耗費的邊加入已選擇的邊的集合中。註意到所選取的邊若產生環路則不可能形成一棵生成樹。kruskal算法分e 步,其中e 是網絡中邊的數目。按耗費遞增的順序來考慮這e 條邊,每次考慮一條邊。當考慮某條邊時,若將其加入到已選邊的集合中會出現環路,則將其拋棄,否則,將它選入。
1).將所有的邊的長度排序,用排序的結果作為我們選擇邊的依據。這裏再次體現了貪心算法的思想。資源排序,對局部最優的資源進行選擇
2).再選邊:AD --> CE --> AB --> BE
3).下一步就是關鍵了。下面選擇那條邊呢? BC或者EF嗎?都不是,盡管現在長度為8的邊是最小的未選擇的邊。但是他們已經連通了(對於BC可以通過CE,EB來連接,類似的EF可以通過EB,BA,AD,DF來接連)。所以我們不需要選擇他們。類似的BD也已經連通了(這裏上圖的連通線用紅色表示了)。

技術分享

4).最後就剩下EG和FG了。當然我們選擇了EG
Kruskal算法的時間復雜度由排序算法決定,若采用快排則時間復雜度為O(N log N)。

克魯斯爾算法主要是針對邊來展開,邊數少時效率會非常高,所以對於稀疏圖有很大的優勢;
普裏姆算法主要是針對頂點來展開,對於稠密圖,即邊數非常多的情況會更好一些。

2、圖的抽象類型

public interface Graph {
public static final int UndirectedGraph = 0;//無向圖
public static final int DirectedGraph = 1;//有向圖

//返回圖的類型
public int getType();
//返回圖的頂點數
public int getVexNum();
//返回圖的邊數
public int getEdgeNum();
//返回圖的所有頂點
public Iterator getVertex();
//返回圖的所有邊
public Iterator getEdge();
//刪除一個頂點v
public void remove(Vertex v);
//刪除一條邊e
public void remove(Edge e);
//添加一個頂點v
public Node insert(Vertex v);
//添加一條邊e
public Node insert(Edge e);
//判斷頂點u、v是否鄰接,即是否有邊從u到v
public boolean areAdjacent(Vertex u, Vertex v);
//返回從u指向v的邊,不存在則返回null
public Edge edgeFromTo(Vertex u, Vertex v);
//返回從u出發可以直接到達的鄰接頂點
public Iterator adjVertexs(Vertex u);
//對圖進行深度優先遍歷
public Iterator DFSTraverse(Vertex v);
//對圖進行廣度優先遍歷
public Iterator BFSTraverse(Vertex v);
//求頂點v到其他頂點的最短路徑
public Iterator shortestPath(Vertex v);
//求無向圖的最小生成樹,如果是有向圖不支持此操作
public void generateMST() throws UnsupportedOperation;
//求有向圖的拓撲序列,無向圖不支持此操作
public Iterator toplogicalSort() throws UnsupportedOperation;
//求有向無環圖的關鍵路徑,無向圖不支持此操作
public void criticalPath() throws UnsupportedOperation;
}

3、圖的存儲方法

3.1、鄰接矩陣表示法

采用2個數組來表示圖;一個是存儲所有頂點信息的一維數組、一個是存儲圖中頂點之間關聯關系的二維數組,這個二維數組稱為鄰接矩陣。

技術分享

對於無向圖a,鄰接矩陣是一個對稱矩陣。第i行(i列)非∞元素的個數正好是第i個頂點的度。
對於有向圖b,第i行非∞元素的個數是第i個頂點的出度、i列非∞元素的個數是第i個頂點的入度。

鄰接矩陣的不足:
1、由n個頂點構成的圖中最多可以有n2條邊,但大多數情況下,邊的數目遠遠達不到這個量級,鄰接矩陣中大多數單元都是閑置的。
2、矩陣結構是靜態的,大小N需要預先估計,然後創建N*N的矩陣,而圖的規模往往是動態變化的,N估計過大會造成空間浪費,過小則造成空間不夠用。

3.2、鄰接表表示法:

鏈接表是圖的一種動態的鏈式存儲方法,類似於樹的孩子鏈表表示法。
對於n個頂點、m條邊的無向圖,采用鄰接表,需要n個表頭節點和2m個邊表節點。在邊稀疏的情況下,要比使用鄰接矩陣節省空間。

技術分享

鄰接表的特性:
1.在無向圖中,頂點v的度為v的鄰接表中 表表節點的個數。
2.在有向圖中,頂點v的鄰接表中邊表的節點個數僅為v的出度。入度需要遍歷這個鄰接表或者從逆鄰接表中獲取。判斷2個頂點直接是否有邊需要搜素2個頂點對應的鄰接表。不如鄰接矩陣方便。

3.3、雙鏈式存儲結構:

 鄰接表是圖的一種很有效的存儲結構,但是在該結構中刪除頂點,邊,或添加頂點,邊的操作實現起來都比較復雜,所需時間代價較大。所以給出雙鏈式存儲結構解決上述問題。
 頂點、邊都抽象為獨立的類,所有的頂點類都存儲在一個鏈接表中,所有的邊類也存儲在一個鏈接表中。並在頂點、邊之間建立關系。

技術分享

邊中有5個重要的指針域:第一頂點域、第二頂點域、第一邊表位置域、第二邊表位置域、邊位置域。
有向圖中:第一頂點域指向該邊的起始頂點在頂點表中的位置、第二頂點域指向該邊的終止頂點在頂點表中的位置; 第一邊表位置域指向邊在其起始點的出邊表中的位置,第二邊表位置域指向邊表在其終止點的入邊表中的位置;
無向圖:二個頂點域分別指向邊的兩個頂點在頂點表中的位置。第一邊表位置域、第二邊表位置域分別指向第一、第二頂點的鄰接邊表中的位置。
邊位置域:指向邊在邊表中的位置。

圖的基礎知識