【问题标题】:C++ STL "Association Set/Map"C++ STL“关联集/映射”
【发布时间】:2012-02-06 19:15:15
【问题描述】:

我正在寻找一种特定类型的集合/映射的名称,希望有一些现有代码,例如 C++ STL。

呼叫集合A

我想运行以下命令:

A.associate(3,5)
A.associate(3,6)
A.associate(6,8)
A.associate(8,10)
A.associate(4,9)

然后能够提出以下问题,并收到指定的答案:

A.is_associated(3,5)  -> True
A.is_associated(5,10) -> True
A.is_associated(10,3) -> True
A.is_associated(4,10) -> False

你知道这种构造/集合的名称吗?

你知道是否有现成的 C/C++ 实现吗?

【问题讨论】:

  • 为什么A.is_associated(5,10) 是真的?能否请您更详细地解释一下关联逻辑?
  • 序列5->3->6->8->10
  • 似乎没有一个回答者理解这个问题:/
  • 啊,所以这是一个双向关联,可以根据需要尽可能多地跟随链。是的,我称之为无向图。我将删除我的答案。 OP,以后请在解释您的要求时更加小心。
  • @PlatinumAzure,我觉得这个问题很清楚,没有假设提出一个底层结构并且可能会偏向答案。如果您正在为此寻找答案,我深表歉意 - 感谢您的帮助。

标签: c++ algorithm stl map set


【解决方案1】:

一般来说,我认为这个数据结构是一个图:也就是说,在您的情况下使用整数标识的一组节点,以及一组可能是有向节点对的边。根据您的具体需求,有许多表示和图表的方法。 Boost 有许多典型的图数据结构以及对其进行操作的算法集合。

基于 cmets 中的一些说明:is_associated() 操作是无向图中的路径搜索。根据图的需要,可以添加边来表示数据结构,使得is_associated() 是常数时间,代价是插入成本线性时间。

【讨论】:

  • 如果你想要快速和轻量级的构造,这个解决方案很好,但你愿意接受慢速查询。如果您想要快速查询(以较慢的构建速度和更多的空间为代价),您最好使用集合地图。
  • @KornelKisielewicz 您可以执行以下任一操作:如果您想要有很多更改和很少的查询,则每次都需要进行路径搜索。如果您有很多查询和少量更改,您可以根据更改设置图表:有很多方法可以表示针对特定新闻量身定制的图表。实际上,集合的映射与图同构。
  • 想一想,这不是一个图,而是一个不相交集。
  • 是的,Union Find 可能有用。我倾向于将其视为使用的图形算法,例如在全对最短路径中。
【解决方案2】:

编辑:如果你想更进一步,你应该考虑Disjoint-set Union-Find的有效实现。

不幸的是,我看到的唯一理智的方法是在 std::sets 上创建一个 std::map -- 您需要在每次插入时检查这两个元素是否在不同的子集中 -- 如果是,则合并它们。

所以外部集合存储了所有元素并将它们指向它们所属的集合。

现在如果你添加一个新的关联:

  1. 检查 A 和 B 属于哪个集合
  2. 如果两者都不属于任何集合,则创建一个新集合并将两个值映射到该集合(结束)
  3. 如果一个属于一个集合而另一个不属于,则将另一个放入第一个集合并映射到那里(结束)
  4. 如果两者都属于不同的集合,则合并集合并相应地更新元素映射。

现在 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。

【讨论】:

    猜你喜欢
    • 2013-11-26
    • 2016-08-06
    • 2011-04-02
    • 1970-01-01
    • 2011-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多