通过 std::sort 对 C 2D 数组进行排序

Sorting a C 2D array via std::sort

本文关键字:数组 2D 排序 std sort 通过      更新时间:2023-10-16

我有一个 2D 数组a[][40]。我正在尝试通过调用std::sort对其进行排序,并且我已经编写了Compare函数。但是,C++希望我有一个要排序的std::vector,而不是一个简单的数组,我希望排序后的数组本身a,我不想创建另一个数组并将排序结果保存在那里。似乎有很多方法可以实现这一点。我可以想到五种方法,但似乎没有一种是高效和有效的。

1(

直接使用std::sort(std::begin(a), std::begin(a) + something, cmp);

它不起作用,因为std::begin不知道如何指向 2D 数组的开头。此外,即使编译,它的排序也会不正确,因为 2D 数组不是对数组的引用数组,而是连续数组(与 Java 不同(。

游乐场:https://godbolt.org/g/1tu3TF

2(

std::vector<unsigned char[40]> k(a, a + x);
std::sort(k.begin(), k.end(), cmp);

然后将所有内容复制回a

它不起作用,因为它是一个 2D 数组,并且不能使用std::sort以这种方式排序。与第一次试验相比,这个试验使用的内存是内存的两倍,并且复制了两次所有内容(如果它有效的话(!

游乐场:https://godbolt.org/g/TgCT6Z

3(

std::vector<int> k(x);
for (int i = 0; i < x; k[i] = i, i++);
std::sort(k.begin(), k.end(), cmp2);

然后将a的顺序更改为与k相同;

这个想法很简单,创建一个代表性"指针"的向量,对它们进行排序(因为cmp2函数秘密访问a并比较值(,然后使ak具有相同的顺序。

最后,重新排序循环将非常复杂,需要一个大的临时变量。此外,为了让cmp2访问a的值,必须创建一个指向a的全局变量指针,这是"坏"代码。

游乐场:https://godbolt.org/g/EjdMo7

4(

对于所有unsigned char[40],可以创建一个结构,并且可以将其值复制到结构中。需要声明比较运算符和=运算符。排序后,可以将它们复制回a

如果数组不必复制到结构体以使用结构体的运算符,这将是一个很好的解决方案,但它们需要被复制,因此所有值将被复制两次,并且将根据需要使用两次内存。

5(

对于所有unsigned char[40],可以创建一个具有指向它们的指针的结构。它们可以按指向的值排序,结果可以保存到指针数组中。

这可能是最好的选择,尽管结果是指针数组而不是a。它为什么好的另一个原因是它不会移动数组,而是移动指针。

总而言之,我需要通过std::sort对 2D 数组a[][40]进行排序,但我还没有决定最佳方法。似乎有一个我想不出的"最好的方法"。你能帮帮我吗?

编辑:澄清一下,我希望{{3,2}{1,4}}变得{{1,4}{3,2}}

问题不在于迭代 2D 数组。如果列大小是constexpr值,则指向数组的指针是很好的迭代器。

但是C++排序(或变异(算法都要求底层类型是可移动的,并且移动可分配的,并且数组不可分配。但是包装底层数组就足够了:

template <class T, int sz>
class wrapper {
T* base;
bool own;            // a trick to allow temporaries: only them have own == true
public:
// constructor using a existing array
wrapper(T* arr): base(arr), own(false) {}
~wrapper() {              // destructor
if (own) {
delete[] base;    // destruct array for a temporary wrapper
}
}
// move constructor - in fact copy to a temporary array
wrapper(wrapper<T, sz>&& src): base(new T[sz]), own(true) {
for(int i=0; i<sz; i++) {
base[i] = src.base[i];
}
}
// move assignment operator - in fact also copy
wrapper<T, sz>& operator = (wrapper<T, sz>&& src) {
for(int i=0; i<sz; i++) {
base[i] = src.base[i];
}
return *this;
}
// native ordering based on lexicographic string order
bool operator < (const wrapper<T, sz>& other) const {
return std::char_traits<char>::compare(base, other.base, sz) < 0;
}
const T* value() const {    // access to the underlying string for tests
return base;
}
};

然后,您可以使用任何C++排序算法对 C 兼容的 2D 数组进行排序:

std::vector<wrapper<char, 40> > v { &arr[0], &arr[sz] };  // pointer are iterators...
std::sort(v.begin(), v.end());                            // and that's all!
for (int i=0; i<sz; i++) {                                // control
std::cout << arr[i] << std::endl;
}

开销是包含指针和布尔值的结构向量,但排序的实际上是原始的 2D 数组。

当然,由于 C 库可以从C++访问,因此qsort对 C 兼容的 2D 数组进行排序肯定会更容易。但是这种方式允许使用stable_sortpartial_sort(如果它们是相关的(。