Boost :: Multi-index用于嵌套列表

如何在列表列表上实现Boost :: Multi-index

我有一个如下的分层树:

typedef std::list<struct obj> objList // the object list
typedef std::list<objList> topLevelList // the list of top-level object lists

struct obj
{
   int Id; // globally unique Id
   std::string objType;
   std::string objAttributes;
   ....
   topLevelList  childObjectlist;
}

在顶层,我有一个struct obj的std :: list 然后,每个顶级obj可以具有任意数量的子对象, 包含在该对象的topLevelList列表中。这可以继续进行,嵌套列表中的一个孩子也有自己的孩子。

某些对象只能是子对象,而其他对象是容器并且可以拥有自己的子对象。容器对象有X个子容器,每个子容器都有自己的子对象列表,这就是为什么我在每个obj结构中都拥有topLevelList而不是简单的objList的原因。

我想使用boost :: Multi-index索引此列表列表,以通过其全局唯一ID随机访问顶级列表或后代列表中的任何对象。

这可以实现吗?我搜索了没有成功的示例。

我认为按对象ID使主搜索索引平坦的唯一方法是使上面的列表成为指向对象的指针的列表,然后遍历完整的层次结构列表,然后将指针登录到主搜索索引中每个对象都物理分配在内存中。然后可以通过主搜索索引定位任何对象。

使用Boost :: Multi-index,我仍然必须遍历层次结构,尽管希望能够在遇到的每个列表中使用随机而不是顺序访问来找到所需的对象。

使用嵌套向量而不是列表是一个问题-由于向量中会发生添加和删除,因此会降低性能,并且随着向量的重新分配,指向对象的指针可能会失效。

除非有人有更好的解决方案可以利用Boost :: Multi-index,否则我几乎要说服自己实现扁平化的objId主指针搜索索引。

konjakys 回答:Boost :: Multi-index用于嵌套列表

在有帮助的情况下,您可以使用Boost.MultiIndex使用路径排序的概念来实现一种分层容器。

假设我们具有以下由对象ID标识的对象层次结构:

|-------
|      |
0      4
|----  |----
| | |  | | |
1 2 3  5 8 9
       |--
       | |
       6 7

我们将每个对象的路径定义为从根到对象的ID序列:

0 --> 0
1 --> 0,1
2 --> 0,2
3 --> 0,3
4 --> 4
5 --> 4,5
6 --> 4,5,6
7 --> 4,7
8 --> 4,8
9 --> 4,9

这些路径可以按字典顺序排序,因此按路径排序的对象序列实际上是底层层次结构的表示。如果我们添加指向对象的parent指针以建模父子关系:

struct obj
{
   int        id;
   const obj* parent=nullptr;
};

然后,我们可以定义一个multi_index_container,同时通过ID和基于层次结构的索引对O(1)进行访问:

using nested_container=multi_index_container<
  obj,indexed_by<
    hashed_unique<member<obj,int,&obj::id>>,ordered_unique<identity<obj>,obj_less>
  >
>;

其中obj_less根据路径顺序比较对象。如下例所示,所有类型的树操作和访问都是可能的(代码并非完全无关紧要,请随时询问)。

Live On Coliru

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <iterator>

struct obj
{
   int        id;
   const obj* parent=nullptr;
};

struct subtree_obj
{
  const obj& obj_;
};

struct path
{
  int         id;
  const path* next=nullptr;
};

struct subtree_path
{
  const path& path_;
};

inline bool operator<(const path& x,const path& y)
{
       if(x.id<y.id)return true;
  else if(y.id<x.id)return false;
  else if(!x.next)  return y.next;
  else if(!y.next)  return false;
  else              return *(x.next)<*(y.next);
}

inline bool operator<(const subtree_path& sx,const path& y)
{
  const path& x=sx.path_;

       if(x.id<y.id)return true;
  else if(y.id<x.id)return false;
  else if(!x.next)  return false;
  else if(!y.next)  return false;
  else              return subtree_path{*(x.next)}<*(y.next);
}

inline bool operator<(const path& x,const subtree_path& sy)
{
  return x<sy.path_;
}

