【问题标题】:Sorting a vector in descending order按降序对向量进行排序
【发布时间】:2012-02-19 22:58:56
【问题描述】:

我应该使用

std::sort(numbers.begin(), numbers.end(), std::greater<int>());

std::sort(numbers.rbegin(), numbers.rend());   // note: reverse iterators

按降序对向量进行排序?一种方法或另一种方法有什么好处或缺点吗?

【问题讨论】:

  • +1 我认为答案很明显,但是这个问题有一点有趣的琐事。 :)
  • 我会投票给第一个选项,因为那样我就不必处理reverse_iterator的问题了。
  • @wilhelmtell 一个菜鸟问题,但为什么第二个要按降序排列?我们将相同的数组作为 sort 方法的输入。只是我们以相反的顺序给出它,那么为什么它应该按降序而不是升序排序,就像 ar.begin() 和 ar.end 的情况一样。
  • @shshnk std::sort(b, e); 将最小值放在 b (在我们的例子中是 rbegin,所以是 last 元素),最大值放在 e (在我们的case rend,所以 first 元素)。
  • 这能回答你的问题吗? Sorting vector elements in descending order

标签: c++ sorting stl vector iterator


【解决方案1】:

实际上,第一个是个坏主意。使用第二个,或者这个:

struct greater
{
    template<class T>
    bool operator()(T const &a, T const &b) const { return a > b; }
};

std::sort(numbers.begin(), numbers.end(), greater());

这样,当有人决定 numbers 应该保留 longlong long 而不是 int 时,您的代码不会静默中断。

【讨论】:

  • @FredOverflow:您在评论中获得了荣誉;)
  • 还是坚持第一个吧。对 numberContainer 使用 typedef - 一个好主意,以便有人可以交换到 long long - 并编写: std::sort(numbers.begin(), numbers.end(), std::greater<:value_type>( ));
  • +1 第一个确实令人困惑。 greater 比另一个是什么? rbeginrend 是为特定目的而制作的。
  • 为什么不只是std::greater&lt;typename decltype(numbers)::value_type&gt;() 之类的?
  • 这个答案已经过时了 - 你可以使用 std::greater&lt;&gt;() 从 C++14 开始。
【解决方案2】:

使用 c++14 你可以这样做:

std::sort(numbers.begin(), numbers.end(), std::greater<>());

