在单个排序调用C++中对多个向量进行排序
Sorting multiple vectors in single sort call C++
这个答案演示了如何使用漂亮的新 C++11 功能在值向量上使用 std::sort
有效地获取索引向量(这个问题也有各种重复项(。它还提示您可以通过"使用额外的向量"来获得排序向量和排序索引的双精度输出。但是,我实现这一目标的唯一方法是第二次致电std:sort
。我正在使用长度为数十个,也许数百,数千个元素的数组,试图专注于效率。是否可以从对std::sort
的单个调用中获得排序的向量和排序的索引?
更一般地说,我的问题是:可以用一个排序调用对多个向量进行排序吗?假设排序顺序仅基于一个提供的向量。
我在此期间想出的内容如下(对链接答案中的代码稍作修改(。如您所见,它需要为每个要排序的向量调用 std::sort
,即使它们都是根据单个向量的排序进行排序的。我怀疑可能有一种方法可以通过传递对 lambda 比较函数的引用来做到这一点,但我似乎无法让它工作。
#include <numeric>
#include <algorithm>
using std;
void sort_vectors(vector<size_t> idx, vector<double> &v) {
// sort indexes based on comparing values in v
sort(idx.begin(), idx.end(),
[&v](size_t i1, size_t i2) {return v[i1] < v[i2];});
// Sort the actual vector
sort(v.begin(), v.end());
return idx;
}
std::sort 采用迭代器:尽管自定义排序可能会在单个排序步骤中同时获取索引和值,但它不太可能有多大用处(并且可能需要不同的算法,使其速度较慢(。
算法设计
为什么?因为 std::sort 在O(n*logn)
时间内执行。从排序索引中移动元素将花费O(n)
时间,相比之下相对便宜。
使用上面的例子,在给出的链接中,我们有这个现有的代码:
using namespace std;
template <typename T>
vector<size_t> sort_indexes(const vector<T> &v)
{
// initialize original index locations
vector<size_t> idx(v.size());
iota(idx.begin(), idx.end(), 0);
// sort indexes based on comparing values in v
sort(idx.begin(), idx.end(),
[&v](size_t i1, size_t i2) {return v[i1] < v[i2];});
return idx;
}
我们现在可以从这些索引创建一个排序数组,这是一个便宜的步骤:
template <typename T>
vector<T> sorted_array(const vector<T> &v, const vector<size_t>& i)
{
vector<T> out;
out.reserve(v.size())
for (auto j: i) {
out.emplace_back(v[j]);
}
}
如果复制值太过繁琐,则可以使用 std::reference_wrapper
创建不可为空的包装器。
template <typename T>
vector<reference_wrapper<const T>> sorted_array(const vector<T> &v, const vector<size_t>& i)
{
vector<reference_wrapper<const T>> out;
out.reserve(v.size())
for (auto j: i) {
out.emplace_back(std::cref(v[j]));
}
}
即使对于大型阵列,这也应该非常有效。
谨慎
不要尝试一次对两个数组进行排序。对索引数组进行排序时,不要尝试移动值数组中的项目。为什么?因为值数组的比较是基于索引的:移动项目将破坏原始数组中的排序。由于一旦您拥有排序索引,将项目移动到正确的位置非常便宜,因此不必担心这里的性能:排序是瓶颈。
根据排序索引对数组或向量的重新排序可以在 O(n( 时间内就地完成。此示例使用第三个索引数组对两个数组进行排序。在重新排序期间,索引数组将恢复到从 0 到 n-1 的原始状态。在这个例子中,我手动完成了iota部分,它不使用模板,但可以很容易地转换为模板和向量:
#include <algorithm>
#include <iostream>
int main()
{
int A[8] = {8,6,1,7,5,3,4,2};
char B[8] = {'h','f','a','g','e','c','d','b'};
size_t I[8];
size_t i, j, k;
int ta;
char tb;
// create array of indices to A[]
for(i = 0; i < sizeof(A)/sizeof(A[0]); i++)
I[i] = i;
// sort array of indices according to A[]
std::sort(I, I+sizeof(I)/sizeof(I[0]),
[&A](int i, int j) {return A[i] < A[j];});
// reorder A[] B[] I[] according to I[]
for(i = 0; i < sizeof(A)/sizeof(A[0]); i++){
if(i != I[i]){
ta = A[i];
tb = B[i];
k = i;
while(i != (j = I[k])){
A[k] = A[j];
B[k] = B[j];
I[k] = k;
k = j;
}
A[k] = ta;
B[k] = tb;
I[k] = k;
}
}
for(i = 0; i < sizeof(A)/sizeof(A[0]); i++)
std::cout << A[i] << ' ';
std::cout << std::endl;
for(i = 0; i < sizeof(B)/sizeof(B[0]); i++)
std::cout << B[i] << ' ';
std::cout << std::endl;
return 0;
}
或者可以使用指针数组代替索引数组,这允许使用普通比较函数而不是 lambda 比较函数。
#include <algorithm>
#include <iostream>
bool compare(const int *p0, const int *p1)
{
return *p0 < *p1;
}
int main()
{
int A[8] = {8,6,1,7,5,3,4,2};
char B[8] = {'h','f','a','g','e','c','d','b'};
int *pA[8];
size_t i, j, k;
int ta;
char tb;
// create array of pointers to A[]
for(i = 0; i < sizeof(A)/sizeof(A[0]); i++)
pA[i] = &A[i];
// sort array of pointers according to A[]
std::sort(pA, pA+sizeof(A)/sizeof(A[0]), compare);
// reorder A[] B[] pA[] according to pA[]
for(i = 0; i < sizeof(A)/sizeof(A[0]); i++){
if(i != pA[i]-A){
ta = A[i];
tb = B[i];
k = i;
while(i != (j = pA[k]-A)){
A[k] = A[j];
B[k] = B[j];
pA[k] = &A[k];
k = j;
}
A[k] = ta;
B[k] = tb;
pA[k] = &A[k];
}
}
for(i = 0; i < sizeof(A)/sizeof(A[0]); i++)
std::cout << A[i] << ' ';
std::cout << std::endl;
for(i = 0; i < sizeof(B)/sizeof(B[0]); i++)
std::cout << B[i] << ' ';
std::cout << std::endl;
return 0;
}
template <typename OrderBy, typename... Others>
void sort_multiple(OrderBy& order_by, Others&... others) {
auto range = ranges::views::zip(order_by, others...);
ranges::actions::sort(range.begin(), range.end(), std::less{}, [](auto & tuple){ return get<0>(tuple); });
}
这个公式唯一不幸的事情是,没有一种简单的方法来指定自定义比较,因为...
参数是贪婪的。
struct custom_compare_t {} custom_compare;
template <typename Compare, typename OrderBy, typename... Others>
void sort_multiple(custom_compare_t, Compare compare, OrderBy& order_by, Others&... others) {
auto range = ranges::views::zip(order_by, others...);
ranges::actions::sort(range.begin(), range.end(), compare, [](auto & tuple){ return get<0>(tuple); });
}
- 计算排序向量的向量中唯一值的计数
- 查找两个排序向量中共有的元素
- 对的排序向量 (std::vector<pair<int, int>>) 按对的第一个元素搜索并更新第二个元素值
- 如何从C++的对的排序向量中获取有关给定值的相应对
- 对象接收堆栈溢出异常 c++ 的排序向量
- "x"的所有元素都存在于"y"(排序向量)中吗?
- 指向指针排序向量的指针向量的 C++ 向量
- 未排序向量上的lower_bound/upper_bound
- 智能指针的排序向量:神秘崩溃
- 按类型排序向量并按类型或派生类型搜索
- 如何在不使用标准算法的情况下在排序向量中添加 c 元素?
- C++排序向量<double>与在<Object>双成员变量上键控的向量
- 字符串指针的排序向量
- 具有前导数字的字符串的排序向量
- 保证重新排序向量
- 结构解释的C 排序向量
- 更新排序向量的一个条目
- 通过在排序向量上使用二叉搜索来定位未排序向量中的元素
- 排序向量上 std::find_if 和 std::bind2nd 的替代品
- 使用QuickSort算法以降序排序向量