【问题标题】:How to sort separate arrays together (similar to sorting a table by one column)?如何将单独的数组排序在一起(类似于按一列对表格进行排序)?
【发布时间】:2017-04-26 06:04:53
【问题描述】:

给定两个或多个数组,我如何选择对一个数组进行排序并相应地对其他数组进行排序?这类似于按一列对表格进行排序。

int main()
{
    int values1[] = {5, 4, 3, 2, 1};
    double values2[] = {1.0, 2.0, 3.0, 4.0, 5.0};
    std::string values3[] = {"h", "e", "l", "l", "o"};

    // sort by values1

    // values1 = {1, 2, 3, 4, 5};
    // values2 = {5.0, 4.0, 3.0, 2.0, 1.0};
    // values3 = {"o", "l", "l", "e", "h"};

    // sort by values3

    // values1 = {4, 5, 3, 2, 1};
    // values2 = {2.0, 1.0, 3.0, 4.0, 5.0};
    // values3 = {"e", "h", "l", "l", "o"};
}

以下是我想出的一些策略。我希望你能给我一些正确方向的提示。谢谢!

策略一:

  1. 创建一个结构struct sortstruct{int i; double d; std::string s};
  2. 在里面创建向量和复制值
  3. 编写谓词函数并使用 std::sort
  4. 将值复制回原始结构。

这似乎很容易,但这也涉及将所有数据复制两次,并且如果数组很大,还会创建很多小对象。此外,如果数组数量不同,这也不灵活。

策略 2:

  1. 复制要排序的数组。
  2. 使用 std::sort 对副本进行排序。
  3. 遍历排序后的副本,对于每个值,找到原始数组中的值,然后交换所有值数组中的索引位置。

也很容易实现,但是在未排序的数组中查找值需要二次时间(每个值的线性时间)。

策略 3:

  1. 实现自定义排序算法,该算法采用“键”数组并将所有交换应用到其他数组。

我不喜欢我必须自己进行排序。但如果这是最好的策略,这当然没问题。

【问题讨论】:

  • 策略1如何涉及数据复制?
  • @CinCout:见步骤 2 和 4。
  • 如果这三个数组是相互关联的,为什么不把它们包装成一个类,所以隐藏实现细节。
  • 选项 4:创建第四个索引数组并根据用作键的任何数组重新排序该数组。最小的交换,没有副本,也没有机会破坏行之间的逻辑链接,但是为后续查找添加了一个额外的层 (values1[indexes[i]]) 并且不适用于std::sort
  • @Fabian 你可以创建一个结构体向量,并在向量中不断插入结构体对象,然后调用谓词 sort(),最后访问排序后的向量。副本在哪里?

标签: c++ arrays sorting


【解决方案1】:

最好的解决方案可能是创建一个包含值的结构或使用std::tuple,将它的实例放在一个向量中并对后者进行排序,从而摆脱所有其他数组。

如果你不能这样做,你可以引入一个间接层,即一个索引数组(实际上是一个查找数组)。
根据您的策略对其进行排序,并且永远不要更改其他数组中项目的位置。每当您想要获取第 i 个元素元组时,请从索引数组中获取第 i 个元素,并使用它从所有其他数组中获取正确的值。
如果您将来添加越来越多的数组,您不会有问题,只要您创建它们以某种方式相对于另一个隐式排序。
当您添加一个新元组时,只需将其他数组的 size 推到索引数组的末尾(我假设它们都具有相同的大小):这将是新数组的索引元素。如果需要,然后再次对索引数组进行排序。
排序函数可能需要您传递一个可以访问外部数组的比较函数,但使用 lambda 和通过引用捕获非常简单。

【讨论】:

  • 我喜欢查找数组的想法。但是,这意味着数组永远属于一起,但不一定如此。
  • @Fabian 好吧,它将副本和交换的数量减少到最低限度。那不可能是免费的。 ;-)
【解决方案2】:

实际上,我建议使用将分布在各个数组中的元组联系在一起的数据结构,例如 structtuple

但是人们可以利用这样一个事实,像qsort 这样的排序算法使用自定义比较函数来决定是否交换两个值。 鉴于此,可以定义一个比较函数,该函数除了返回比较结果外,还具有“手动”交换其他数组中的值的副作用。

我知道它很丑,因为它依赖于排序算法 qsort 的内部结构,而且我不完全确定我是否错过了一些重要的东西(如果有人因此而否决答案,我不会哭泣)。但在非常特殊的情况下,它可能会有所帮助:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int values1[] = {5, 4, 3, 2, 1};
double values2[] = {1.0, 2.0, 3.0, 4.0, 5.0};
std::string values3[] = {"h", "e", "l", "l", "o"};

int cmpfunc( const void *a, const void *b) {
    // calculate the array indexes of a and b using pointer arithmetics:
    size_t indexA = ((int*)a - values1);  
    size_t indexB = ((int*)b - values1);
    int result = *(int*)a - *(int*)b;
    if (result > 0) {
        swap(values2[indexA],values2[indexB]);
        swap(values3[indexA],values3[indexB]);
    }
    return result;
}

int main() {
    qsort(values1, 5, (size_t) sizeof(int), cmpfunc);
    return 0;
}

【讨论】:

  • 感谢您分享您的想法。您的想法似乎进入了我的策略3的方向。您说得对,这有点难看,但是如果您封装得当,还是值得一试的。
【解决方案3】:

range-v3,你可以这样做

ranges::sort(ranges::view::zip(values1, values2, values3),
             std::less<>{},
             [](const auto& t) { return std::get<1>(t);}); // Projection

Demo

【讨论】:

    猜你喜欢
    • 2022-09-28
    • 2021-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-31
    • 1970-01-01
    相关资源
    最近更新 更多