如何计算N个排序集的交集?

问题描述

以下示例显示了如何计算两个集合的交集。 STL是否提供了不仅可以对2个集,而且对N个集可以做到这一点的工具?

#include <iostream>    
#include <algorithm>
#include <vector>

int main()
{
    std::vector<int> v1 = { 1,2,9,3,4,5 };
    std::vector<int> v2 = { 9,7,1 };
    std::vector<int> v(v1.size() + v2.size());
    std::vector<int>::iterator it;

    std::sort(v1.begin(),v1.end());
    std::sort(v2.begin(),v2.end());
    it = std::set_intersection(v1.begin(),v1.end(),v2.begin(),v2.end(),v.begin());

    v.resize(it - v.begin());
    std::cout << "Both sets have " << (v.size()) << " elements in common:\n";
    for (it = v.begin(); it != v.end(); ++it)
    {
        std::cout << *it << ' ';
    }
    std::cout << '\n';

    return 0;
}

解决方法

STL是否提供工具,不仅允许2这样做,还允许 N套?

不。但是,您可以通过提供如下的recursive Variadic template来轻松制作一个。

if constexpr部分需要支持。但是,有很多示例how you could do it for prior to c++17。此外,由于递归调用,必须以相反的顺序传递参数,以获取您尝试的行为。

See Online Demo

#include <vector>
#include <algorithm>  // std::set_intersection
#include <iterator>   // std::back_inserter

template<typename Container,typename... Rest>
Container NSetIntersections(
    const Container& container1,const Container& container2,Rest&&... rest) noexcept
{
    if constexpr (sizeof...(Rest) == 0)
    {
        Container result;
        std::set_intersection(container1.begin(),container1.end(),container2.begin(),container2.end(),std::back_inserter(result));
        return result;
    }
    else
    {
        Container result;
        std::set_intersection(container1.begin(),std::back_inserter(result));
        return NSetIntersections(result,std::forward<Rest>(rest)...);
    }
}

int main()
{
    // sorted vectors
    std::vector<int> v1 = { 1,2,3,4,5,6 };
    std::vector<int> v2 = { 2,7,8,9 };
    std::vector<int> v3 = { 3,200 };
    std::vector<int> v4 = { 4,100,200,300 };
    std::vector<int> v5 = { 4,200 };
    // call the function like
    const auto res1 = NSetIntersections(v2,v1);              // 2 3 4
    const auto res2 = NSetIntersections(v3,v2,v1);          // 3 4
    const auto res3 = NSetIntersections(v4,v3,v1);      // 4
    const auto res4 = NSetIntersections(v5,v4,v1);  // 4
    return 0;
}

为了自然地将参数传递给NSetIntersections函数,我建议采用以下辅助函数的方式。另外,它还将处理将单个参数传递给NSetIntersections兼容的情况(以防万一!)。

See Online Demo

#include <vector>
#include <algorithm>  // std::set_intersection
#include <iterator>   // std::back_inserter

namespace helper { // helper NSetIntersections functions
    template<typename Container>
    Container NSetIntersections(const Container& container1) noexcept {
        return container1;
    }

    template<typename Container>
    Container NSetIntersections(const Container& container1,const Container& container2) noexcept
    {
        Container result;
        std::set_intersection(container1.begin(),std::back_inserter(result));
        return result;
    }

    template<typename Container,typename... Rest>
    Container NSetIntersections(
        const Container& container1,Rest&&... rest) noexcept
    {
        return helper::NSetIntersections(
            helper::NSetIntersections(container1,container2),std::forward<Rest>(rest)...);
    }
}

template<typename... Containers>
auto NSetIntersections(Containers&&... rest) noexcept
  -> decltype(helper::NSetIntersections(std::forward<Containers>(rest)...))
{
    return helper::NSetIntersections(std::forward<Containers>(rest)...);
}

现在,您可以像这样用args调用函数:

// sorted vectors
std::vector<int> v1 = { 1,6 };
std::vector<int> v2 = { 2,9 };
std::vector<int> v3 = { 3,200 };
std::vector<int> v4 = { 4,300 };
std::vector<int> v5 = { 4,200 };
// call the function like
const auto res1 = NSetIntersections(v1);                 // 1 2 3 4 5 6 
const auto res2 = NSetIntersections(v1,v2);             // 2 3 4 
const auto res3 = NSetIntersections(v1,v3);         // 3 4 
const auto res4 = NSetIntersections(v1,v4);     // 4
const auto res5 = NSetIntersections(v1,v5); // 4

旁注:当我们完成N次std::set_intersection时,在quick-bench.com中完成的基准测试显示(几乎)相同的性能(对于5个排序的容器)。

enter image description here

See Online Quick-bench

,

您可以将所有要与之相交的向量放在另一个向量中,然后创建一个函数,将它们全部循环遍历并计算v1v2的交点并将它们的交点与{ {1}},然后将其交集与v3等...

这是一个为您完成的功能。

v4

注意:我只做了一些非常基本的测试,但是应该可以。

这就是你的称呼方式

using V = std::vector<std::vector<int>>;

std::vector<int> intersections(V vectors)
{
   int largest = vectors[0].size();
   for (int i = 0; i < vectors.size(); i++)
   {
      std::sort(vectors[i].begin(),vectors[i].end());
      if (vectors[i].size() > largest)
         largest = vectors[i].size();
   }

   std::vector<int> res(largest);
   std::vector<int>::iterator it;
   for (int i = 0; i < vectors.size() - 1; i++)
   {
      it = std::set_intersection(vectors[i].begin(),vectors[i].end(),vectors[i + 1].begin(),vectors[i + 1].end(),res.begin()
      );
      res.resize(it - res.begin());
      vectors[i + 1].resize(res.size());
      std::copy(res.begin(),res.end(),vectors[i + 1].begin());
   }

   return res;
}

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...