【问题标题】:Why does std::sort not working with my comp function?为什么 std::sort 不能与我的 comp 函数一起使用?
【发布时间】:2021-01-10 16:40:28
【问题描述】:

有人能解释一下为什么 main 中的 2 last sort 函数不起作用,而第一个函数可以正常工作。

程序构建没有错误。代码如下:

#include <iostream>
#include <vector>
#include <math.h>
#include <algorithm>

using namespace std;

bool comp (int a, int b) {
    if (a % 2 == 0) { return 1; }
    else { return 0; }
}

int main()  {
    int n; cin >> n;
    int arr[n];
    for (int i = 0; i < n; i++) {
        cin >> arr[i];
    }
    sort(arr, arr+n, comp);
    int pos;
    for (int i = 0; i < n; i++) {
        if (arr[i] % 2 == 1) {
            pos = i;
            break;
        }
    }
    vector<int> un_sorted;
    for (int i = 0; i < n; i++)
    {
        un_sorted.push_back(arr[i]);
    }
    //7
    //5 9 2 8 6 4 7
    //4 6 8 2 5 9 7
    sort(un_sorted.begin(), un_sorted.begin()+pos-1);
    sort(un_sorted.begin()+pos, un_sorted.end(), greater<int>());
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
}

我试图将一个数组分成两部分:赔率和偶数,然后偶数按升序排序,赔率按降序排序

【问题讨论】:

  • 你的函数不符合std::sort比较器的要求
  • std::partition 是您将序列分成两部分的方式。 sort(arr, arr+n,comp) 表现出未定义的行为,因为 comp 不满足严格的弱排序要求。
  • @AlanBirtles:您的评论是有效的,但我不会将此问题标记为欺骗。
  • 作为欺骗链接的具体示例,comp(4, 4) 说 4 应该在 4 之前。它还说 2 应该在 comp(2, 4) 中的 4 之前,但 4 应该在 @987654328 中的 2 之前@.
  • @einpoklum 程序的行为是未定义的,问题只会在以后发生并不奇怪,一些std::sort 实现会使用无效的比较器超出容器的范围

标签: c++ sorting comparison


【解决方案1】:

comp() 函数存在问题

正如@AlanBirtles 所说,您的comp() 函数不会对un_sorted 数组的元素进行排序——这是对它们进行排序所必需的。

具体来说,

  • 对于x 的所有值,comp(x,x) 不为假;因此,从某种意义上说,一个值可以按照数组元素的正确顺序“先于它自己”。
  • comp(x,y)comp(y,x) 有时都为真 - 所以我们可以有成对的元素,每个元素都需要按排序顺序排在另一个之前。

因此,使用您的comp() 进行排序是没有意义的。

看着std::sort()page on cppreference,我们读到:

comp - 比较函数对象(即满足Compare 要求的对象),如果第一个参数小于(即排在前面)则返回​true第二个。

正如预期的那样,std::sort() 不能保证根据您的comp() 做任何有意义的事情。事实上,你的程序有undefined behavior,编译器可以让程序做它喜欢的任何事情。程序可能会崩溃,可能会陷入无限循环,或者什么也不做。

考虑使用std::partition()

你说过你想要:

  1. 将数组分成两部分:奇数和偶数。
  2. 按升序对偶数进行排序。
  3. 按降序排列赔率。

好吧,为什么不这样做呢?

标准库提供了两种可供您使用的算法:std::sortstd::partition。代码如下所示:

auto is_even = [](int x) { return x % 2 == 0; };
auto evens_end_and_odds_begin = 
    std::partition(std::begin(my_array), std::end(my_array), is_even);
std::sort(std::begin(my_array), evens_end_and_odds_begin, std::greater<int>{});
std::sort(evens_end_and_odds_begin, std::end(my_array), std::less<int>{});

【讨论】:

    【解决方案2】:

    您可以通过一个简单的sort 调用实现您的目标,使用巧妙的比较功能。像这样的:

    bool fancy_comparison(int a, int b) {
      bool a_is_even = (a % 2 == 0);
      bool b_is_even = (b % 2 == 0);
      if (a_is_even != b_is_even) {
        // Sort even numbers before odd ones
        return a_is_even;
      }
      if (a_is_even) {
        // Sort two even numbers ascending
        return a < b;
      }
      // Sort two odd numbers descending
      return a > b;
    }
    

    然后

    sort(arr, arr+n, fancy_comparison);
    

    【讨论】:

      【解决方案3】:

      问题是您可能假设按sort 排序的范围包括第二个参数指向的元素,在大多数文档中称为last。 但是 C++ 中的范围总是排除 end 指向的元素。 C++ 始终使用半开范围 [begin, end)。 在您的情况下,un_sorted.begin()+pos-1 处的元素被排除在外,因为第一个 sort 调用对除该元素之外的所有元素进行排序。第二个从下一个开始。 您应该从排序调用中删除-1

      在考虑迭代器时,数组中的索引有助于理解它们。 end() 始终是 begin() + size()。从等式中删除begin 时,您会得到一系列索引:[begin, end)[0, size),注意:半开范围 您对[0, pos-1)[pos, size) 的范围进行排序,缺少pos-1[0, pos) 是你的偶数元素。 [pos, size) 奇怪的元素。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多