【问题标题】:How do I order an array of strings on the basis of an array of integers如何根据整数数组对字符串数组进行排序
【发布时间】:2021-12-26 06:54:37
【问题描述】:

我有一个整数数组,里面有一堆 1-10 的数字 然后我有一个名称(字符串)数组,它们属于数字 a.e.

Numbers[0] = 5, Numbers[1] = 2
Names[0] = "Jeremy", Names [1] = "Samantha".

我可以通过以下方式轻松订购数字:

    int n = sizeof(Numbers) / sizeof(Numbers[0]);
    sort(Numbers, Numbers + n, greater<int>());

但是名称和数字根本不匹配。 我该如何解决这个问题?

【问题讨论】:

标签: c++ arrays sorting


【解决方案1】:

一种非常常见的方法是创建一个索引数组并对其进行排序:

std::vector<int> indices(Numbers.size());
std::iota(indices.begin(), indices.end(), 0);
std::sort(indices.begin(), indices.end(),
          [&](int A, int B) -> bool {
              return Numbers[A] < Numbers[B];
          });

原始数组未更改,但现在indices 可用于按所需顺序访问两个数组。

如果我们想在原地重新排序NumbersNames,那么我们可以 创建一组“后索引”,记录在排序数组中找到元素 i 的位置:

std::vector<int> back_indices(indices.size());
for (size_t i = 0; i < indices.size(); i++)
    back_indices[indices[i]] = i;

现在我们可以重新排序,例如,Names 按所需的顺序就位:

int index = 0;
std::string name = Names[index];
for (int i = 0; i < back_indices.size(); i++) {
    index = back_indices[index];
    std::swap(name,Names[index]);
}

【讨论】:

  • 这是一个很好的观点,在许多情况下您实际上不必重新排序数组,而只需以不同的预定索引顺序访问数组就足够了。但是,如果您确实需要重新排序数组,您可以使用向量indices 就地完成吗?
  • @PatrickParker 根据indices 对数组重新排序是困难的(即归结为诉诸),除非您也愿意重新排序索引——一旦您对数组的一个条目进行了置换,现在索引不再正确。如果您愿意复制(即没有到位),那么它是微不足道的。
  • @PatrickParker 添加了一种基于“后索引”的就地排序方法
【解决方案2】:

这是使用并行数组引入的复杂性的一个很好的例子。

如果您坚持将它们保留为并行数组,这是一种可能的方法。创建一个整数索引向量,初始化为 { 0, 1, 2, 3, etc }。每个整数代表数组中的一个位置。使用自定义比较函数对索引向量进行排序,该函数使用索引来引用 array1(数字)。完成后,您可以使用已排序的索引对 array1 和 array2(名称)重新排序。

也可以编写自己的排序算法,同时对额外的数组执行交换。

或者可以通过使用巧妙设计的代理来欺骗std::sort 对两个数组进行同时排序。我将证明这样的事情是可能的,尽管下面的代码可能被认为是一个简单的 hackish 概念证明。

使用设计巧妙的代理来欺骗 std::sort

#include <iostream>
#include <algorithm>

constexpr size_t SZ = 2;
int Numbers[SZ] = {5, 2};
std::string Names[SZ] = {"Jeremy", "Samantha"};
int tempNumber;
std::string tempName;

class aproxy {
    public:
    const size_t index = 0;
    const bool isTemp = false;
    aproxy(size_t i) : index(i) {}
    aproxy() = delete;
    aproxy(const aproxy& b) : isTemp(true)
    {
        tempName = Names[b.index];
        tempNumber = Numbers[b.index];
    }
    void operator=(const aproxy& b) {
        if(b.isTemp) {
            Names[index] = tempName;
            Numbers[index] = tempNumber;
        } else {
            Names[index] = Names[b.index];
            Numbers[index] = Numbers[b.index];
        }
    }
    bool operator<(const aproxy& other) {
        return Numbers[index] < Numbers[other.index];
    }
};

int main() {
    aproxy toSort[SZ] = {0, 1};
    std::sort(toSort, toSort+SZ);
    for(int i=0; i<SZ; ++i) {
        std::cout << "Numbers[" << i << "]=" << Numbers[i] << std::endl;
        std::cout << "Names[" << i << "]=" << Names[i] << std::endl;
    }
    return 0;
}

...设计更巧妙的代理可以完全避免分配 SZ“代理”元素的需要。

使用“设计更巧妙”的代理来欺骗 std::sort

#include <iostream>
#include <algorithm>
class aproxy;

constexpr size_t SZ = 2;
int Numbers[SZ] = {5, 2};
std::string Names[SZ] = {"Jeremy", "Samantha"};
aproxy *tempProxyPtr = nullptr;
int tempNumber;
std::string tempName;

class aproxy {
    public:
    size_t index() const
    {
        return (this - reinterpret_cast<aproxy*>(Numbers));
    }
    bool isTemp() const
    {
        return (this == tempProxyPtr);
    }
    ~aproxy()
    {
        if(isTemp()) tempProxyPtr = nullptr;
    }
    aproxy() {}
    aproxy(const aproxy& b)
    {
        tempProxyPtr = this;
        tempName = Names[b.index()];
        tempNumber = Numbers[b.index()];
    }
    void operator=(const aproxy& b) {
        if(b.isTemp()) {
            Names[index()] = tempName;
            Numbers[index()] = tempNumber;
        } else {
            Names[index()] = Names[b.index()];
            Numbers[index()] = Numbers[b.index()];
        }
    }
    bool operator<(const aproxy& other) {
        return Numbers[index()] < Numbers[other.index()];
    }
};

int main() {
    aproxy* toSort = reinterpret_cast<aproxy*>(Numbers);
    std::sort(toSort, toSort+SZ);
    for(int i=0; i<SZ; ++i) {
        std::cout << "Numbers[" << i << "]=" << Numbers[i] << std::endl;
        std::cout << "Names[" << i << "]=" << Names[i] << std::endl;
    }
    return 0;
}

免责声明: 虽然我上面的最后一个示例在技术上可能违反了严格别名规则(由于两种不同类型访问内存中的相同空间),但底层内存仅用于寻址空间 - 未修改 - 当我测试它时它似乎工作正常。此外,它完全依赖于std::sort 以某种方式编写:使用通过复制构造、单线程等初始化的单个临时变量。将所有这些假设放在一起,这可能是一个方便的技巧,但不是很便携,所以在你的时候使用风险自负。

【讨论】:

    【解决方案3】:

    我已经测试了这段代码,它应该会给你所需的行为:

    struct numberName {
        int num;
        string name;
    };
    
    bool compare(numberName a, numberName b){
        return a.num < b.num; // if equal, no need to sort.
    }
    
    int main() {
        numberName list[2];
        list[0].num = 5, list[1].num = 2;
        list[0].name = "Jeremy", list[1].name = "Samantha";
        sort(list, list+2, compare);
    }
    

    就像HAL9000 说的那样,你想使用一个结构,因为这会将属于彼此的变量保持在一起。或者,您可以使用一对,但我不知道一对是否适合您的情况。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-17
      • 1970-01-01
      • 1970-01-01
      • 2016-10-24
      • 2017-09-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多