使用Boost Graph library(二)

接續上一篇文章:使用Boost Graph library(一) 

讓我們從一個新的圖的開始,定義一些屬性,然後加入一些帶屬性的頂點和邊。我們將給出所有的代碼,這樣你不需要將我們前面給出的代碼片段拼接起來。

 

// Property types

typedef property<edge_weight_t, int> EdgeWeightProperty;

typedef property<vertex_name_t, std::string,

  property<vertex_index2_t, int> > VertexProperties;

 

// Graph type

typedef adjacency_list<vecS, vecS, undirectedS,

  VertexProperties, EdgeWeightProperty> Graph;

 

// Graph instance

Graph g;

 

// Property accessors

property_map<Graph, vertex_name_t>::type

  city_name = get(vertex_name, g);

property_map<Graph, vertex_index2_t>::type

  city_index2 = get(vertex_index2, g);

property_map<Graph, edge_weight_t>::type

  edge_distance = get(edge_weight, g);

 

// Create the vertices

typedef graph_traits<Graph>::vertex_descriptor Vertex;

Vertex u1;

u1 = add_vertex(g);

city_name[u1] = "Los Angeles";

city_index2[u1] = 3;

 

  Vertex u2;

  u2 = add_vertex(g);

  city_name[u2] = "Bakersfield";

  city_index2[u2] = 2;

 

Vertex u3;

u3 = add_vertex(g);

city_name[u3] = "New York";

city_index2[u3] = 1;

 

// Create the edges

typedef graph_traits<Graph>::edge_descriptor Edge;

Edge e1;

e1 = (add_edge(u1, u2, g)).first;

edge_distance[e1] = 100;

 

Edge e2;

e2 = add_edge(u1, u3, g).first;

edge_distance[e2] = 2500;

 

第一段代碼中,我們定義了屬性類型,接着定義了圖類型,然後我們創建了圖的一個實例g。下一步我們要獲得對象的屬性來存取對象,如同我們前面所說的。

 

我們開始增加頂點和邊。爲了簡化代碼,從vertex_descriptor創建了一個自己的類型Vertex,接着我們傳遞g給協助函數add_vertex,從而爲g增加一個頂點。

 

注意

請記住,圖中的頂點是沒有位置的,所以我們不需要告訴圖我們想要增加頂點的位置,我們只需增加頂點即可。

 

圖增加一個頂點後返回該頂點的頂點描述,這樣我們可以利用屬性存取對象設置該頂點相應的屬性:city_name city_index2。設置屬性是容易的,我們只要將頂點象個索引似的放在[]中。

 

創建邊也是類似的,除了add_edge函數返回的是pair類型,而不是邊類型。所以我們給該函數的返回值加上.first得到需要的邊類型。我們將結果(邊類型)保存在一個變量中,接着我們使用該變量來設置屬性。由於在數學上邊無非就是兩個頂點組成的集合,所以我們需要將它們(兩個頂點)和圖本身傳遞給函數add_edge

 

遍歷頂點和邊

爲了遍歷邊和頂點,你需要調用edgesvertices來獲得相應的迭代子。這些函數的返回值類型是pair,你可以在你的遍歷中使用它們。下面的兩小塊代碼展示瞭如何遍歷邊和頂點。

// Iterate through the vertices and print them out

typedef graph_traits<Graph>::vertex_iterator vertex_iter;

std::pair<vertex_iter, vertex_iter> vp;

for (vp = vertices(g); vp.first != vp.second; ++vp.first)

  std::cout << city_name[*vp.first] << " " << city_index2[*vp.first] << std::endl;

std::cout << std::endl;

 

// Iterate through the edges and print them out

Vertex v1, v2;

typedef graph_traits<Graph>::edge_iterator edge_iter;

std::pair<edge_iter, edge_iter> ep;

edge_iter ei, ei_end;

for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)

  std::cout << edge_distance[*ei] << endl;

上面的這兩小塊代碼使用了兩種不同的方法來演示edgesvertices函數的返回值類型pair的使用。當遍歷頂點時,返回的是一個pair(我們爲這個pair創建了一個typedef);爲了訪問頂點,我們使用vp.first,這是一個迭代子;象絕大多數C++中的迭代子一樣,可以通過解引用(dereference)的方式來獲得它指向的對象。這樣我們可以寫出城市名稱和序號:

       city_name[*vp.first]

city_index2[*vp.first]

 

 

如果你不想使用pair,標準庫中有一個方便的協助函數tie,它可以將pair中的各個部分賦給tie函數中的實參。比如,edges函數返回一個pair,調用:

tie(ei, ei_end) = edges(g)

將保存pair的第一項給ei,第二項給ei_end。這樣,在循環中,你可以簡單地使用*ei來存取邊,就像:

edge_distance[*ei]

 

 

使用圖和boos解決問題

雖然遊戲Six Degrees of Kevin Bacon很好玩,但事實上游戲中的問題就是最短路徑問題,該問題有一些具體的應用,而超出了電影明星所考慮的問題。比方說,你叫一個貨運公司幫你從Bakersfield , CaliforniaLake Mary, Florida運送貨物,運送方需要找到一條從出發地到目的地的最短路徑。通常,這個包裹需要經過幾個城市,每個城市就可以看作的一個頂點,並且城市之間是用邊連接起來的。這個圖是帶權重的,這是因爲城市之間的距離是在變化的。在這一點上,這個問題有別於Six Degrees問題的。

 

數學家和計算機學家研究出了許許多多算法來求解圖論問題,這其中就包括了最短路徑問題,許多書全文討論如何使用圖論方法來解決問題。當然,許多大問題都涉及大量的頂點和邊,這樣的問題最適合讓計算機來計算了。

 

Boost庫包含許多著名的算法,這樣你就不需要自己編寫代碼實現這些算法了。比如它就包含了幾種不同的最短路徑算法。事實上,Kevin Bacon問題並不是圖論真正關心的問題,因爲它所有邊的權重都是1,這意味着當兩個演員出現在一本電影中,他們就連接起來了,而這連接並沒有所謂的物理距離。

 

當所有的邊的權重都是1時,算法就等價於廣度優先算法(BFS),BFS是用於從根節點(頂點)出發遍歷所有的頂點。其思想是從根頂點出發,訪問與根頂點相連的頂點,並標記這些頂點爲已經訪問過的。接着你再用同樣的方法繼續訪問還沒訪問過的頂點,直到所有的頂點都被訪問,或者在你找到你想要的頂點上停止遍歷,這也意味着你找到了你所要頂點的最短路徑。

 

提示 

請考慮:當你找到你所要尋找的頂點,你如何可以認爲你找到了最短路徑呢?這是因爲,假如有比你現在找到的更短的路徑,它應當在此之前就已經被找到了。這個理由聽起來很平凡,但從數學上,你已經能過看到算法是如何找到最短路徑的。

 

Boost庫的文檔包含了Kevin Bacon遊戲的很好的示例,在這裏就不重複這些類似的代碼了,我們建議去查看這個文檔。

 

對大多數程序員來說,比Kevin Bacon遊戲更感興趣的是文件依賴問題。象makeant程序都需要這個算法,比方說,你在寫一個電子表格軟件,表中有許多單元格,它們包含了對其他單元格引用的公式;需要你給出這些公式的計算順序。如果單元格A1的公式使用了單元格A2,並且A2也包含了一個公式,你就必須在計算A1之前先計算A2中的公式;這就是依賴問題。同樣的道理也出現在編譯程序(如makeant)中,如果file1依賴於file2,就需要在編譯file1之前編譯file2Boost Graph庫的文檔中有一個求解文件依賴問題的示例。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章