多个排序数组的交集

时间:2014-08-26 12:29:25

标签: c++ arrays algorithm sorting

this开始,我们知道解决两个排序数组交集的方法。那么如何获得多个排序数组的交集?

根据两个排序数组的答案,我们可以将它应用于多个数组。这是代码

vector<int> intersectionVector(vector<vector<int> > vectors){
    int vec_num = vectors.size();

    vector<int> vec_pos(vec_num);// hold the current position for every vector
    vector<int> inter_vec; // collection of intersection elements

    while (true){
        int max_val = INT_MIN;
        for (int index = 0; index < vec_num; ++index){
            // reach the end of one array, return the intersection collection
            if (vec_pos[index] == vectors[index].size()){
                return inter_vec;
            }

            max_val = max(max_val, vectors[index].at(vec_pos[index]));
        }

        bool bsame = true;
        for (int index = 0; index < vec_num; ++index){
            while (vectors[index].at(vec_pos[index]) < max_val){
                vec_pos[index]++; // advance the position of vector, once less than max value
                bsame = false;
            }
        }

        // find same element in all vectors
        if (bsame){
            inter_vec.push_back(vectors[0].at(vec_pos[0]));

            // advance the position of all vectors
            for (int index = 0; index < vec_num; ++index){
                vec_pos[index]++;
            }
        }
    }
}

有更好的方法可以解决吗?

UPDATE1

从这两个主题12开始,似乎Hash set是更有效的方法。

UPDATE2

要提高效果,可以在上面的代码中使用min-heap代替vec_pos。变量max_val保存所有向量的当前最大值。所以只需将根值与max_val进行比较,如果它们相同,则可以将此元素放入交集列表中。

2 个答案:

答案 0 :(得分:3)

要获得两个已排序范围的交集,可以使用std::set_intersection

std::vector<int> intersection (const std::vector<std::vector<int>> &vecs) {

    auto last_intersection = vecs[0];
    std::vector<int> curr_intersection;

    for (std::size_t i = 1; i < vecs.size(); ++i) {
        std::set_intersection(last_intersection.begin(), last_intersection.end(),
            vecs[i].begin(), vecs[i].end(),
            std::back_inserter(curr_intersection));
        std::swap(last_intersection, curr_intersection);
        curr_intersection.clear();
    }
    return last_intersection;
}

这看起来比您的解决方案更清晰,因为它太混乱而无法检查是否正确。 它也具有最佳的复杂性。

标准库算法set_intersection可以以任何使用

的方式实现
  

最多2·(N1 + N2-1)比较,其中N1 = std :: distance(first1,last1)和N2 = std :: distance(first2,last2)。

first1等是定义输入范围的迭代器。如果它是开源的(如libstd ++或libc ++),你可以查看标准库源代码中的实际实现。

答案 1 :(得分:1)

这假设您知道相交的容器数量:

template<class Output, class... Cs>
Output intersect( Output out, Cs const&... cs ) {
  using std::begin; using std::end;
  auto its = std::make_tuple( begin(cs)... );
  const auto ends = std::make_tuple( end(cs)... );
  while( !at_end( its, ends ) ) {
    if ( all_same( its ) ) {
      *out++ = *std::get<0>(its);
      advance_all( its );
    } else {
      advance_least( its );
    }
  }
  return out;
}

完成简单实施:

bool at_end( std::tuple<Iterators...> const& its, std::tuple<Iterators...> const& ends );
bool all_same( std::tuple<Iterators...> const& its );
void advance_all( std::tuple<Iterators...>& its );
void advance_least( std::tuple<Iterators...>& its );

第一个很容易(使用索引技巧,成对比较,检查如果元组为空则返回true)。

第二个是类似的。如果你比较std::get<i>(its) == std::get<i+1>(its)我认为应该更容易,而不是将所有比较为零。可能需要一个空的特殊情况。

advance_all更容易。

最后一个是棘手的。要求是你至少推进一个迭代器,并且你没有推进最多解引用的迭代器,并且你最多推进一次迭代器,并且你可以提高效率。

我认为最简单的方法是找到最大的元素,将所有内容推进到1以下。

如果您不知道相交的容器数量,可以重构上述内容以使用动态存储进行迭代。这看起来与您自己的解决方案类似,除了将细节分解为子函数。