BGL(boost graph Library)(0-7)

BGL官网入口
Boost库说明


BGL的用途是给某些图的结构提供接口,而隐藏其内部细节
不需要built,但是编译程序一定要--optimized
三种方式得到STL:

  • 算法/数据结构
    每种算法都以数据结构无关的方式写,允许在不同类的容器中使用。代码量为O(m+n),m是算法数,n是容器数。
  • 函数对象扩展
    用户可以通过函数对象自行修改STL。
  • 元素类型参数化
    像std::list<T>,
    BGL采用了类似STL的方式
  • 算法/数据结构互通
    首先,将BGL的图形算法写入一个可以抽出特定图形数据结构细节的接口。 像STL一样,BGL使用迭代器来定义数据结构遍历的接口。 有三种不同的图形遍历模式:遍历图形中的所有顶点,遍历所有边缘,并沿着图形的相邻结构(从顶点到其每个邻居)。 每个遍历模式都有独立的迭代器
    该通用接口允许诸如breadth_first_search()之类的模板函数可以处理各种图形数据结构,从使用指针链接节点的图形到以数组编码的图形。 这种灵活性在图形领域尤为重要。 图形数据结构通常是针对特定应用程序定制的。 传统上,如果程序员想要重用算法实现,他们必须将它们的图形数据转换/复制到图形库的规定的图形结构中。 图书馆如LEDA,GTL,Stanford GraphBase; 在Fortran中编写的图形算法尤其如此。 这严重限制了图形算法的重用。
    相比之下,使用外部自适应的BGL通用图形算法可以按照原样使用定制(或甚至遗留的)图形结构(参见“如何将现有图形转换为BGL”)。 外部自适应围绕数据结构包装一个新界面,而不需要复制,而不将数据放在适配器对象内。 BGL界面经过精心设计,使之适应性变得容易。 为了证明这一点,我们在BGL图算法中建立了使用各种图形结构(LEDA图,Stanford GraphBase图,甚至Fortran样式阵列)的接口代码。
  • 通过访客实现扩展
    BGL的图算法是可扩展的。 BGL引入了一个访问者的概念,它只是一个具有多种方法的函数对象。 在图形算法中,通常有几个关键的“事件点”可用于插入用户定义的操作。 访问对象具有在每个事件点调用的不同方法。 特定的事件点和相应的访问者方法取决于具体的算法。 它们通常包括start_vertex(),discover_vertex(),inspect_edge(),tree_edge()和finish_vertex()等方法。
  • 顶点和边缘属性多参数化
    BGL通用的第三种方法类似于STL容器中的元素类型的参数化,但是对于图形来说,故事再复杂一些。 我们需要将值(称为“属性”)与图形的顶点和边缘相关联。 另外,通常需要将多个属性与每个顶点和边缘相关联; 这是我们通过多参数化的意思。 STL std :: list <T>类的元素类型有一个参数T。 类似地,BGL图类具有顶点和边缘“属性”的模板参数。 属性指定属性的参数化类型,并为属性分配标识标签。 该标签用于区分边缘或顶点可能具有的多个属性。 附加到特定顶点或边缘的属性值可以通过属性映射获取。 每个属性都有一个单独的属性映射。
    传统图形库和图形结构在图形属性的参数化方面下降。 这是图表数据结构必须为应用程序定制的主要原因之一。 BGL图类中属性的参数化使它们非常适合重复使用。

算法

核心算法模式和一系列图算法:
核心算法模式:

  • Breadth First Search
  • Depth First Search
  • Uniform Cost Search
    BGL不会用核心算法模式在图上计算数值,只是为图算法构建块。而图算法包括以下:
  • 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

数据结构

提供两种图类型一种边列表适配器
邻接表;邻接矩阵;边列表;
其中最通用的是邻接表;高度参数化,可以针对不同情况优化

