如何使用仅使用其中一个向量的条件以相同的方式对两个向量进行排序?
例如,假设我有两个相同大小的向量:
vector<MyObject> vectorA;
vector<int> vectorB;
然后我使用一些比较函数对vectorA
进行排序。排序重新排序vectorA
。如何将相同的重新排序应用于vectorB
?
一种选择是创建一个结构:
struct ExampleStruct {
MyObject mo;
int i;
};
然后将包含vectorA
和vectorB
内容的向量排序为单个向量:
// vectorC[i] is vectorA[i] and vectorB[i] combined
vector<ExampleStruct> vectorC;
这似乎不是一个理想的解决方案。还有其他选择,特别是在C ++ 11中吗?
答案 0 :(得分:97)
给定std::vector<T>
和T
的比较,我们希望能够找到您在使用此比较对矢量进行排序时使用的排列。
template <typename T, typename Compare>
std::vector<std::size_t> sort_permutation(
const std::vector<T>& vec,
Compare& compare)
{
std::vector<std::size_t> p(vec.size());
std::iota(p.begin(), p.end(), 0);
std::sort(p.begin(), p.end(),
[&](std::size_t i, std::size_t j){ return compare(vec[i], vec[j]); });
return p;
}
给定std::vector<T>
和排列,我们希望能够构建一个根据排列重新排序的新std::vector<T>
。
template <typename T>
std::vector<T> apply_permutation(
const std::vector<T>& vec,
const std::vector<std::size_t>& p)
{
std::vector<T> sorted_vec(vec.size());
std::transform(p.begin(), p.end(), sorted_vec.begin(),
[&](std::size_t i){ return vec[i]; });
return sorted_vec;
}
您当然可以修改apply_permutation
以改变您提供的向量,而不是返回新的已排序副本。这种方法仍然是线性时间复杂度,并且在向量中每个项目使用一位。从理论上讲,它仍然是线性空间复杂性;但是,实际上,当sizeof(T)
很大时,内存使用量的减少可能会非常显着。 (See details)
template <typename T>
void apply_permutation_in_place(
std::vector<T>& vec,
const std::vector<std::size_t>& p)
{
std::vector<bool> done(vec.size());
for (std::size_t i = 0; i < vec.size(); ++i)
{
if (done[i])
{
continue;
}
done[i] = true;
std::size_t prev_j = i;
std::size_t j = p[i];
while (i != j)
{
std::swap(vec[prev_j], vec[j]);
done[j] = true;
prev_j = j;
j = p[j];
}
}
}
vector<MyObject> vectorA;
vector<int> vectorB;
auto p = sort_permutation(vectorA,
[](T const& a, T const& b){ /*some comparison*/ });
vectorA = apply_permutation(vectorA, p);
vectorB = apply_permutation(vectorB, p);
答案 1 :(得分:3)
我会像Timothy一样使用排列,但如果你的数据太大并且你不想为排序后的矢量分配更多内存,你应该就地。以下是使用置换的O(n)(线性复杂度)就地排序的示例:
诀窍是获取排列和反向排列,以了解上次排序步骤覆盖数据的位置。
template <class K, class T>
void sortByKey(K * keys, T * data, size_t size){
std::vector<size_t> p(size,0);
std::vector<size_t> rp(size);
std::vector<bool> sorted(size, false);
size_t i = 0;
// Sort
std::iota(p.begin(), p.end(), 0);
std::sort(p.begin(), p.end(),
[&](size_t i, size_t j){ return keys[i] < keys[j]; });
// ----------- Apply permutation in-place ---------- //
// Get reverse permutation item>position
for (i = 0; i < size; ++i){
rp[p[i]] = i;
}
i = 0;
K savedKey;
T savedData;
while ( i < size){
size_t pos = i;
// Save This element;
if ( ! sorted[pos] ){
savedKey = keys[p[pos]];
savedData = data[p[pos]];
}
while ( ! sorted[pos] ){
// Hold item to be replaced
K heldKey = keys[pos];
T heldData = data[pos];
// Save where it should go
size_t heldPos = rp[pos];
// Replace
keys[pos] = savedKey;
data[pos] = savedData;
// Get last item to be the pivot
savedKey = heldKey;
savedData = heldData;
// Mark this item as sorted
sorted[pos] = true;
// Go to the held item proper location
pos = heldPos;
}
++i;
}
}
答案 2 :(得分:2)
我想通过我提出的扩展做出贡献。 目标是能够使用简单的语法同时对多个向量进行排序。
sortVectorsAscending(criteriaVec, vec1, vec2, ...)
该算法与Timothy提出的算法相同,但使用variadic templates,因此我们可以同时对任意类型的多个向量进行排序。
以下是代码段:
template <typename T, typename Compare>
void getSortPermutation(
std::vector<unsigned>& out,
const std::vector<T>& v,
Compare compare = std::less<T>())
{
out.resize(v.size());
std::iota(out.begin(), out.end(), 0);
std::sort(out.begin(), out.end(),
[&](unsigned i, unsigned j){ return compare(v[i], v[j]); });
}
template <typename T>
void applyPermutation(
const std::vector<unsigned>& order,
std::vector<T>& t)
{
assert(order.size() == t.size());
std::vector<T> st(t.size());
for(unsigned i=0; i<t.size(); i++)
{
st[i] = t[order[i]];
}
t = st;
}
template <typename T, typename... S>
void applyPermutation(
const std::vector<unsigned>& order,
std::vector<T>& t,
std::vector<S>&... s)
{
applyPermutation(order, t);
applyPermutation(order, s...);
}
template<typename T, typename Compare, typename... SS>
void sortVectors(
const std::vector<T>& t,
Compare comp,
std::vector<SS>&... ss)
{
std::vector<unsigned> order;
getSortPermutation(order, t, comp);
applyPermutation(order, ss...);
}
// make less verbose for the usual ascending order
template<typename T, typename... SS>
void sortVectorsAscending(
const std::vector<T>& t,
std::vector<SS>&... ss)
{
sortVectors(t, std::less<T>(), ss...);
}
在Ideone中测试。
我在this blog post中解释得更好。
答案 3 :(得分:2)
使用range-v3很简单,排序一个zip视图:
std::vector<MyObject> vectorA = /*..*/;
std::vector<int> vectorB = /*..*/;
ranges::v3::sort(ranges::view::zip(vectorA, vectorB));
或明确使用投影:
ranges::v3::sort(ranges::view::zip(vectorA, vectorB),
std::less<>{},
[](const auto& t) -> decltype(auto) { return std::get<0>(t); });
答案 4 :(得分:1)
从您的各个向量中创建一对双向量
initialize vector of pairs
Adding to a vector of pair
制作自定义排序比较器:
Sorting a vector of custom objects
http://rosettacode.org/wiki/Sort_using_a_custom_comparator#C.2B.2B
对你的对矢量进行排序。
将您的对矢量分成单个矢量。
将所有这些都放入一个函数中。
代码:
std::vector<MyObject> vectorA;
std::vector<int> vectorB;
struct less_than_int
{
inline bool operator() (const std::pair<MyObject,int>& a, const std::pair<MyObject,int>& b)
{
return (a.second < b.second);
}
};
sortVecPair(vectorA, vectorB, less_than_int());
// make sure vectorA and vectorB are of the same size, before calling function
template <typename T, typename R, typename Compare>
sortVecPair(std::vector<T>& vecA, std::vector<R>& vecB, Compare cmp)
{
std::vector<pair<T,R>> vecC;
vecC.reserve(vecA.size());
for(int i=0; i<vecA.size(); i++)
{
vecC.push_back(std::make_pair(vecA[i],vecB[i]);
}
std::sort(vecC.begin(), vecC.end(), cmp);
vecA.clear();
vecB.clear();
vecA.reserve(vecC.size());
vecB.reserve(vecC.size());
for(int i=0; i<vecC.size(); i++)
{
vecA.push_back(vecC[i].first);
vecB.push_back(vecC[i].second);
}
}
答案 5 :(得分:0)
我假设vectorA和vectorB的长度相等。你可以创建另一个向量,我们称之为pos,其中:
pos[i] = the position of vectorA[i] after sorting phase
然后,您可以使用pos对vectorB进行排序,即创建vectorBsorted,其中:
vectorBsorted[pos[i]] = vectorB[i]
然后vectorBsorted按照与vectorA相同的索引排列进行排序。
答案 6 :(得分:0)
我不确定这是否有效,但我会使用这样的东西。例如,为了对两个向量进行排序,我将使用降序冒泡排序方法和向量对。
对于降序冒泡排序,我会创建一个需要向量对的函数。
void bubbleSort(vector< pair<MyObject,int> >& a)
{
bool swapp = true;
while (swapp) {
int key;
MyObject temp_obj;
swapp = false;
for (size_t i = 0; i < a.size() - 1; i++) {
if (a[i].first < a[i + 1].first) {
temp_obj = a[i].first;
key = a[i].second;
a[i].first = a[i + 1].first;
a[i + 1].first = temp_obj;
a[i].second = a[i + 1].second;
a[i + 1].second = key;
swapp = true;
}
}
}
}
之后我将你的2个矢量值放入一个矢量对。如果您能够同时添加值,请使用此值,然后调用冒泡排序功能。
vector< pair<MyObject,int> > my_vector;
my_vector.push_back( pair<MyObject,int> (object_value,int_value));
bubbleSort(my_vector);
如果要在添加到2个向量后使用值,可以使用此值而不是调用冒泡排序功能。
vector< pair<MyObject,int> > temp_vector;
for (size_t i = 0; i < vectorA.size(); i++) {
temp_vector.push_back(pair<MyObject,int> (vectorA[i],vectorB[i]));
}
bubbleSort(temp_vector);
我希望这会有所帮助。 问候, 蚕儿
答案 7 :(得分:0)
我最近写了一个适合stl算法的zip迭代器。 它允许您生成如下代码:
std::vector<int> a{3,1,4,2};
std::vector<std::string> b{"Alice","Bob","Charles","David"};
auto zip = Zip(a,b);
std::sort(zip.begin(), zip.end());
for (const auto & z: zip) std::cout << z << std::endl;
它包含在单个标头中,唯一的要求是C ++ 17。 在GitHub上签出。
codereview上还有一则帖子,其中包含所有源代码。
答案 8 :(得分:0)
根据蒂莫西·希尔兹(Timothy Shields)的回答。
只需对apply_permutaion
进行细微调整,您就可以使用fold表达式将置换一次应用于不同类型的多个向量。
template <typename T, typename... Ts>
void apply_permutation(const std::vector<size_t>& perm, std::vector<T>& v, std::vector<Ts>&... vs) {
std::vector<bool> done(v.size());
for(size_t i = 0; i < v.size(); ++i) {
if(done[i]) continue;
done[i] = true;
size_t prev = i;
size_t curr = perm[i];
while(i != curr) {
std::swap(v[prev], v[curr]);
(std::swap(vs[prev], vs[curr]), ...);
done[curr] = true;
prev = curr;
curr = perm[curr];
}
}
}