使用Boost Graph library(一)

本文是一篇譯文,來自:http://blog.csdn.net/jjqtony/article/details/1555965 

本文翻譯自"Manipulating C++ Graph Data Structures with the Boost Graph Library",原文請見:

http://www.informit.com/articles/article.asp?p=673259&seqNum=4

 

使用Boost創建一個簡單的圖

如果你不得不使用算法,你要麼自己構建圖數據結構,或者使用第三方庫。本文所使用的是Boost庫。讓我們通過第一個例子來展示如何使用Boost庫以支持圖算法。

 

一般地,在計算機中可以表示爲一組頂點和一組連接頂點的邊的集合;邊簡單地表示爲頂點對。進一步每一個頂點和每一條邊可能還依附着額外的數據,比如對邊來說會有一個數來表示權重。這就是Boost庫如何工作的本質。

 

Boost Graph library是以模板的形式提供的,這意味着你可以在這些模板的基礎上創建自己的類型。然而,這些模板的所有類型參數都有默認值,所以你無需任何定製,就可以創建一個圖。

 

下面的一小段代碼展示了創建一個圖和插入頂點和邊的過程,這段代碼可以被完全編譯,但它除了創建一個圖外,並沒有做其他什麼事情。

 

#include <iostream>

#include <stdlib.h>

#include <boost/graph/adjacency_list.hpp>

using namespace boost;

int main(int argc, char *argv[])

{

  adjacency_list<> mygraph;

  add_edge(1, 2, mygraph);

  add_edge(1, 3, mygraph);

  add_edge(1, 4, mygraph);

  add_edge(2, 4, mygraph);

  return 0;

}

mygraph變量就是一個圖,其類型是adjacency_list,它是圖庫中基本的圖模板類型。在這裏我們使用了默認參數,在<>中的參數列表是空的。

函數add_edge是一個模板協助函數,它用來給圖增加頂點或邊的。爲了使用它,你需要傳遞兩個頂點參數,如果在該圖中這些頂點不存在,則它們會被加入;然後連接這兩個頂點的邊也會被加入。這樣,上面的這段代碼就創建了一個圖,它具有四個頂點,編號是1,2,3,4,邊有(1,2)(1,3)(1,4)(2,4)

這只是一個很基本的例子。爲了創建更加複雜的,你需要定義更多的數據關聯到你的邊和頂點,我們將在下一節討論。

給頂點和邊增加屬性值

當你使用圖,通常你會附加一些信息給頂點和邊,圖庫使用了一個很有趣的方法來做到這一點。諸如名字或顏色信息作爲一種屬性來存儲,一個屬性由一個名字(或標識),如距離,和類型,如浮點數或字符串,組成。你可以給頂點和邊增加屬性。

 

提示

庫中,有幾個常用的,已經內建好了的屬性標識,如顏色,名字和權重。所有的內建標識都列在在線幫助中,同樣你可以在graph/properties.hpp這個頭文件中找到它們。它們是用宏的方式來定義的,形成了枚舉常量。比如,在properties.hpp中有一行屬性定義:

BOOST_DEF_PROPERTY(edge, name);

 

這個宏的結果是產生了一個枚舉常量edge_name,它的類型是edge_name_t。你還能看到其他的內建屬性標識定義。

 

當你定義一個圖時,你可以指定跟該圖相關聯的的屬性類型,你也可以加入你很多想要的屬性給頂點和邊。爲了定義屬性,首先你需要爲每一個屬性類型創建一個模板類型,然後在你具現化圖之前定義圖類型。比如,假設你想每一條邊增加距離屬性,給每個頂點賦予城市名稱,下面的代碼就來展示如何定義這兩種屬性類型:

 

typedef property<edge_weight_t, int> EdgeWeightProperty;

typedef property<vertex_name_t, std::string> VertexNameProperty;

 

