如何在C++中将排序的向量合并为单个向量

How to merge sorted vectors into a single vector in C++

本文关键字:向量 合并 单个 排序 C++      更新时间:2023-10-16

>我有 10,000 个vector<pair<unsigned,unsigned>>,我想将它们合并到一个向量中,以便按字典顺序排序并且不包含重复项。为此,我编写了以下代码。但是,令我惊讶的是,下面的代码花费了很多时间。有人可以建议如何减少代码的运行时间吗?

using obj = pair<unsigned, unsigned>
vector< vector<obj> > vecOfVec;  // 10,000 vector<obj>, each sorted with size()=10M
vector<obj> result;
for(auto it=vecOfVec.begin(), l=vecOfVec.end(); it!=l; ++it)
{
  // append vectors
  result.insert(result.end(),it->begin(),it->end());   
  // sort result  
  std::sort(result.begin(), result.end());
  // remove duplicates from result   
  result.erase(std::unique(result.begin(), result.end()), result.end());      
}

我认为你应该使用vectOfVect中的向量被排序的事实。

因此,在单个向量上检测前面的最小值,将其push_back()result中,并删除从与最小值匹配的向量前面检测到的所有值(避免result重复)。

如果你可以删除vecOfVec变量,比如(注意:代码未经测试:只是为了给出一个想法)

while ( vecOfVec.size() )
 {
   // detect the minimal front value
   auto itc    = vecOfVec.cbegin();
   auto lc     = vecOfVec.cend();
   auto valMin = itc->front();
   while ( ++itc != lc )
      valMin = std::min(valMin, itc->front());
   // push_back() the minimal front value in result
   result.push_back(valMin);
   for ( auto it = vecOfVec.begin() ; it != vecOfVec.end() ; )
    {
      // remove all the front values equals to valMin (this remove the 
      // duplicates from result)
      while ( (false == it->empty()) && (valMin == it->front()) )
         it->erase(it->begin());
      // when a vector is empty is removed
      it = ( it->empty() ? vecOfVec.erase(it) : ++it );
    }
 }

如果可以的话,我建议你把vecOfVecvector< vector<obj> >切换到允许从单个容器(堆栈?)前面有效移除和有效移除单个容器(列表?)的东西。

如果有很多

重复项,你应该使用set而不是vector作为结果,因为 set 是存储没有重复项的东西最自然的事情:

set< pair<unsigned,unsigned> > resultSet;
for (auto it=vecOfVec.begin(); it!=vecOfVec.end(); ++it)
    resultSet.insert(it->begin(), it->end());

如果需要将其转换为向量,可以编写

vector< pair<unsigned,unsigned> > resultVec(resultSet.begin(), resultSet.end());

请注意,由于您的代码运行超过 8000 亿个元素,因此无论如何,它仍然需要大量时间。 至少几个小时,如果不是几天的话。

其他想法是:

  • 递归合并向量 (10000 -> 5000 -> 2500 -> ... -> 1)
  • 要合并 10000 个
  • 向量,请将 10000 个迭代器存储在堆结构中

代码的一个问题是过度使用std::sort。不幸的是,快速排序算法(通常是 std::sort 使用的工作马)在遇到已经排序的数组时并不是特别快。

此外,您没有利用初始向量已经排序的事实。这可以通过使用其下一个值的堆来利用,此时您不需要再次调用sort。这可以编码如下(使用 obj = int 测试的代码),但也许可以更简洁。

// represents the next unused entry in one vector<obj>
template<typename obj>
struct feed
{
  typename std::vector<obj>::const_iterator current, end;
  feed(std::vector<obj> const&v)
    : current(v.begin()), end(v.end()) {}
  friend bool operator> (feed const&l, feed const&r)
  { return *(l.current) >  *(r.current); }
};
// - returns the smallest element
// - set corresponding feeder to next and re-establish the heap
template<typename obj>
obj get_next(std::vector<feed<obj>>&heap)
{
  auto&f = heap[0];
  auto x = *(f.current++);
  if(f.current == f.end) {
    std::pop_heap(heap.begin(),heap.end(),std::greater<feed<obj>>{});
    heap.pop_back();
  } else
    std::make_heap(heap.begin(),heap.end(),std::greater<feed<obj>>{});
  return x;
}
template<typename obj>
std::vector<obj> merge(std::vector<std::vector<obj>>const&vecOfvec)
{
  // create min heap of feed<obj> and count total number of objects
  std::vector<feed<obj>> input;
  input.reserve(vecOfvec.size());
  size_t num_total = 0;
  for(auto const&v:vecOfvec)
    if(v.size()) {
      num_total += v.size();
      input.emplace_back(v);
    }
  std::make_heap(input.begin(),input.end(),std::greater<feed<obj>>{});
  // append values in ascending order, avoiding duplicates
  std::vector<obj> result;
  result.reserve(num_total);
  while(!input.empty()) {
    auto x = get_next(input);
    result.push_back(x);
    while(!input.empty() &&
          !(*(input[0].current) > x))  // remove duplicates
      get_next(input);
  }
  return result;
}