【讨论】:

    【解决方案3】:

    使用第一个:

    std::sort(numbers.begin(), numbers.end(), std::greater<int>());
    

    它清楚地说明了正在发生的事情 - 即使有评论,将 rbegin 误读为 begin 的可能性也会降低。它清晰易读,正是您想要的。

    此外,考虑到反向迭代器的性质,第二个可能比第一个效率低,尽管您必须对其进行分析才能确定。

    【讨论】:

      【解决方案4】:

      这个呢?

      std::sort(numbers.begin(), numbers.end());
      std::reverse(numbers.begin(), numbers.end());
      

      【讨论】:

      • 一个原因可能是为了避免额外的复杂性:O(n * log(n)) + O(n) vs O(n * log(n))
      • @greg O(n * log(n)) = O(n * log(n) + n)。它们是定义同一集合的两种方式。你的意思是说“这可能会更慢。”
      • @pjvandehaar Greg 很好。他明确没有说 O(n * log(n) + n),他说 O(n * log(n)) + O(n)。你是对的,他的措辞不清楚(尤其是他对复杂性这个词的误用),但你可以用更友好的方式回答。例如:也许你的意思是使用“计算”​​这个词而不是“复杂性”这个词。反转数字是不必要的 O(n) 步骤,而不是其他相同的 O(n * log(n)) 步骤。
      • @OfekGila 我的理解是 big-O 表示法是关于函数集的,涉及 =+ 的表示法只是为了方便,意思是 。在这种情况下,O(n*log(n)) + O(n)O(n*log(n)) ∪ O(n) 的方便表示法,与O(n*log(n)) 相同。 “计算”这个词是一个很好的建议,你的语气是对的。
      【解决方案5】:

      您可以使用 Lambda 函数,而不是 Mehrdad 建议的仿函数。

      sort(numbers.begin(), numbers.end(), [](const int a, const int b) {return a > b; });
      

      【讨论】:

        【解决方案6】:

        根据我的机器,使用第一种方法对 [1..3000000] 的long long 向量进行排序大约需要 4 秒,而使用第二种方法大约需要两倍的时间。显然,这说明了一些事情,但我也不明白为什么。只是认为这会有所帮助。

        here 报告了同样的事情。

        正如 Xeo 所说,-O3 他们使用大约相同的时间来完成。

        【讨论】:

        • 您是否只是没有在优化打开的情况下进行编译?听起来很像 reverse_iterator 操作没有内联,并且考虑到它们只是实际迭代器的包装器,难怪如果没有内联,它们会花费双倍的时间。
        • @Xeo 即使它们被内联,一些实现也会使用每个取消引用的添加。
        • @ildjarn:因为它是那样的?例如,base() 成员函数返回包装的迭代器。
        • @Xeo 现在他们都在一秒钟内完成。谢谢!
        • @Xeo :我收回;标准实际上要求 std::vector&lt;&gt;::reverse_iterator 是根据std::reverse_iterator&lt;&gt; 实现的。奇怪的;今天我学会了。 :-P
        【解决方案7】:

        第一种方法是指:

            std::sort(numbers.begin(), numbers.end(), std::greater<>());
        

        您可能会使用第一种方法,因为它比第二种方法效率更高。
        第一种方法的时间复杂度小于第二种方法。

        【讨论】:

        • 这与 mrexciting 的答案相同。关于复杂性的评论我也不清楚。
        【解决方案8】:
        bool comp(int i, int j) { return i > j; }
        sort(numbers.begin(), numbers.end(), comp);
        

        【讨论】:

        • 要成为一个有效的答案,您应该考虑写一些关于您与 OP 提及方法的优点/缺点的内容
        【解决方案9】:

        TL;DR

        使用任何。它们几乎相同。

        无聊的回答

        像往常一样,有利有弊。

        使用std::reverse_iterator:

        • 当您对自定义类型进行排序并且不想实现时 operator&gt;()
        • 当你懒得输入std::greater&lt;int&gt;()

        在以下情况下使用std::greater

        • 当您想要更明确的代码时
        • 当您想避免使用晦涩难懂的反向迭代器时

        就性能而言,这两种方法同样有效。我尝试了以下基准:

        #include <algorithm>
        #include <chrono>
        #include <iostream>
        #include <fstream>
        #include <vector>
        
        using namespace std::chrono;
        
        /* 64 Megabytes. */
        #define VECTOR_SIZE (((1 << 20) * 64) / sizeof(int))
        /* Number of elements to sort. */
        #define SORT_SIZE 100000
        
        int main(int argc, char **argv) {
            std::vector<int> vec;
            vec.resize(VECTOR_SIZE);
        
            /* We generate more data here, so the first SORT_SIZE elements are evicted
               from the cache. */
            std::ifstream urandom("/dev/urandom", std::ios::in | std::ifstream::binary);
            urandom.read((char*)vec.data(), vec.size() * sizeof(int));
            urandom.close();
        
            auto start = steady_clock::now();
        #if USE_REVERSE_ITER
            auto it_rbegin = vec.rend() - SORT_SIZE;
            std::sort(it_rbegin, vec.rend());
        #else
            auto it_end = vec.begin() + SORT_SIZE;
            std::sort(vec.begin(), it_end, std::greater<int>());
        #endif
            auto stop = steady_clock::now();
        
            std::cout << "Sorting time: "
                  << duration_cast<microseconds>(stop - start).count()
                  << "us" << std::endl;
            return 0;
        }
        

        使用这个命令行:

        g++ -g -DUSE_REVERSE_ITER=0 -std=c++11 -O3 main.cpp \
            && valgrind --cachegrind-out-file=cachegrind.out --tool=cachegrind ./a.out \
            && cg_annotate cachegrind.out
        g++ -g -DUSE_REVERSE_ITER=1 -std=c++11 -O3 main.cpp \
            && valgrind --cachegrind-out-file=cachegrind.out --tool=cachegrind ./a.out \
            && cg_annotate cachegrind.out
        

        std::greater demo std::reverse_iterator demo

        时间是一样的。 Valgrind 报告相同数量的缓存未命中。

        【讨论】:

          【解决方案10】:

          您可以使用第一个,也可以尝试下面同样有效的代码

          sort(&a[0], &a[n], greater<int>());
          

          【讨论】:

            【解决方案11】:

            我认为您不应该使用问题中的任何一种方法,因为它们都令人困惑,而第二种方法正如 Mehrdad 所建议的那样脆弱。

            我提倡以下几点,因为它看起来像一个标准库函数并且其意图很明确:

            #include <iterator>
            
            template <class RandomIt>
            void reverse_sort(RandomIt first, RandomIt last)
            {
                std::sort(first, last, 
                    std::greater<typename std::iterator_traits<RandomIt>::value_type>());
            }
            

            【讨论】:

            • 这比仅仅使用std::greater 比较器要混乱一千倍......
            • @Apollys 我同意从 C++14 开始,std::greater 看起来是首选解决方案。如果您没有 C++14,如果您想排除 std::greater 的任何意外(例如,当某个时刻的类型从 int 更改为 long 时),它仍然很有用。
            猜你喜欢
            • 2019-11-04
            • 2018-10-08
            • 2022-11-28
            • 2022-01-02
            • 2022-07-20
            • 1970-01-01
            • 2011-03-25
            • 1970-01-01
            相关资源
            最近更新 更多