提问人:Krillex 提问时间:5/22/2016 更新时间:5/22/2022 访问量:30637
C++,根据一个向量对另一个向量进行排序 [重复]
C++, Sort One Vector Based On Another One [duplicate]
问:
我得到的最好的例子是我想根据它们的分数对名称进行排序。
vector <string> Names {"Karl", "Martin", "Paul", "Jennie"};
vector <int> Score{45, 5, 14, 24};
因此,如果我将分数排序为 {5, 14, 24, 45},则名称也应该根据它们的分数进行排序。
答:
最好的方法是有一个结构,将名称与它们的分数结合起来,并有一个向量。
struct Person
{
std::string Name;
int Score;
};
然后,您可以声明向量:
std::vector<Person> people{ { "Karl", 45 }, { "Martin", 5 }, { "Paul", 14 } };
并且很容易从以下位置对其进行排序:std::sort
<algorithm>
std::sort(people.begin(), people.end(),
[](const auto& i, const auto& j) { return i.Score < j.Score; } );
或者,如果要按降序排序,可以更改 lambda:
std::sort(people.begin(), people.end(),
[](const auto& i, const auto& j) { return i.Score > j.Score; } );
评论
一种方法是将名称和分数存储在单个数据结构中,例如 a,然后可以按如下方式进行排序:std::vector<std::pair<std::string,int>>
#include <algorithm>
#include <vector>
#include <string>
#include <utility>
//...
std::vector<std::pair<std::string, int>> names_scores_vec;
// ... populate names_scores_vec...
// lambda for sorting, change to > for descending order
auto sort_by_scores = [](const std::pair<string,int>& _lhs,
const std::pair<string,int>& _rhs) { return _lhs.second < _rhs.second; };
std::sort(names_scores_vec.begin(), names_scores_vec.end(), sort_by_scores);
或者,如果想要重复的键(即允许重复的名称),请使用 a 或 等存储。std::map
std::multimap
如果无法将数据合并到对向量或结构中,则可以创建迭代器向量或从 0 到 size-1 的索引。然后使用自定义比较器对此进行排序。最后,创建一个新的向量,使用迭代器或索引填充它。
template<class T1, class A1, class T2, class A2>
std::vector<T1, A1> sort_by(
std::vector<T1,A1> const& vin, std::vector<T2,A2> const& keys
){
std::vector<std::size_t> is;
is.reserve(vin.size());
for (auto&& unused:keys)
is.push_back(is.size());
std::sort(begin(is),end(is),[&](std::size_t l, std::size_t r){
return keys[l]<keys[r];
});
std::vector<T1, A1> r;
r.reserve(vin.size());
for(std::size_t i:is)
r.push_back(vin[i]);
return r;
}
评论
std::iota
正如其他答案中已经建议的那样:将每个人的名字和分数结合起来可能是最简单的解决方案。
通常,这可以通过有时称为“zip”操作来实现:将两个向量组合成一个对向量 - 以及相应的“解压缩”。
通常实现,这可能如下所示:
#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
#include <iterator>
// Fill the zipped vector with pairs consisting of the
// corresponding elements of a and b. (This assumes
// that the vectors have equal length)
template <typename A, typename B>
void zip(
const std::vector<A> &a,
const std::vector<B> &b,
std::vector<std::pair<A,B>> &zipped)
{
for(size_t i=0; i<a.size(); ++i)
{
zipped.push_back(std::make_pair(a[i], b[i]));
}
}
// Write the first and second element of the pairs in
// the given zipped vector into a and b. (This assumes
// that the vectors have equal length)
template <typename A, typename B>
void unzip(
const std::vector<std::pair<A, B>> &zipped,
std::vector<A> &a,
std::vector<B> &b)
{
for(size_t i=0; i<a.size(); i++)
{
a[i] = zipped[i].first;
b[i] = zipped[i].second;
}
}
int main(int argc, char* argv[])
{
std::vector<std::string> names {"Karl", "Martin", "Paul", "Jennie"};
std::vector<int> score {45, 5, 14, 24};
// Zip the vectors together
std::vector<std::pair<std::string,int>> zipped;
zip(names, score, zipped);
// Sort the vector of pairs
std::sort(std::begin(zipped), std::end(zipped),
[&](const auto& a, const auto& b)
{
return a.second > b.second;
});
// Write the sorted pairs back to the original vectors
unzip(zipped, names, score);
for(size_t i=0; i<names.size(); i++)
{
std::cout << names[i] << " : " << score[i] << std::endl;
}
return 0;
}
评论
std::pair
operator<
std::pair<B, A>
std::pair<A, B>
zip(score, names, zipped)
zip(names, score, zipped)
std::sort(std::begin(zipped), std::end(zipped))
first
这不能通过自定义迭代器类型来完成吗?
编辑:
我以最简单的形式思考 - 基于第一个向量对一对向量进行排序 - 是有一个迭代器,其功能(如取消引用、下标、成员访问和相等以及排序比较)将调用第一个迭代器上的相应函数,所有其他函数(复制、算术、交换等)作用于两个迭代器。
template <typename Driver, typename Passenger>
struct duo_iterator { . . . };
template <typename D, typename P>
auto make_duo_iterator(D d, P p) -> duo_iterator<D, P> { . . . }
sort(make_duo_iterator(begin(v1), begin(v2)),
make_duo_iterator(end(v1), end(v2)));
迭代器可以扩展为 a 以使用任何重新排序算法,指向任意数量的额外背负序列。
这可能是一个有趣的小项目。或者也许类似的东西已经存在,在 Boost 或其他地方。multi_iterator
编辑2:
忘记上面的内容。
Eric Niebler 的 Range-v3 库有一个包装器,“给定 N 个范围,返回一个新范围,其中 Mth 元素是对所有 N 个范围的 Mth 元素调用make_tuple的结果。
在元组的第一个元素上使用谓词对范围进行排序可能就可以了。view::zip
很多人问这个问题,没有人想出一个满意的答案。这是一个 std::sort 帮助程序,它能够同时对两个向量进行排序,只考虑一个向量的值。该解决方案基于自定义 RadomIt(随机迭代器),直接对原始向量数据进行操作,无需临时副本、结构重排或附加索引:
namespace std {
namespace sort_helper {
template <typename _Data, typename _Order>
struct value_reference_t;
template <typename _Data, typename _Order>
struct value_t {
_Data data;
_Order val;
inline value_t(_Data _data, _Order _val) : data(_data), val(_val) {}
inline value_t(const value_reference_t<_Data,_Order>& rhs);
};
template <typename _Data, typename _Order>
struct value_reference_t {
_Data* pdata;
_Order* pval;
value_reference_t(_Data* _itData, _Order* _itVal) : pdata(_itData), pval(_itVal) {}
inline value_reference_t& operator = (const value_reference_t& rhs) { *pdata = *rhs.pdata; *pval = *rhs.pval; return *this; }
inline value_reference_t& operator = (const value_t<_Data,_Order>& rhs) { *pdata = rhs.data; *pval = rhs.val; return *this; }
inline bool operator < (const value_reference_t& rhs) { return *pval < *rhs.pval; }
};
template <typename _Data, typename _Order>
struct value_iterator_t :
iterator< random_access_iterator_tag, value_t<_Data,_Order>, ptrdiff_t, value_t<_Data,_Order>*, value_reference_t<_Data,_Order> >
{
_Data* itData;
_Order* itVal;
value_iterator_t(_Data* _itData, _Order* _itVal) : itData(_itData), itVal(_itVal) {}
inline ptrdiff_t operator - (const value_iterator_t& rhs) const { return itVal - rhs.itVal; }
inline value_iterator_t operator + (ptrdiff_t off) const { return value_iterator_t(itData + off, itVal + off); }
inline value_iterator_t operator - (ptrdiff_t off) const { return value_iterator_t(itData - off, itVal - off); }
inline value_iterator_t& operator ++ () { ++itData; ++itVal; return *this; }
inline value_iterator_t& operator -- () { --itData; --itVal; return *this; }
inline value_iterator_t operator ++ (int) { return value_iterator_t(itData++, itVal++); }
inline value_iterator_t operator -- (int) { return value_iterator_t(itData--, itVal--); }
inline value_t<_Data,_Order> operator * () const { return value_t<_Data,_Order>(*itData, *itVal); }
inline value_reference_t<_Data,_Order> operator * () { return value_reference_t<_Data,_Order>(itData, itVal); }
inline bool operator < (const value_iterator_t& rhs) const { return itVal < rhs.itVal; }
inline bool operator == (const value_iterator_t& rhs) const { return itVal == rhs.itVal; }
inline bool operator != (const value_iterator_t& rhs) const { return itVal != rhs.itVal; }
};
template <typename _Data, typename _Order>
inline value_t<_Data,_Order>::value_t(const value_reference_t<_Data,_Order>& rhs)
: data(*rhs.pdata), val(*rhs.pval) {}
template <typename _Data, typename _Order>
bool operator < (const value_t<_Data,_Order>& lhs, const value_reference_t<_Data,_Order>& rhs) {
return lhs.val < *rhs.pval; }
template <typename _Data, typename _Order>
bool operator < (const value_reference_t<_Data,_Order>& lhs, const value_t<_Data,_Order>& rhs) {
return *lhs.pval < rhs.val; }
template <typename _Data, typename _Order>
void swap(value_reference_t<_Data,_Order> lhs, value_reference_t<_Data,_Order> rhs) {
std::swap(*lhs.pdata, *rhs.pdata);
std::swap(*lhs.pval, *rhs.pval); }
} // namespace sort_helper
} // namespace std
这是一个使用示例,它使用 Age 值对 Names 和 Age 进行排序,使用标准 std::sort:
char* Names[] = { "Karl", "Paul", "Martin", "Jennie" };
int Age[] = { 45, 14, 5, 24 };
typedef std::sort_helper::value_iterator_t<char*,int> IndexIt;
std::sort(IndexIt(Names, Age), IndexIt(Names+4, Age+4));
排序为:
{ "Martin", "Paul", "Jennie", "Karl" };
{ 5, 14, 24, 45 };
在 Visual Studio 2017 和 GCC 5.4.0 上测试的代码。
评论
value_iterator_t
_[A-Z]
namespace std
将名称和分数合并到单个结构中的替代方法是创建一个索引列表并对其进行排序:
std::vector<int> indices(Names.size());
std::iota(indices.begin(), indices.end(), 0);
std::sort(indices.begin(), indices.end(),
[&](int A, int B) -> bool {
return Score[A] < Score[B];
});
现在可用于索引并按所需的排序顺序。indices
Names
Scores
评论
std::vector<std::pair<int,std::string>>
struct
int
string
vector
std::vector<size_t>
comp(i, j) := Score[i] < Score[j]