编辑:如果你想更进一步,你应该考虑Disjoint-set Union-Find的有效实现。
不幸的是,我看到的唯一理智的方法是在 std::sets 上创建一个 std::map -- 您需要在每次插入时检查这两个元素是否在不同的子集中 -- 如果是,则合并它们。
所以外部集合存储了所有元素并将它们指向它们所属的集合。
现在如果你添加一个新的关联:
- 检查 A 和 B 属于哪个集合
- 如果两者都不属于任何集合,则创建一个新集合并将两个值映射到该集合(结束)
- 如果一个属于一个集合而另一个不属于,则将另一个放入第一个集合并映射到那里(结束)
- 如果两者都属于不同的集合,则合并集合并相应地更新元素映射。
现在 is_related 函数真的很快——只要检查两个元素是否属于同一个集合。
英文:
typedef std::map< int, std::set< int >& > assoc_set; // note that you need to
// store the sets somewhere else, maybe in another set
可能是这样的:
class assoc_set
{
typedef std::set< int > int_set;
typedef std::set< int_set > int_set_set;
typedef std::map< int, int_set_set::iterator > set_map;
public:
void associate( int a, int b )
{
set_set_map::iterator ia = iss.find( a );
set_set_map::iterator ib = iss.find( b );
if ( ia == set_set_map::end() )
{
if ( ib == set_set_map::end() )
{
// create new
int_set ab;
ab.insert(a);
ab.insert(b);
int_set_set::iterator r = iss.insert( ab ).second;
smap[a] = r;
smap[b] = r;
}
else
{
// add to a
smap[a] = ib;
ib->insert( a );
}
}
else
{
if ( ib == set_set_map::end() )
{
// add to b
smap[b] = ia;
ia->insert( b );
}
else
{
// merge
ia->insert( ib->begin(), ib->end() );
// this could be done better
for ( int_set::iterator i = it->begin(); i != it->end(); ++i )
{
smap[*i] = ia;
}
}
}
}
bool is_associated( int a, int b )
{
set_set_map::iterator ia = iss.find( a );
set_set_map::iterator ib = iss.find( b );
return ia != set_set_map::end() && ia = ib;
}
private:
int_set_set iss;
set_map smap;
}
会有错误和错误,我只有记事本可用(反正在这上面花了太多时间)。
添加是O(n)(但可以通过使用额外的间接层(并摆脱愚蠢的地图循环)减少到O(log(n))。查询是O(log(n))。
使用unordered_set/unordered_map,您可以将两者都减少到O(1) amortized。