为什么此std :: sort比较失败?

时间:2019-06-08 23:11:00

标签: c++ sorting vector std

我有一个由无符号整数组成的向量。父向量的每个元素都是三个无符号整数的向量。我主要想按子向量的第一个元素的降序对父向量排序,但我也想对具有相同第一个元素的子向量按第三个元素的顺序进行排序。我最初使用以下代码进行了此操作:

sort(league_vector.begin(), league_vector.end());
reverse(league_vector.begin(), league_vector.end());
sort(league_vector.begin(), league_vector.end(),
   [](const std::vector<unsigned int>& a, const std::vector<unsigned int>& b) {return a[0] == b[0] && a[2] < b[2];});

因此,只需对整个内容进行排序,然后将其反转即可,这将按第一个元素排序。然后使用lambda函数进行自定义排序,该函数仅在第三个元素相等时才返回true。

当父向量中元素的数量相对较少(大约50个或更少)时,这似乎很好用,但是当我的数量更多时,最终的排序将变得非常混乱,根本没有明显的模式。

我已将其替换为一个自定义排序:

sort(league_vector.begin(), league_vector.end(),
   [](const std::vector<unsigned int>& a, const std::vector<unsigned int>& b)
   {return ((a[0] > b[0]) || (a[0] == b[0] && a[2] < b[2]));});

因此,当第一个元素较大时,或者当第三个元素较小并且第一个元素相同时,此方法返回true。这似乎很好用,所以我只用它,但是我无法弄清第一种方法的问题。尤其是第一种方法似乎在某些时候可行,第二种比较只是第一种方法的扩展。

2 个答案:

答案 0 :(得分:2)

首先,std::sort不是稳定的排序,这意味着它不保留等效元素的顺序。如果要稳定排序,请使用std::stable_sort。此外,您的自定义比较功能没有任何意义。让我们分析一下它的行为:

如果a[0]等于b[0],则您的函数返回比较a[2]b[2]的结果。但是,如果a[0]不等于b[0],则函数始终返回false。由于等价定义为!(a < b) && !(b < a),因此根据您的比较函数,具有不同第一元素的任何两个向量都是相等的。

此函数也不是有效的比较函数,因为它不满足严格的弱排序。具有不同第一元素的任何两个向量是相等的,但是具有相同第一元素的两个向量不一定是相等的。这意味着,如果a = {1, 2, 3}b = {2, 3, 4}c = {1, 3, 4}a == bb == c却是a != c

答案 1 :(得分:0)

为什么您的第一次尝试失败?让我们以一个具体的例子为重点,并通过一个简单的测试来解释为什么这是无效的。

以下是两个联盟向量:

std::vector<std::vector<unsigned int>> league_vector = {{1,2,3,4}, {2,3,4,5}, {4,5,6,7}};

现在将其发送给std::sort

std::sort(league_vector.begin(), league_vector.end(),
          [](const std::vector<unsigned int>& a, 
          const std::vector<unsigned int>& b) {return a[0] == b[0] && a[2] < b[2];});

专注于此:

a[0] == b[0]

因此,假设std::sort按此顺序比较了league_vector中的前两个向量

a={1,2,3,4} b={2,3,4,5}

false起,您的比较函数将返回a[0] != b[0]

然后,如果编译器进行了切换,然后又给您提供此权限,只是看您的函数是否模棱两可,该怎么办:

a={2,3,4,5} b={1,2,3,4}

换句话说,就是简单的数值切换。自false起,您再次返回a[0] != b[0]

这是有什么道理的,您在第一个测试a的位置应该在b之后,而在第二个测试只是切换值的位置,a应该在{{ 1}}?

排序算法理所当然地变得混乱,并且将值以某种非正统的顺序放置。

请注意,Visual Studio编译器执行了我所描述的测试,其中给比较函数ba,检查返回值,然后检查b和{{1 }},并检查返回值。如果指出存在不一致之处,则调试运行时将使用“无效比较”(或类似的条件)断言。