简体   繁体   English

选择排序与数组和链接列表之间的区别?

[英]Difference between selection sort with array and linked list?

Im wondering what is the logic behind selection sort using array with findmax, and linked list with findmin. 我想知道使用带findmax的数组和带findmin的链表进行选择排序的逻辑是什么。 Best and worst cases for both? 两者的最佳和最差情况?

TL;DR TL; DR

Selection sort is generically bad. 选择排序通常是不好的。 Merge sort is generically good, but can be improved by std::sort for random access containers and member functions sort() for node based containers. 合并排序通常很好,但是可以通过std::sort来改进随机访问容器,并通过成员函数sort()来改进基于节点的容器。

Selection sort scales quadratically 选择排序按二次缩放

Consider the following generic version of selection_sort 考虑以下selection_sort通用版本

template<class ForwardIt, class Compare = std::less<typename std::iterator_traits<ForwardIt>::value_type>>
void selection_sort(ForwardIt first, ForwardIt last, Compare cmp = Compare())
{
        for (auto it = first; it != last; ++it) {
                auto const selection = std::min_element(it, last, cmp);
                std::iter_swap(selection, it);
        }
}

On both std::array and std::list of length N , this has O(N^2) complexity: the outer loop processes all N elements, and the inner call to std::min_element is also of linear complexity, which gives overall quadratic scaling. 在长度为N std::arraystd::list上都具有O(N^2)复杂度:外部循环处理所有N元素,内部对std::min_element调用也具有线性复杂度,因此总体二次缩放。

However, since comparison based sorting can be done as cheaply as O(N log N) , this is typically unacceptable scaling for large N . 但是,由于基于比较的排序可以像O(N log N)一样便宜地进行,因此对于大N ,这通常是不可接受的缩放比例 As mentioned by @EJP , one redeeming feature of selection sort is that although it does O(N^2) comparisons, it only does O(N) data swaps. 如@EJP所述,选择排序的一项赎回功能是,尽管它执行O(N^2)比较,但仅进行O(N)数据交换。 However, for very large N , this advantage over most O(N log N) sorting algorithms, will ultimately be overwhelmed by the O(N^2) comparison cost. 但是,对于非常大的N ,与大多数O(N log N)排序算法相比,这种优势最终将因O(N^2)比较成本而无法承受。

Generic merge sort to the rescue? 通用合并排序可以解救吗?

Consider the following generic version of merge_sort 考虑以下的merge_sort通用版本

template<class BiDirIt, class Compare = std::less<typename std::iterator_traits<BiDirIt>::value_type>>
void merge_sort(BiDirIt first, BiDirIt last, Compare cmp = Compare())
{
        auto const N = std::distance(first, last);
        if (N < 2) return;
        auto middle = first + N / 2;
        merge_sort(first, middle, cmp);
        merge_sort(middle, last, cmp);
        std::inplace_merge(first, middle, last, cmp);
}

On both std::array and std::list of length N , this has O(N log N) complexity: the recursion depth is O(log N) (since the interval is being cut in half each time) and the call to std::inplace_merge is of linear complexity, which gives overall O(N log N) scaling. 在长度为N std::arraystd::list上,都具有O(N log N)复杂度:递归深度为O(log N) (因为每次将间隔减半),并且对std::inplace_merge具有线性复杂度,从而给出了总体O(N log N)缩放比例。

However, pretty much any serious sorting algorithm contender will distinguish itself not significantly with number of comparisons but rather the associated overhead for accessing and placing the data. 但是,几乎所有严肃的排序算法竞争者都不会在比较数量上显着区分自己,而是在访问和放置数据时会产生相关的开销。 Such optimizations can only be done with more knowledge than for the generic version. 这样的优化只能比通用版本具有更多的知识。

Random access containers can benefit from a hybrid algorithm 随机访问容器可以从混合算法中受益

Containers with random access iterators can be more cheaply sorted using hybrid algorithms. 使用混合算法可以更便宜地对具有随机访问迭代器的容器进行分类。 The std::sort() and std::stable_sort functions from the Standard Library provide such hybrid algorithms of O(N log N) worst-case complexity. 标准库中的std::sort()std::stable_sort函数提供了O(N log N)最坏情况复杂度的混合算法。 Typically they are implemented as IntroSort, which mixes the recursive random-pivot quick sort with heap sort and insertion sort, depending on the size of the various recursively sorted sub-ranges. 通常,它们以IntroSort的形式实现,它根据各种递归排序子范围的大小,将递归随机数据透视快速排序与堆排序和插入排序混合在一起。

Node-based containers can benefit from a member function sort() 基于节点的容器可以受益于成员函数sort()

Comparison based sorting algorithms make heavy use of copying or swapping the underlying data pointed to by the iterators. 基于比较的排序算法大量使用了复制或交换迭代器指向的基础数据的方法。 For regular containers, swapping the underlying data is the best you can do. 对于常规容器,交换基础数据是您最好的方法。 For node-based containers such as std::list or std::forward_list , you would prefer to splice : only rearranging the node pointers and avoid copying potentially large amounts of data. 对于基于节点的容器,例如std::liststd::forward_list ,您可能希望进行splice :仅重新排列节点指针,并避免复制可能的大量数据。 However, this requires knowledge about the connections between iterators. 但是,这需要了解迭代器之间的连接。

This is the reason that std::list and std::forward_list both have a member function sort() : they have the same O(N log N) worst-case complexity, but take advantage of the node-based character of the container. 这就是std::liststd::forward_list都具有成员函数 sort() :它们具有相同的O(N log N)最坏情况复杂度,但是利用了容器的基于节点的特征。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM