按顺序迭代,有序推送和删除的排序数据结构(仅从顶部开始的N个元素)

时间:2013-01-28 16:08:51

标签: c++ algorithm optimization data-structures stl

什么被认为是按顺序推送内容的最佳数据结构(所以在任何位置插入,能够找到正确的位置),按顺序迭代,并从顶部弹出N个元素(因此N个最小元素,N确定通过与阈值比较)?推送和弹出需要特别快(在循环的每次迭代中运行),而数据的有序完全迭代以可变速率发生,但可能不太经常地减少一个数量级。完整迭代无法清除数据,需要保持不变。推送的所有内容最终都会被弹出,但由于pop可以删除多个元素,因此弹出比弹出更多。任何时候结构中的数据规模可能达到数百或数千个元素。

我目前正在使用std::deque和二分搜索以按升序插入元素。分析显示它占据了大部分时间,因此必须改变一些东西。 std::priority_queue不允许迭代,我看到这样做的黑客不会按顺序迭代。即使是在有限的测试中(没有完整的迭代!),std::set类的表现也比我的std::deque方法更差。

我正在搞乱的类似乎都没有考虑到这个用例。如果在STL中找不到数据结构或者出于某种原因需要提升数据结构,我不反对自己创建类。

编辑:

现在有两个主要功能,pushprunepush使用65%的时间,prune使用32%。 push中使用的大部分时间都是由于插入deque(65%中的64%)。只有1%来自二元搜索以找到位置。

template<typename T, size_t Axes>
void Splitter<T, Axes>::SortedData::push(const Data& data) //65% of processing
{
 size_t index = find(data.values[(axis * 2) + 1]);

 this->data.insert(this->data.begin() + index, data); //64% of all processing happens here
}

template<typename T, size_t Axes>
void Splitter<T, Axes>::SortedData::prune(T value) //32% of processing
{
 auto top = data.begin(), end = data.end(), it = top;

 for (; it != end; ++it)
 {
  Data& data = *it;

  if (data.values[(axis * 2) + 1] > value) break;
 }

 data.erase(top, it);
}

template<typename T, size_t Axes>
size_t Splitter<T, Axes>::SortedData::find(T value)
{
 size_t start = 0;
 size_t end = this->data.size();

 if (!end) return 0;

 size_t diff;

 while (diff = (end - start) >> 1)
 {
  size_t mid = diff + start;

  if (this->data[mid].values[(axis * 2) + 1] <= value)
  {
   start = mid;
  }
  else
  {
   end = mid;
  }
 }

 return this->data[start].values[(axis * 2) + 1] <= value ? end : start;
}

5 个答案:

答案 0 :(得分:2)

根据您的要求,根据您的需求量身定制的混合数据结构可能表现最佳。正如其他人所说,连续内存非常重要,但我不建议始终对数组进行排序。我建议你使用3个缓冲区(1 std::array和2 std::vector s):

  • 1(常量)缓冲区用于&#34;插入堆&#34;。需要适应缓存。
  • 2(可变大小)缓冲区(A + B)用于维护和更新已排序的数组。

推送元素时,可以通过std::push_heap将其添加到插入堆中。由于插入堆的大小是常量,因此可能会溢出。当发生这种情况时,您std::sort 向后std::merge将已排序的序列缓冲区(A)放入第三个(B),并根据需要调整它们的大小。这将是新的已排序缓冲区,旧的缓冲区可以被丢弃,即您将A和B交换为下一个批量操作。当您需要排序的迭代序列时,您也会这样做。删除元素时,将堆中的顶部元素与排序序列中的最后一个元素进行比较并删除它(这就是为什么要对其进行排序,以便pop_back代替pop_front

作为参考,这个想法基于sequence heaps

答案 1 :(得分:0)

您是否尝试过使用std :: vector?听起来很奇怪它可能实际上非常快,因为它使用连续的内存。如果我没记错的话Bjarne Stroustrup在2012年的Going Native(http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Keynote-Bjarne-Stroustrup-Cpp11-Style中谈到了这个问题,但我并不是100%肯定这是在这个视频中。)

答案 2 :(得分:0)

您可以使用二进制搜索节省时间,但在双端队列的随机位置插入速度很慢。我会建议使用std :: map。

答案 3 :(得分:0)

你不能比o(log n)排序的插入时间做得更好,一个set和一个priority_queue(无论如何都是deque)都可以。您可以查看STL的地图,但我认为您不会看到任何改进。

对我来说,你所说的是优化的STL插入操作对你来说不够快。如果是这种情况,我建议您查看比较函数并可能对其进行优化,或者考虑编写自己的执行相同操作的数据结构,但可能会利用有关容器中对象的假设,然后在不考虑任何异常处理的情况下,首先优化它。

答案 4 :(得分:0)

从您的编辑中,听起来就像延迟是在复制 - 这是一个复杂的对象吗?你可以在结构中堆积和存储指针,这样每个条目只创建一次;你需要提供一个带指针的自定义比较器,因为不会调用对象操作符&lt;()。 (自定义比较器可以简单地调用operator&lt;())

编辑: 你自己的数字显示它是插入花费时间,而不是'排序'。虽然一些插入时间是创建对象的副本,但有些(可能是大多数)是创建将保存对象的内部结构 - 我认为这不会在list / map / set / queue等之间发生变化。如果您可以预测数据集的可能最终/最大大小,并且可以编写或找到您自己的排序算法,并且在分配对象时丢失了时间,那么矢量可能是最佳选择。