第一行定義了EdgeWeightProperty類型,模板的第一個參數是這個屬性的標識,在這裏我們使用的是預先定義好的標識edge_weight_t,它的意思是邊的權重,第二個參數是int,意思是這個屬性具有一個整型數值。第二行定義了第二個屬性,它的標識是vertex_name_t,類型是string

 

現在你已經定義好屬性,你可以定義一個圖類型了:

       typedef adjacency_list<vecS, vecS, undirectedS,

       VertexNameProperty, EdgeWeightProperty> Graph;

 

這個定義中的前三個模板參數是預先定義好的,第一和第二個參數(都是vecS)表示我們要求圖使用vector來作爲圖的內部存儲方式,第三個參數說明這是一個無向圖,第四、五個參數是我們剛剛定義的屬性類型,前者是頂點的屬性類型,後者是邊的屬性類型。

 

這個時候,你可能會有疑問:如何給頂點和邊指定不止一個屬性類型呢?事實上,可以把屬性串聯在一起。基本類型模板包含了一個模板參數,它可以是你已經定義的屬性類型。下面是頂點類型定義,不過,這時我們定義了爲頂點定義了兩個屬性,一個是name,一個是index(下面的代碼使用了預定義的標識vertex_index2_t):

       typedef property<vertex_index2_t, int>

                    VertexIndexProperty;

typedef property<vertex_name_t, std::string,

                    VertexIndexProperty> VertexNameProperty;

 

注意

這裏使用的是vertex_index2_t,而不是vertex_index_t,這是因爲後者有一些與它有關的內建模板已經做了我們正在做的。

 

我們首先定義了index屬性,不過這個順序無關緊要。屬性VertexNameProperty就包含了VertexIndexProperty作爲它的第三個參數,這樣就創建了一個鏈。然後你也可以象前面一樣將VertexNameProperty作爲第四個參數傳遞給圖定義。

 

你也可以精簡你的代碼,不過精簡過的代碼對於圖庫初學者來說可讀性要差一些,雖然一旦你理解了屬性的這種機制後,這種精簡的過程也是十分的直接。

typedef property<vertex_name_t, std::string,

             property<vertex_index2_t, int> > VertexProperties;

 

我們重命名了類型VertexProperties,這是由於它現在包含了兩種不同的屬性。有個地方請大家注意,C++標準要求在兩個>之間要有空格,這是爲了同>>運算符區分開來。別忘了這個空格,否則你將得到編譯錯誤。

 

操縱屬性

爲了訪問http://shanzhizi.blog.51cto.com/中的屬性,你需要從圖庫中獲得一個協助對象,對於每一種屬性類型你都需要一個協助對象。這些對象是模板對象,一旦你創建了一個圖類型的實例後,你就可以獲得它。下面是獲得前一節定義的三個屬性的代碼:

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);

 

別擔心這裏的函數名字是一個很通用的單詞get,這個函數是得到很好定義的模板函數,它會編譯的很好。

 

第一條語句創建了一個對象來獲得vertex_name屬性,仔細觀察這條語句的參數,在最左邊,傳遞給模板的參數Graph是前面定義過的,緊跟着是屬性的標識vertex_name_t;右邊,在get函數裏,第一個參數並不是屬性的標識名,而是一個與該屬性相關的枚舉類型vertex_name。每一個預定義的標識名都有一個枚舉類型。get函數的第二個參數是圖對象g

 

請記住,這裏的三個調用你可以用來獲得頂點的屬性和邊的屬性,雖然我們這裏給出的代碼片段是針對頂點來做的。下面的一行代碼將來要用於獲得屬性:

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

 

這裏的vp.first是一個指向vertex的指針。對象city_namemap一樣地工作,它將一個頂點對象作爲參數,然後返回該頂點合適的屬性,在這個例子中,返回的是城市名。

 

加入頂點和邊

在你指定了屬性類型和定義了圖類型之後,你已經可以去創建一個圖實例,然後向圖結構中加入頂點和邊,這些頂點和邊是具有你所定義的屬性的。

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