基于公共元素组合整数对

combine pairs of integers based on common element

本文关键字:整数 组合 元素 于公共      更新时间:2023-10-16

假设我有一对整数,例如 (1,3)、(5,6)、(7,8)、(3,9) 那么我想根据它们之间的共同元素将此对组合成唯一的集合,即我可以组合 (1,3) 和 (3,9),因为它们之间是共同的,所以上述输入的最终输出应该是这样的 (1,3,9)、(5,6)、(7,8)一种方法可能是遍历这个数组,并根据公共元素将其组合并从数组中删除第二对,我认为这很耗时。在 C/C++ 中执行此操作的有效方法是什么?

一种有效的方法是将其视为图形连接问题。创建一个图形,其中节点是整数对,如果两个节点对应的对具有公共元素,则在两个节点之间添加无向边。现在,在此图中找到连接的组件,并构造由组件中的节点对应的对的并集形成的集合。

查找连接的组件需要O(E)时间,如果有n对,则可能会O(n^2)时间。合并它们都可以用类似heap的结构来完成,每次插入需要O(log n)。因此运行时O(E + n log n)最多是O(n^2)。复杂性可能会低得多,具体取决于有多少对具有共同的元素。

我会使用从intshared_ptr<list<int>>的地图,list超过vector,因为这是一个很好的splice用例:

class Combiner {
    std::unordered_map<int, std::shared_ptr<std::list<int>>> map;
public:
    void addPair(const std::pair<int, int>& p) {
        auto a = map.find(p.first);
        auto b = map.find(p.second);
        // 4 cases: (1) both found the same list, (2) both found different lists,
        //          (3) one found a list, (4) neither found a list
        if (a != map.end()) {
            if (b != map.end()) {
                if (a->second == b->second) {
                    // (1) nothing to do, done
                }
                else {
                    // (2) have to combine our lists
                    a->second->splice(a->second.end(), *(b->second));
                    b->second = a->second;
                }
            }
            else {
                // (3), add p.second to a
                a->second->push_back(p.second);
                map.insert(std::make_pair(p.second, a->second));
            }
        }
        else {
            if (b != map.end()) {
                // (3), add p.first to b
                b->second->push_back(p.first);
                map.insert(std::make_pair(p.first, b->second));
            }
            else {
                // (4), make a new list
                auto new_list = std::make_shared<std::list<int>>();
                new_list->push_back(p.first);
                new_list->push_back(p.second);
                map.insert(std::make_pair(p.first, new_list));
                map.insert(std::make_pair(p.second, new_list));
            }
        }
    }

您希望容器使用哪些类型? 它们是设置的吗?

#include <algorithm>
#include <set>
#include <list>
#include <iostream>
void dump( const std::string & label, const std::list< std::set< int > > & values )
{
    std::cout << label << std::endl;
    for( auto iter : values )
    {
        std::cout << "{ ";
        for( auto val : iter )
            std::cout << val << ", ";
        std::cout << "}, ";
    }
    std::cout << std::endl;
}
void combine( std::list< std::set< int > > & values )
{
    for( std::list< std::set< int > >::iterator iter = values.begin(); iter != values.end(); ++iter )
        for( std::list< std::set< int > >::iterator niter( iter ); ++niter != values.end(); )
            if( std::find_first_of( iter->begin(), iter->end(), niter->begin(), niter->end() ) != iter->end() )
            {
                iter->insert( niter->begin(), niter->end() );
                values.erase( niter );
                niter = iter;
            }
}
int main( int argc, char ** argv )
{
    std::list< std::set< int > > to_process = { { 1, 3 }, { 5, 6 }, { 7, 8 }, { 3, 9 } };
    dump( "Before", to_process );
    combine( to_process );
    dump( "After", to_process );
    to_process = { { 1, 3 }, { 5, 6 }, { 7, 8 }, { 3, 9 }, { 9, 13 }, { 8, 11 } };
    dump( "Before", to_process );
    combine( to_process );
    dump( "After", to_process );
    to_process = { { 1, 2 }, { 5, 6 }, { 7, 8 }, { 3, 9 }, { 9, 13 }, { 8, 13 } };
    dump( "Before", to_process );
    combine( to_process );
    dump( "After", to_process );
    return 0;
}

输出:

Before
{ 1, 3, }, { 5, 6, }, { 7, 8, }, { 3, 9, }, 
After
{ 1, 3, 9, }, { 5, 6, }, { 7, 8, }, 
Before
{ 1, 3, }, { 5, 6, }, { 7, 8, }, { 3, 9, }, { 9, 13, }, { 8, 11, }, 
After
{ 1, 3, 9, 13, }, { 5, 6, }, { 7, 8, 11, }, 
Before
{ 1, 2, }, { 5, 6, }, { 7, 8, }, { 3, 9, }, { 9, 13, }, { 8, 13, }, 
After
{ 1, 2, }, { 5, 6, }, { 3, 7, 8, 9, 13, },