在 C++ 中对向量进行分组排序
Posted
技术标签:
【中文标题】在 C++ 中对向量进行分组排序【英文标题】:Group sorting a vector in C++ 【发布时间】:2009-07-09 21:11:48 【问题描述】:我有一个充满对象的 std::vector,每个对象都有一个与之关联的数字组标识符。该对象还具有“大小”和“名称”等属性。
我需要能够按名称、大小和其他属性对对象向量进行排序,同时将它们组合在一起(例如,按上面提到的组标识符)。
如何实现这个目标?
【问题讨论】:
【参考方案1】:使用 STL,可以直接插入您自己的比较函数。您想定义一个比较函数,首先比较组,然后比较其他属性。
static bool CompareWidget(const Widget& w1, const Widget& w2)
if(w1.GetGroupNumber() != w2.GetGroupNumber())
return (w1.GetGroupNumber() < w2.GetGroupNumber());
if(w1.GetHeight() != w2.GetHeight())
return (w1.GetHeight() < w2.GetHeight();
/// etc
return false;
static void SortWidgetVector(WidgetVector& widgetVector)
std::sort(widgetVector.begin(), widgetVector.end(), CompareWidget);
【讨论】:
【参考方案2】:首先,让我们重新表述问题。您真正想要的是按组 ID 对对象进行排序,然后按(名称、大小、...)。如果先按组 ID 对它们进行排序,那么显然具有相同组 ID 的对象会粘在一起。
这显然可以使用std::sort
的自定义谓词轻松完成。大致如下:
struct MyPredicate
bool operator() (const MyClass& lhs, const MyClass& rhs) const
if (lhs.groupID != rhs.groupID)
return lhs.groupId < rhs.groupId;
else if (lhs.name != rhs.name)
return lhs.name < rhs.name;
else
return lhs.size < rhs.size.
;
std::sort(myObjects.begin(), myObjects.end(), MyPredicate());
【讨论】:
第二最佳答案。我发现结构的运算符重载在视觉上/心理上很麻烦,并且不如简单地定义一个函数来做同样的事情那么清楚。不知何故,我觉得我可以被说服……也许如果它被称为“class ServerGroupSort”(假设他的分组对象是“class Server”类型)。 将比较函子写成函数对象而不仅仅是普通函数的主要原因是你可以在需要类型的地方重用它们(例如std::map
comparer)。这个名字是随机的,正是因为没有足够的输入信息来想出一个好名字:)
我认为支持基于struct
的解决方案的主要原因是您可以在函数内声明struct
,从而有效地创建本地函数。
@Frerich 这仅适用于 C++11。您可以像在 C++03 中那样声明一个本地结构,但不能将其用作模板类型参数(VC++ 允许这样做,但它始终是语言扩展,而 g++ 会向您咆哮)。【参考方案3】:
使用组标识符作为主排序键。
例如,要按组排序,然后是名称,然后是大小,您可以使用比较对象,例如:
class my_comparison : public std::binary_function< object, object, bool >
public:
inline bool operator()(
object const &left,
object const &right ) const
return ( left.group_id < right.group_id ? true :
left.group_id > right.group_id ? false :
left.name < right.name ? true :
left.name > right.name ? false :
left.size < right.size );
;
然后将 this 的一个实例作为第三个参数传递给 std::sort()
函数。
【讨论】:
【参考方案4】:我需要能够按名称、大小和其他属性对对象向量进行排序,同时将它们组合在一起(例如,按上面提到的组标识符)。
实际上,我正在尝试做相反的事情。首先,我想按次要属性(例如名称、大小等)对向量进行排序,然后确保所有向量元素都包含在组中。
无论你怎么想,结果都应该是一样的,除非你想在不按组标识符排序的中间状态下使用向量。如果不是这样,我看不出为什么不能使用使用两个标准的比较函数(其中组 id 是主要条件,名称/大小/其他是次要条件)。您甚至可以创建一个通用的比较对象,它结合了两个谓词 (by_two_criteria) 的使用:
#include <vector>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <cstdlib>
template <class FirstCondition, class SecondCondition>
class by_two_criteria_t
FirstCondition first;
SecondCondition second;
public:
by_two_criteria_t(FirstCondition f, SecondCondition s): first(f), second(s)
template <class T>
bool operator()(const T& a, const T& b) const
return first(a, b) || (!first(b, a) && second(a, b));
;
template <class FirstCondition, class SecondCondition>
by_two_criteria_t<FirstCondition, SecondCondition> by_two_criteria(FirstCondition f, SecondCondition s)
return by_two_criteria_t<FirstCondition, SecondCondition>(f, s);
class X
int group;
int value;
public:
X(int g, int n): group(g), value(n)
friend bool compare_group(const X& a, const X& b);
friend bool compare_value(const X& a, const X& b);
friend std::ostream& operator<<(std::ostream& os, const X& x) return os << x.group << ", " << x.value;
;
bool compare_group(const X& a, const X& b) return a.group < b.group;
bool compare_value(const X& a, const X& b) return a.value < b.value;
X random_x()
return X(rand() % 10, rand() % 20);
int main()
using namespace std;
vector<X> vec;
generate_n(back_inserter(vec), 100, random_x);
sort(vec.begin(), vec.end(), by_two_criteria(compare_group, compare_value));
copy(vec.begin(), vec.end(), ostream_iterator<X>(cout, "\n"));
只是为了好玩,这是一个结合了 n 比较标准的仿函数(C++0x 仅适用于可变参数模板和新型初始化语法):
#include <functional>
template <class T, class ...Fun>
class n_criteria_t ;
template <class T, class Fun1, class ...FunN>
class n_criteria_t<T, Fun1, FunN...>: public std::binary_function<T, T, bool>
public:
n_criteria_t(Fun1 f1, FunN... fn): f1(f1), f2(fn...)
bool operator() (const T& a, const T& b) const
return f1(a, b) || (!f1(b, a) && f2(a, b));
private:
Fun1 f1;
n_criteria_t<T, FunN...> f2;
;
template <class T, class Fun1>
class n_criteria_t<T, Fun1>: public std::binary_function<T, T, bool>
public:
n_criteria_t(Fun1 f1): f1(f1)
bool operator() (const T& a, const T& b) const
return f1(a, b);
private:
Fun1 f1;
;
template <class T, class ...Fun>
n_criteria_t<T, Fun...> n_criteria(Fun... f)
return f...;
【讨论】:
【参考方案5】:您可以使用 partition 或 stable_partion 将您的对象分成几组并单独排序。我不确定它快多少或慢多少,但在我看来,代码会更容易理解。
class GroupPredicate : std::unary_function<object, bool>
public:
GroupPredicate(INT group)
: m_group(group)
inline bool operator()(const object &object)
return object.group == m_group;
INT m_group;
;
class SizeSort : public std::binary_function<object, object, bool>
public:
inline bool operator()(const object &left, const object &right)
return left.size < right.size;
;
//...
std::vector<object> arVector;
for (INT i = 0; i < groupCount; ++i)
std::vector<object>::iterator pEndGroup = std::partition(pStartGroup, arVector.end(), GroupPredicate(i));
std::sort(pStartGroup, pEndGroup, SizeSort());
pStartGroup = pEndGroup;
【讨论】:
以上是关于在 C++ 中对向量进行分组排序的主要内容,如果未能解决你的问题,请参考以下文章