struct obj_less
{
private:
  template<typename F>
  static auto apply_to_path(const obj& x,F f)
  {
    return apply_to_path(x.parent,path{x.id},f); 
  }

  template<typename F>
  static auto apply_to_path(const obj* px,const path& x,F f)
    ->decltype(f(x))
  { 
    return !px?f(x):apply_to_path(px->parent,{px->id,&x},f);
  }

public:
  bool operator()(const obj& x,const obj& y)const
  {
    return apply_to_path(x,[&](const path& x){
      return apply_to_path(y,[&](const path& y){
        return x<y;
      });
    });
  }

  bool operator()(const subtree_obj& x,const obj& y)const
  {
    return apply_to_path(x.obj_,[&](const path& y){
        return subtree_path{x}<y;
      });
    });
  }

  bool operator()(const obj& x,const subtree_obj& y)const
  {
    return apply_to_path(x,[&](const path& x){
      return apply_to_path(y.obj_,[&](const path& y){
        return x<subtree_path{y};
      });
    });
  }
};

using namespace boost::multi_index;
using nested_container=multi_index_container<
  obj,obj_less>
  >
>;

template<typename Iterator>
inline auto insert_under(nested_container& c,Iterator it,obj x)
{
  x.parent=&*it;
  return c.insert(std::move(x));
}

template<typename Iterator,typename F>
void for_each_in_level(
  nested_container& c,Iterator first,Iterator last,F f)
{
  if(first==last)return;

  const obj* parent=first->parent;
  auto       first_=c.project<1>(first),last_=c.project<1>(last);

  do{
    f(*first_);
    auto next=std::next(first_);
    if(next->parent!=parent){
      next=c.get<1>().upper_bound(subtree_obj{*first_});
    }
    first_=next;
  }while(first_!=last_);
}

template<typename ObjPointer,typename F>
void for_each_child(nested_container& c,ObjPointer p,F f)
{
  auto [first,last]=c.get<1>().equal_range(subtree_obj{*p});
  for_each_in_level(c,std::next(first),last,f);
}

#include <iostream>

auto print=[](const obj& x){std::cout<<x.id<<" ";};

void print_subtree(nested_container& c,const obj& x)
{
  std::cout<<x.id<<" ";
  bool visited=false;
  for_each_child(c,&x,[&](const obj& x){
    if(!visited){
      std::cout<<"[ ";
      visited=true;
    }
    print_subtree(c,x);
  });
  if(visited)std::cout<<"] ";
}

int main()
{
  nested_container c;
  auto it=c.insert({0}).first;
    insert_under(c,it,{1});
    insert_under(c,{2});
    insert_under(c,{3});
  it=c.insert({4}).first;
    auto it2=insert_under(c,{5}).first;
      insert_under(c,it2,{6});
      insert_under(c,{7});
    insert_under(c,{8});
    insert_under(c,{9});

  std::cout<<"preorder:\t";
  std::for_each(c.begin(),c.end(),print);  
  std::cout<<"\n"; 

  std::cout<<"top level:\t";
  for_each_in_level(c,c.begin(),print);
  std::cout<<"\n"; 

  std::cout<<"children of 0:\t";
  for_each_child(c,c.find(0),print);
  std::cout<<"\n";

  std::cout<<"children of 4:\t";
  for_each_child(c,c.find(4),print);
  std::cout<<"\n";

  std::cout<<"children of 5:\t";
  for_each_child(c,c.find(5),print);
  std::cout<<"\n"; 

  std::cout<<"bracketed:\t";
  for_each_in_level(c,[&](const obj& x){
    print_subtree(c,x);
  });
  std::cout<<"\n"; 
}

输出

preorder:       0 1 2 3 4 5 6 7 8 9 
top level:      0 4 
children of 0:  1 2 3 
children of 4:  5 8 9 
children of 5:  6 7 
bracketed:      0 [ 1 2 3 ] 4 [ 5 [ 6 7 ] 8 9 ] 
本文链接:https://www.f2er.com/3164575.html

大家都在问