BGL官網(wǎng)入口
Boost庫(kù)說(shuō)明
BGL的用途是給某些圖的結(jié)構(gòu)提供接口,而隱藏其內(nèi)部細(xì)節(jié)
不需要built,但是編譯程序一定要--optimized
三種方式得到STL:
- 算法/數(shù)據(jù)結(jié)構(gòu)
每種算法都以數(shù)據(jù)結(jié)構(gòu)無(wú)關(guān)的方式寫(xiě),允許在不同類(lèi)的容器中使用。代碼量為O(m+n),m是算法數(shù),n是容器數(shù)。 - 函數(shù)對(duì)象擴(kuò)展
用戶可以通過(guò)函數(shù)對(duì)象自行修改STL。 - 元素類(lèi)型參數(shù)化
像std::list<T>,
BGL采用了類(lèi)似STL的方式 - 算法/數(shù)據(jù)結(jié)構(gòu)互通
首先,將BGL的圖形算法寫(xiě)入一個(gè)可以抽出特定圖形數(shù)據(jù)結(jié)構(gòu)細(xì)節(jié)的接口。 像STL一樣,BGL使用迭代器來(lái)定義數(shù)據(jù)結(jié)構(gòu)遍歷的接口。 有三種不同的圖形遍歷模式:遍歷圖形中的所有頂點(diǎn),遍歷所有邊緣,并沿著圖形的相鄰結(jié)構(gòu)(從頂點(diǎn)到其每個(gè)鄰居)。 每個(gè)遍歷模式都有獨(dú)立的迭代器
該通用接口允許諸如breadth_first_search()之類(lèi)的模板函數(shù)可以處理各種圖形數(shù)據(jù)結(jié)構(gòu),從使用指針鏈接節(jié)點(diǎn)的圖形到以數(shù)組編碼的圖形。 這種靈活性在圖形領(lǐng)域尤為重要。 圖形數(shù)據(jù)結(jié)構(gòu)通常是針對(duì)特定應(yīng)用程序定制的。 傳統(tǒng)上,如果程序員想要重用算法實(shí)現(xiàn),他們必須將它們的圖形數(shù)據(jù)轉(zhuǎn)換/復(fù)制到圖形庫(kù)的規(guī)定的圖形結(jié)構(gòu)中。 圖書(shū)館如LEDA,GTL,Stanford GraphBase; 在Fortran中編寫(xiě)的圖形算法尤其如此。 這嚴(yán)重限制了圖形算法的重用。
相比之下,使用外部自適應(yīng)的BGL通用圖形算法可以按照原樣使用定制(或甚至遺留的)圖形結(jié)構(gòu)(參見(jiàn)“如何將現(xiàn)有圖形轉(zhuǎn)換為BGL”)。 外部自適應(yīng)圍繞數(shù)據(jù)結(jié)構(gòu)包裝一個(gè)新界面,而不需要復(fù)制,而不將數(shù)據(jù)放在適配器對(duì)象內(nèi)。 BGL界面經(jīng)過(guò)精心設(shè)計(jì),使之適應(yīng)性變得容易。 為了證明這一點(diǎn),我們?cè)贐GL圖算法中建立了使用各種圖形結(jié)構(gòu)(LEDA圖,Stanford GraphBase圖,甚至Fortran樣式陣列)的接口代碼。 - 通過(guò)訪客實(shí)現(xiàn)擴(kuò)展
BGL的圖算法是可擴(kuò)展的。 BGL引入了一個(gè)訪問(wèn)者的概念,它只是一個(gè)具有多種方法的函數(shù)對(duì)象。 在圖形算法中,通常有幾個(gè)關(guān)鍵的“事件點(diǎn)”可用于插入用戶定義的操作。 訪問(wèn)對(duì)象具有在每個(gè)事件點(diǎn)調(diào)用的不同方法。 特定的事件點(diǎn)和相應(yīng)的訪問(wèn)者方法取決于具體的算法。 它們通常包括start_vertex(),discover_vertex(),inspect_edge(),tree_edge()和finish_vertex()等方法。 - 頂點(diǎn)和邊緣屬性多參數(shù)化
BGL通用的第三種方法類(lèi)似于STL容器中的元素類(lèi)型的參數(shù)化,但是對(duì)于圖形來(lái)說(shuō),故事再?gòu)?fù)雜一些。 我們需要將值(稱(chēng)為“屬性”)與圖形的頂點(diǎn)和邊緣相關(guān)聯(lián)。 另外,通常需要將多個(gè)屬性與每個(gè)頂點(diǎn)和邊緣相關(guān)聯(lián); 這是我們通過(guò)多參數(shù)化的意思。 STL std :: list <T>類(lèi)的元素類(lèi)型有一個(gè)參數(shù)T。 類(lèi)似地,BGL圖類(lèi)具有頂點(diǎn)和邊緣“屬性”的模板參數(shù)。 屬性指定屬性的參數(shù)化類(lèi)型,并為屬性分配標(biāo)識(shí)標(biāo)簽。 該標(biāo)簽用于區(qū)分邊緣或頂點(diǎn)可能具有的多個(gè)屬性。 附加到特定頂點(diǎn)或邊緣的屬性值可以通過(guò)屬性映射獲取。 每個(gè)屬性都有一個(gè)單獨(dú)的屬性映射。
傳統(tǒng)圖形庫(kù)和圖形結(jié)構(gòu)在圖形屬性的參數(shù)化方面下降。 這是圖表數(shù)據(jù)結(jié)構(gòu)必須為應(yīng)用程序定制的主要原因之一。 BGL圖類(lèi)中屬性的參數(shù)化使它們非常適合重復(fù)使用。
算法
核心算法模式和一系列圖算法:
核心算法模式:
- Breadth First Search
- Depth First Search
- Uniform Cost Search
BGL不會(huì)用核心算法模式在圖上計(jì)算數(shù)值,只是為圖算法構(gòu)建塊。而圖算法包括以下: - Dijkstra's Shortest Paths
- Bellman-Ford Shortest Paths
- Johnson's All-Pairs Shortest Paths
- Kruskal's Minimum Spanning Tree
- Prim's Minimum Spanning Tree
- Connected Components
- Strongly Connected Components
- Dynamic Connected Components (using Disjoint Sets)
- Topological Sort
- Transpose
- Reverse Cuthill Mckee Ordering
- Smallest Last Vertex Ordering
- Sequential Vertex Coloring
數(shù)據(jù)結(jié)構(gòu)
提供兩種圖類(lèi)型一種邊列表適配器
鄰接表;鄰接矩陣;邊列表;
其中最通用的是鄰接表;高度參數(shù)化,可以針對(duì)不同情況優(yōu)化
int main(int,char*[])
{
// create a typedef for the Graph type
typedef adjacency_list<vecS, vecS, bidirectionalS> Graph;//使用vector來(lái)存出邊和頂點(diǎn)集的數(shù)據(jù)結(jié)構(gòu),雙向
// Make convenient labels for the vertices
enum { A, B, C, D, E, N };
const int num_vertices = N;
const char* name = "ABCDE";
// writing out the edges in the graph
typedef std::pair<int, int> Edge;
Edge edge_array[] =
{ Edge(A,B), Edge(A,D), Edge(C,A), Edge(D,C),
Edge(C,E), Edge(B,D), Edge(D,E) };
const int num_edges = sizeof(edge_array)/sizeof(edge_array[0]);
// declare a graph object
Graph g(num_vertices);
// add the edges to the graph object
for (int i = 0; i < num_edges; ++i)
add_edge(edge_array[i].first, edge_array[i].second, g);
...
return 0;
}
除了使用add_edge()接口,也可以使用迭代器:
Graph g(edge_array, edge_array + sizeof(edge_array) / sizeof(Edge), num_vertices);`
此外,點(diǎn)數(shù)也是不固定的,可以使用 add_vertex() & remove_vertex()
邊集訪問(wèn):edges()返回的迭代器,source()和target()為被該邊連接的點(diǎn)
int main(int,char*[])
{
// ...
std::cout << "edges(g) = ";
graph_traits<Graph>::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
//tie接口將返回的pair分離為兩個(gè)變量。在本例中為ei和ei_end;
std::cout << "(" << index[source(*ei, g)]
<< "," << index[target(*ei, g)] << ") ";
std::cout << std::endl;
// ...
return 0;
}
鄰接結(jié)構(gòu):
std::for_each(vertices(g).first, vertices(g).second, exercise_vertex<Graph>(g));
使用 exercise_vertex是為了在運(yùn)行遍歷時(shí)保留對(duì)圖像的引用。將其模板化以便在不同的圖類(lèi)型中重用
頂點(diǎn)描述符是由每個(gè)圖形類(lèi)型提供的,可用于通過(guò)以下部分中描述的out_edges(),in_edges(),adjacent_vertices()和屬性映射函數(shù)來(lái)訪問(wèn)有關(guān)圖形的信息。 vertex_descriptor類(lèi)型通過(guò)graph_traits類(lèi)獲得。下面使用的typename關(guān)鍵字是必需的,因?yàn)閟cope :: operator(graph_traits <Graph>類(lèi)型)左側(cè)的類(lèi)型取決于模板參數(shù)(Graph類(lèi)型)。這是我們?nèi)绾味x函子的應(yīng)用方法:
template <class Graph> struct exercise_vertex {
//...
typedef typename graph_traits<Graph>
::vertex_descriptor Vertex;
void operator()(const Vertex& v) const
{
//...
}
//...
};
邊描述:out_edges()函數(shù),兩個(gè)參數(shù):定點(diǎn)和圖對(duì)象。返回pair迭代器,提供該頂點(diǎn)所有出邊的接口,出邊迭代器dereference其中之一給出邊描述符對(duì)象。(類(lèi)似頂點(diǎn)描述符)
template <class Graph> struct exercise_vertex {
//...
void operator()(const Vertex& v) const
{
typedef graph_traits<Graph> GraphTraits;
typename property_map<Graph, vertex_index_t>::type index = get(vertex_index, g);
std::cout << "out-edges: ";
typename GraphTraits::out_edge_iterator out_i, out_end;
typename GraphTraits::edge_descriptor e;
for (boost::tie(out_i, out_end) = out_edges(v, g);
out_i != out_end; ++out_i) {
e = *out_i;
Vertex src = source(e, g), targ = target(e, g);
std::cout << "(" << index[src] << ","
<< index[targ] << ") ";
}
std::cout << std::endl;
//...
}
//...
};
in_edges()與之類(lèi)似,(前提是雙向圖,且僅用于鄰接表結(jié)構(gòu))
有時(shí),算法不需要查看圖形的邊,只關(guān)心頂點(diǎn)。 因此,圖形界面還包括AdjacencyGraph接口的adjacent_vertices()函數(shù),它提供對(duì)相鄰頂點(diǎn)的直接訪問(wèn)。 此函數(shù)返回一對(duì)鄰接迭代器。 dereference鄰接迭代器給出相鄰頂點(diǎn)的頂點(diǎn)描述符。
涂色:加權(quán)。由于一般一個(gè)算法只用到一個(gè)屬性,因此屬性和圖對(duì)象應(yīng)當(dāng)分別存儲(chǔ).屬性有兩類(lèi):內(nèi)部存儲(chǔ)屬性/外部存儲(chǔ)屬性.內(nèi)部屬性通常采用模板插件,外部屬性方法較多.直接存儲(chǔ)的方法:點(diǎn)或者邊映射的映射.對(duì)于vecS(即vector,參見(jiàn)某章),這種映射型容器會(huì)自己分配索引,用vertex_index_t來(lái)查找,而邊不可以(廢話太多了不就加個(gè)索引么)
用訪問(wèn)者擴(kuò)展算法
一般來(lái)講lib中的算法足夠使用,但很有可能不完全相同.比如Dij算法通常記錄最短距離,但是有可能我們需要用到最短路徑樹(shù).一種方法是在最短路徑樹(shù)中記錄每個(gè)節(jié)點(diǎn)的前導(dǎo).
在STL中,這種計(jì)算通過(guò)functor實(shí)現(xiàn).這是每個(gè)算法的可選參數(shù).在BGL中這個(gè)由游客來(lái)實(shí)現(xiàn).一個(gè)游客就是一個(gè)functor,它有多種方法被調(diào)用
(會(huì)在vistor章節(jié)詳細(xì)介紹.)每一個(gè)方法都可以在算法的某一個(gè)點(diǎn)被調(diào)用.BGL提供了一些常用任務(wù)的訪問(wèn)者.可以自己創(chuàng)建BGL到visitor,本處先介紹前綴記錄的實(shí)現(xiàn)和使用.以Dijkstra為例,創(chuàng)建一個(gè)Dijkstra visitor
前綴記錄訪問(wèn)者的功能分為兩部分.為了存儲(chǔ)可訪問(wèn)前綴屬性,我們使用屬性映射.而后,前綴訪問(wèn)者只對(duì)要記錄的父負(fù)責(zé).創(chuàng)建了一個(gè)record_predecessor屬性類(lèi)及模板在前綴屬性映射上,由于這個(gè)訪問(wèn)者只有一種方法,我們繼承dijkstra_visitor,為其余提供空白方法.構(gòu)造函數(shù)將使用屬性映射對(duì)象并將其保存在數(shù)據(jù)成員中.
回顧:m_開(kāi)頭說(shuō)明是類(lèi)成員變量,構(gòu)造函數(shù)的意圖是把p存進(jìn)m_這個(gè)protected里去.
然后又是一個(gè)模板函數(shù)
template <class PredecessorMap>
class record_predecessors : public dijkstra_visitor<>
{
public:
record_predecessors(PredecessorMap p)
: m_predecessor(p) { }
template <class Edge, class Graph>
void edge_relaxed(Edge e, Graph& g) {
// set the parent of the target(e) to source(e)
put(m_predecessor, target(e, g), source(e, g));
}
protected:
PredecessorMap m_predecessor;
};
將source記錄為目標(biāo)頂點(diǎn)的前身,每次relax時(shí)覆蓋之前的值,使用put來(lái)記錄前綴.訪客的edge_filter通知算法什么時(shí)候啟用explore(),該情況下我們只在最短路徑樹(shù)中通知邊所以制定tree_edge_tag.
為了方便的創(chuàng)建前綴訪問(wèn),啟用helper如下
//類(lèi)模板外函數(shù)?總之定義了make_,返回了record_的結(jié)果
template <class PredecessorMap>
record_predecessors<PredecessorMap>
make_predecessor_recorder(PredecessorMap p) {
return record_predecessors<PredecessorMap>(p);
}
現(xiàn)在可以像下文一樣使用:
vector<Vertex> p(num_vertices(G), graph_traits<G>::null_vertex());
//容器(大小,迭代器)
dijkstra_shortest_paths(G, s, distance_map(&d[0]).
visitor(make_predecessor_recorder(&p[0])));
//計(jì)算最短路徑,添加visitor并存儲(chǔ) 參數(shù)是數(shù)組的位置
cout << "parents in the tree of shortest paths:" << endl;
for(vi = vertices(G).first; vi != vertices(G).second; ++vi) {
cout << "parent(" << *vi;
if (p[*vi] == graph_traits<G>::null_vertex())
cout << ") = no parent" << endl;
else
cout << ") = " << p[*vi] << endl;
}
鄰接矩陣:一般用于密集圖表
邊列表:可以使用任意邊迭代和實(shí)現(xiàn)