std :: sort没有复制结构

时间:2013-01-06 01:55:30

标签: c++ sorting standard-library

假设我有一个对象向量,其中:

  • 复制构造和作业很昂贵
  • 默认构造和交换两个对象很便宜。

这似乎是引用大数据的对象的标准 - 例如矢量矢量。

问题:有没有办法使用std::sort或标准库中的其他一些排序例程对此向量进行排序,这样就不会发生复制,而是使用交换?我正在寻找一个前c++0x解决方案(无移动语义)。

重载std::swap似乎是第一次自然的尝试,它确实有点帮助,但它只消除了一小部分复制。

注意:gcc行为示例

要对100 81 64 49 36 25 16 9 4 1 0 1 4 9 16 25 36 49 64 81进行排序,我的gcc std :: sort调用19个拷贝构造函数,92个赋值和6个交换。

2 个答案:

答案 0 :(得分:2)

// C++03 solution won't work with arrays and some other custom containers.
// Mostly drop this block:
#include <type_traits>
#include <vector>
#include <algorithm>
#include <iostream>
namespace aux {
  using std::begin; using std::end;
  template<typename C> auto adl_begin( C&& c )->decltype( begin(c) );
  template<typename C> auto adl_end( C&& c )->decltype( end(c) );

  template<typename C>
  struct container_traits:
    std::iterator_traits< typename std::decay< decltype( aux::adl_begin( *(C*)nullptr ) ) >::type >
  {
    typedef typename std::decay< decltype( adl_begin( *(C*)nullptr ) ) >::type iterator_type;
  };
}

// C++03 solution won't work with arrays.  Inside std::less, use Container::value_type:
template<
  typename Container,
  typename Comparison = std::less<
    typename aux::container_traits<Container>::value_type
  >
>
void indirect_sort_then_swap( Container& c, Comparison&& comp = Comparison() ) {
  typedef aux::container_traits<Container> con_traits;
  typedef typename con_traits::value_type value_type;
  typedef typename con_traits::iterator_type iterator_type;
  std::vector< iterator_type > indirect;
  {
    // C++03 solution can use c.begin(), but will not work with arrays:
    using std::begin; using std::end;
    auto begin_ = begin(c);
    auto end_ = end(c);
    for( auto it = begin_; it != end_; ++it ) {
      indirect.push_back( it );
    }
  }
  // In C++03, write a functor class that does this:
  auto indirect_sort = [&comp]( iterator_type const& left, iterator_type const& right )->bool {
    return comp(*left, *right);
  };
  std::sort( indirect.begin(), indirect.end(), indirect_sort );
  // at this point, indirect is a vector with the contents of c sorted by iterator:
  // a hard part remains, namely to take this information and sort c with minimal swaps
  // That is hard.  I will instead create an easy approach, namely create an empty
  // copy of c full of empty elements, and directly swap the correct entry of c into
  // each slot, then I swap c with its copy.
  // the downside is that my container now needs to support push_back.  Oh well.
  Container c2;
  // C++03 solution cannot use auto here.  But we know the type of indirect:
  for (auto it = indirect.begin(); it != indirect.end(); ++it) {
    // See previous comment
    auto itv = *it;
    c2.push_back( value_type() );
    using std::swap;
    swap( *itv, c2.back() );
  }
  // by this point, the contents of c have been swap-moved to c2
  // swap them back:
  {
    using std::swap;
    swap( c, c2 );
  }
}

int main() {
   std::vector<int> foo;
   foo.push_back(7);
   foo.push_back(3);
   indirect_sort_then_swap(foo);
   for (auto i:foo) {
      std::cout << i << "\n";
   }
}
像上面这样的东西是一种可行的方法。我在C ++ 11中写了很多内容,但是包含了关于如何去除额外的C ++ 11内容的注释(在某些情况下它实际上简化了代码,但是删除了处理类似容器的东西的能力)。

基本思路是将vector iterator s排序到原始容器中。然后我们创建一个临时容器,其中包含琐碎的value_typeswap那些具有原始容器中正确数据的琐碎value_type(由vector确定排序iterator s),然后swap我们原始容器的临时容器。

有很多分配,但希望是廉价的东西。

为了使其正常工作,您要排序的数据需要是可以轻松构建的。为了提高效率,您在简单构建时使用的数据需要很便宜,swap需要高效。

我试图尽可能地将其作为ADL友好,因为我发现这是一种很好的做法。

答案 1 :(得分:1)

Heap-sort是一种仅限交换的排序,它不稳定(排序期间等效元素的顺序可能会发生变化)。我回答了一个other similar question,我自己实现了堆排序(PasteBin),但你可能会发现更好,更灵活的实现。

结论是g ++的std::sort使用了35个副本,19个赋值,10个交换和35个删除(总共99个操作)20个元素,我的堆排序使用了62个交换而没有别的。

我刚刚碰到一个只使用交换here on stackoverflow的稳定排序。我没有深入研究它。