如何在 C++ 中按升序交替排序偶数和赔率?

How to alternate sort between even and odds ascendingly in C++?

我有一个由相等的偶数和奇数组成的数组。

例如,如果数组是 [1, 2, 3, 4, 5, 6]。排序后的数组为 [2, 1, 4, 3, 6, 5].

我想使用 std::sort 比较功能来做到这一点。但不幸的是我做不到。我认为这是不可能的。

bool cmp(int lhs, int rhs) {
    // couldn't write anything
}

如果您创建一个 compare 函数,将所有奇数放在偶数之前,并简单地在这些组内进行比较,您可以在一种排序中先对所有奇数排序,然后对所有偶数排序。

然后你需要正确地交换它们。

像这样

bool cmp(int lhs, int rhs) {
    if ((lhs ^ rhs) & 1) // different oddness
        return lhs & 1;  // place odds first
    return lhs < rhs;
}

如评论中所述,您的比较器不可能完成所有需要的工作。所以我们只需要一个免费功能来完成所需的工作。

#include <algorithm>
#include <iostream>
#include <vector>

std::vector<int> organize_numbers(const std::vector<int>& v) {
  std::vector<int> evens;
  std::vector<int> odds;

  // Separate evens and odds
  for (auto i : v) {
    i & 1 ? odds.push_back(i) : evens.push_back(i);
  }

  std::sort(evens.begin(), evens.end());
  std::sort(odds.begin(), odds.end());

  // Zip evens and odds back together
  std::vector<int> result;
  std::size_t i = 0;
  for (; i < evens.size() && i < odds.size(); ++i) {
    result.push_back(evens[i]);
    result.push_back(odds[i]);
  }

  // One of evens or odds may be longer
  auto& remainder = evens.size() > odds.size() ? evens : odds;
  for (; i < remainder.size(); ++i) {
    result.push_back(remainder[i]);
  }

  return result;
}

int main() {
  std::vector<int> nums{1, 2, 3, 4, 5, 6, 7, 9, 11, 13};

  nums = organize_numbers(nums);

  for (auto i : nums) {
    std::cout << i << ' ';
  }
  std::cout << '\n';
}

该函数将奇数和偶数分开,对奇数和偶数进行排序,然后将它们压缩在一起。需要注意的是最后一个循环,用于处理其中一个比另一个长的情况。移动语义使价值交换更便宜。

我确定有一种涉及 std::partition 的方法可以节省一点存储成本。您可以分开偶数和赔率,分别对每个范围进行排序,然后交换值。这将允许您就地修改矢量,这可能是可取的。

首先,我认为这个问题需要改进,因为它很不清楚。

但是,我会尝试推测。

I have an array of equal even and odd integers.

基于此,由于偶数和奇数不能相等,我假设你想说 你有相等数量的偶数和奇数,例如 3 个奇数和 3 个偶数,如输入 [1, 2, 3, 4, 5, 6].

另一方面,我认为示例[1, 2, 3, 4, 5, 6]不够通用,因为一个奇偶校验的每个整数已经在另一个奇偶校验的两个连续整数之间,所以解决方案是交换前两个元素,发送两个元素,第三个两个元素等等。

一个稍微更一般的例子是 [1, 2, 5, 3, 10, 8] 应该变成 [2, 1, 8, 3, 10, 5].

要做到这一点,一种策略是

  1. 划分vector/list/whatever,所有偶数占据它的左边,所有奇数占据它的右边([2,10,8,1,5,3]),
  2. 对两个分区独立排序([2,8,10,1,3,5]),
  3. 将它们压缩到 range-of-2 ([[2,1],[8,3],[10,5]])
  4. 连接这些范围 ([2,1,8,3,10,5])

您可以使用 Range-v3 轻松做到这一点,它几乎像上面的列表一样:

#include <iostream>
#include <range/v3/action/sort.hpp>
#include <range/v3/algorithm/partition.hpp>
#include <range/v3/view/concat.hpp>
#include <range/v3/view/drop.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/view/single.hpp>
#include <range/v3/view/take.hpp>
#include <range/v3/view/zip_with.hpp>
#include <vector>

using ranges::partition;
using namespace ranges::actions;
using namespace ranges::views;

int main(){
    std::vector<int> v{1, 2, 5, 3, 10, 8};
    auto constexpr is_even = [](int i){ return i % 2 == 0; };
    
    auto constexpr make_range_of_2 = [](int x, int y){
        return concat(single(x), single(y));
    };
    partition(v, is_even); // partition
    auto r = zip_with(make_range_of_2,
                      sort(v | take(v.size()/2)/* even integers */),
                      sort(v | drop(v.size()/2)/* odd  integers */))
             | join/* range-or-ranges -> range */;

    for (auto i : r) {
        std::cout << i << std::endl;
    }
}

考虑到一半代码是 #includes 和 usings,解决方案非常简洁。

我们再看一遍,vs英文:

  • 我们 partition 集合 v 这样所有偶数都排在所有奇数之前,
    partition(v, is_even);
    
  • 我们zip_with函数make_range_of_2...
    auto r = zip_with(make_range_of_2,
    
  • ... v 前半部分的 sorted 范围(偶数)...
                      sort(v | take(v.size()/2)),
    
  • ...和 ​​v 后半部分的 sorted 愤怒(奇数),
                      sort(v | drop(v.size()/2)))
    
  • 最后我们 join 把我们用 zip_with 得到的所有“对”放在一个大范围内
             | join;
    

不言自明!

也许不太清楚的部分是 make_range_of_2 的实现。我们需要的是一个给定两个 int 的函数 returns 一个仅包含这两个的范围。我不知道是否有更简单的方法,但我首先想到的是通过 single 从每个 int 中创建一个单元素范围,然后 concat 对它们进行赋值。