C ++:Scott Meyers“Effective STL”:第31项:了解您的排序选项:帮助理解

时间:2012-02-09 23:47:55

标签: c++ list sorting stl effective-c++

美好的一天!

在他的"有效STL" Scott Meyers写道

第三种方法是使用迭代器的有序容器中的信息迭代地将列表元素拼接到您希望它们所在的位置。正如您所看到的,有很多选择。(第31项,第二部分)

有人可以这样解释我吗?


更多文字(了解背景):

算法sort,stable_sort,partial_sort和nth_element需要随机访问迭代器,因此它们只能应用于向量,字符串,deques和数组。在标准关联容器中对元素进行排序是没有意义的,因为这样的容器使用它们的比较函数始终保持排序。我们可能希望使用sort,stable_sort,partial_sort或nth_element但唯一的容器是list,并且list通过提供其排序成员函数来进行补偿。 (有趣的是,list :: sort执行稳定的排序。)如果要对列表进行排序,则可以,但如果要对列表中的对象使用partial_sort或nth_element,则必须间接执行。一种间接方法是将元素复制到具有随机访问迭代器的容器中,然后将所需的算法应用于该容器。另一种方法是创建一个list :: iterators的容器,在该容器上使用该算法,然后通过迭代器访问列表元素。 第三种方法是使用迭代器的有序容器中的信息迭代地将列表元素拼接到您希望它们所在的位置。如您所见,那里有很多选择。

5 个答案:

答案 0 :(得分:3)

我不确定混淆是什么,但我怀疑它是什么&#34;拼接&#34;指的是:std::list<T>具有splice()成员函数(实际上是几个重载),它们在列表之间传递节点。也就是说,您创建了std::vector<std::list<T>::const_iterator>并将排序算法(例如std::partial_sort())应用于此。然后创建一个新的std::list<T>并使用splice()成员和已排序向量中的迭代器将节点置于正确的顺序而不移动对象本身。

这看起来像这样:

std::vector<std::list<T>::const_iterator> tmp;
for (auto it(list.begin()), end(list.end()); it != end; ++it) {
    tmp.push_back(it);
}
some_sort_of(tmp);
std::list<T> result;
for (auto it(tmp.begin()), end(tmp.end()); it != end; ++it) {
    result.splice(result.end(), list, it);
}

答案 1 :(得分:2)

假设您想在列表中执行partial_sort。您可以通过提供可以使用迭代器排序的比较函数将迭代器存储到集合中的列表中,如下所示:

struct iterator_less
{   
    bool operator() (std::list<int>::iterator lhs,
                 std::list<int>::iterator rhs) const
    {
        return (*lhs < *rhs);
    }
};
typedef std::multiset<
    std::list<int>::iterator, iterator_less
> iterator_set;

您可以让set执行排序,但由于它包含要列出的迭代器,您可以列出:: splice将它们拼接成partial_sorted列表:

std::list<int> unsorted, partialSorted;
unsorted.push_back(11);
unsorted.push_back(2);
unsorted.push_back(2);
unsorted.push_back(99);
unsorted.push_back(2);
unsorted.push_back(4);
unsorted.push_back(5);
unsorted.push_back(7);
unsorted.push_back(34);

    // First copy the iterators into the set
iterator_set itSet;
for( auto it = unsorted.begin(); it!=unsorted.end();++it)
{
    itSet.insert(it);
}
    // now if you want a partial_sort with the first 3 elements, iterate through the
    // set grabbing the first item in the set and then removing it.
int count = 3;
while(count--)
{
    iterator_set::iterator setTop = itSet.begin();
    partialSorted.splice(
        partialSorted.begin(),
        unsorted,
        *setTop);
    itSet.erase(setTop);
}

partialSorted.splice(
        partialSorted.end(),
        unsorted,
        unsorted.begin(),
        unsorted.end());

答案 2 :(得分:1)

有序容器可以是std::setstd::map。如果您愿意使用带有迭代器的比较器,则可以使用std::set<std::list<mydata>::iterator,comparator>,否则可以使用std::map<mydata,std::list<mydata>::iterator>。您将列表从begin()转到end()并将迭代器插入到集合或映射中;现在,您可以通过迭代集合或映射来使用它以排序顺序访问列表中的项目,因为它会自动排序。

答案 3 :(得分:1)

有序容器为std::setstd::multisetstd::set实现了BST。所以它说的是你应该创建一个std::set<std::list::iterators>,然后使用固有的BST结构进行排序。以下是BST上的链接,可帮助您入门。

答案 4 :(得分:0)

编辑啊。刚刚注意到“有序容器的迭代器”。这意味着要在另一个容器中创建一个索引。

Boost Multi Index有很多这样的例子(其中一个集合由几个不同的排序谓词索引,索引只不过是指针集合(通常是迭代器)到基础容器中。


  

“第三种方法是使用迭代器的有序容器中的信息迭代地将列表的元素拼接到您希望它们位于的位置”

我认为与该描述匹配的一件事是在对std::sort_heap / std::make_heap / push_heap操作的列表/向量进行pop_heap时。

  • make_heap:将序列转换为堆
  • sort_heap:排序堆
  • push_heap:在堆中插入元素
  • pop_heap:从堆中删除顶部元素

堆是序列中元素的组织,这使得(相对)有效地将集合保持在插入/移除下的已知顺序中。顺序是隐式的(就像存储在连续数组中的递归定义二叉树一样),并且可以通过对其执行(高效)sort_heap算法将其转换为相应的正确排序的序列。