int main(int,char*[])
  {
    // create a typedef for the Graph type
    typedef adjacency_list<vecS, vecS, bidirectionalS> Graph;//使用vector来存出边和顶点集的数据结构,双向

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

此外,点数也是不固定的,可以使用 add_vertex() & remove_vertex()
边集访问:edges()返回的迭代器,source()和target()为被该边连接的点

  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分离为两个变量。在本例中为ei和ei_end;
        std::cout << "(" << index[source(*ei, g)] 
                  << "," << index[target(*ei, g)] << ") ";
    std::cout << std::endl;
    // ...
    return 0;
  }

邻接结构:
std::for_each(vertices(g).first, vertices(g).second, exercise_vertex<Graph>(g));
使用 exercise_vertex是为了在运行遍历时保留对图像的引用。将其模板化以便在不同的图类型中重用

顶点描述符是由每个图形类型提供的,可用于通过以下部分中描述的out_edges(),in_edges(),adjacent_vertices()和属性映射函数来访问有关图形的信息。 vertex_descriptor类型通过graph_traits类获得。下面使用的typename关键字是必需的,因为scope :: operator(graph_traits <Graph>类型)左侧的类型取决于模板参数(Graph类型)。这是我们如何定义函子的应用方法:

  template <class Graph> struct exercise_vertex {
    //...
    typedef typename graph_traits<Graph>
      ::vertex_descriptor Vertex;
    void operator()(const Vertex& v) const
    {
      //...
    }
    //...
  };

边描述:out_edges()函数,两个参数:定点和图对象。返回pair迭代器,提供该顶点所有出边的接口,出边迭代器dereference其中之一给出边描述符对象。(类似顶点描述符)

  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()与之类似,(前提是双向图,且仅用于邻接表结构)
有时,算法不需要查看图形的边,只关心顶点。 因此,图形界面还包括AdjacencyGraph接口的adjacent_vertices()函数,它提供对相邻顶点的直接访问。 此函数返回一对邻接迭代器。 dereference邻接迭代器给出相邻顶点的顶点描述符。
涂色:加权。由于一般一个算法只用到一个属性,因此属性和图对象应当分别存储.属性有两类:内部存储属性/外部存储属性.内部属性通常采用模板插件,外部属性方法较多.直接存储的方法:点或者边映射的映射.对于vecS(即vector,参见某章),这种映射型容器会自己分配索引,用vertex_index_t来查找,而边不可以(废话太多了不就加个索引么)
用访问者扩展算法
一般来讲lib中的算法足够使用,但很有可能不完全相同.比如Dij算法通常记录最短距离,但是有可能我们需要用到最短路径树.一种方法是在最短路径树中记录每个节点的前导.
在STL中,这种计算通过functor实现.这是每个算法的可选参数.在BGL中这个由游客来实现.一个游客就是一个functor,它有多种方法被调用
(会在vistor章节详细介绍.)每一个方法都可以在算法的某一个点被调用.BGL提供了一些常用任务的访问者.可以自己创建BGL到visitor,本处先介绍前缀记录的实现和使用.以Dijkstra为例,创建一个Dijkstra visitor
前缀记录访问者的功能分为两部分.为了存储可访问前缀属性,我们使用属性映射.而后,前缀访问者只对要记录的父负责.创建了一个record_predecessor属性类及模板在前缀属性映射上,由于这个访问者只有一种方法,我们继承dijkstra_visitor,为其余提供空白方法.构造函数将使用属性映射对象并将其保存在数据成员中.
回顾:m_开头说明是类成员变量,构造函数的意图是把p存进m_这个protected里去.
然后又是一个模板函数

  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记录为目标顶点的前身,每次relax时覆盖之前的值,使用put来记录前缀.访客的edge_filter通知算法什么时候启用explore(),该情况下我们只在最短路径树中通知边所以制定tree_edge_tag.
为了方便的创建前缀访问,启用helper如下

//类模板外函数?总之定义了make_,返回了record_的结果
  template <class PredecessorMap>
  record_predecessors<PredecessorMap>
  make_predecessor_recorder(PredecessorMap p) {
    return record_predecessors<PredecessorMap>(p);
  }

现在可以像下文一样使用:

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])));
//计算最短路径,添加visitor并存储 参数是数组的位置
  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;
  }

邻接矩阵:一般用于密集图表
边列表:可以使用任意边迭代和实现

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容