【问题标题】:How to alternate sort between even and odds ascendingly in C++?c++ - 如何在C++中交替排序偶数和奇数?
【发布时间】:2021-11-17 03:38:36
【问题描述】:

我有一个相等的偶数和奇数整数数组。我想对数组进行排序,使数组处于该模式:偶数1、奇数1、偶数2、奇数2、偶数3、奇数3、..等等,其中偶数1

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

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

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

【问题讨论】:

  • 输入总是奇数、偶数、奇数、偶数、奇数...还是输入的奇偶校验可以是任何顺序?
  • @jwezorek 可以是任意顺序。
  • 你不能用单一的排序来做到这一点(因为 cmp 需要知道以前发生的事情的状态,而不仅仅是当前的两个数字)。您可以对奇数进行排序,对偶数进行排序,然后压缩合并两个排序的数组。
  • 我仍然不是 100% 相信没有一些非常聪明的方法可以做到这一点......使用可能有状态的比较器?
  • @jwezorek A comparator for std::sort 需要诱导 strict weak orderinglhsrhs 的比较不允许依赖于 lhsrhs 以外的任何内容,因此排除了任何状态概念。如果你违反了这个约束,std::sort 的效果是不确定的。

标签: c++ comparator


【解决方案1】:

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

不过,我会尝试推测。

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

基于此,由于偶数和奇数不能相等,我假设您想说您有相同数量的偶数和奇数,例如 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. 对向量/列表/任何内容进行分区,使所有偶数占据左侧,所有奇数占据右侧 ([2,10,8,1,5,3]),
  2. 对两个分区进行独立排序 ([2,8,10,1,3,5]),
  3. 将它们压缩到 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,
    
  • ...sorted 前半部分的v 范围(偶数)...
                      sort(v | take(v.size()/2)),
    
  • ... 和sorted 的后半部分v(奇数)的愤怒,
                      sort(v | drop(v.size()/2)))
    
  • 最后我们将joinzip_with 得到的所有“配对”放在一个大范围内
             | join;
    

它解释了自己!

也许不太清楚的部分是make_range_of_2 的实现。我们需要的是一个给定两个ints 的函数,它返回一个只包含这两个的范围。我不知道是否有更简单的方法,但我能想到的第一个方法是通过single 从每个int 中创建一个元素范围,然后concatenating 它们。

【讨论】:

  • 很好:范围功能的东西已经走了很长一段路。
【解决方案2】:

正如 cmets 中所述,让您的 Comparator 完成所有需要的工作是不可能的。所以我们只需要一个免费的函数来做我们需要的事情。

#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 的方法可以节省一点存储成本。您可以将偶数和赔率分开,分别对每个范围进行排序,然后交换值。这将允许您就地修改可能需要的向量。

【讨论】:

  • 我谎称使用std::partition 在实现中进行编辑。时间限制让我变得更好。
【解决方案3】:

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

然后您需要正确交换它们。

类似的东西

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

【讨论】:

  • return std::make_tuple(lhs % 2, lhs) &lt; std::make_tuple(rhs % 2, rhs);
猜你喜欢
  • 2021-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-12
  • 1970-01-01
  • 2021-08-10
相关资源
最近更新 更多