基于 C++ 中的多个事物进行排序
Posted
技术标签:
【中文标题】基于 C++ 中的多个事物进行排序【英文标题】:Sort based on multiple things in C++ 【发布时间】:2010-08-26 11:37:51 【问题描述】:struct Record
char Surname[20];
char Initial;
unsigned short int Gender; //0 = male | 1 = female
unsigned short int Age;
;
Record X[100];
如何使用 Quicksort 将值按年龄增长进行排序,女性在男性之前,姓氏按字母顺序排列?我有一个:
bool CompareData(const int& A, const int& B)
return Records[A].Age < Records[B].Age; //this sorts by age atm
【问题讨论】:
一定要快速排序吗? 我已经实现了快速排序,但是我不确定如何提醒 CompareData() 正确排序。 【参考方案1】:一般模式是:
bool CompareData(const T& a, const T& b)
if (a.PrimaryCondition < b.PrimaryCondition) return true;
if (b.PrimaryCondition < a.PrimaryCondition) return false;
// a=b for primary condition, go to secondary
if (a.SecondaryCondition < b.SecondaryCondition) return true;
if (b.SecondaryCondition < a.SecondaryCondition) return false;
// ...
return false;
其中<
表示所需排序顺序中的“小于”,您可能需要为此使用自定义比较运算符(例如,strcmp
用于字符串,或者如果您想要降序排序,则反转<
) (感谢哈利指出这一点)
我在所有条件下都使用了<
,因为有时这是唯一可用的比较操作,例如当您必须使用未知数据类型的比较谓词时。
[编辑] 注意:最后一行return false
处理a
和b
被认为对比较器相等的情况。
想象一下a.PrimaryCondition==b.PrimaryCondition
和a.SecondaryCondition==b.SecondaryCondition
- 在这种情况下,前面的条件都没有返回任何值。
【讨论】:
@Tyler:如果你想颠倒顺序,你交换return true;
和return false;
你不要开始使用operator >
,因为正如peterchen所说,C++标准算法建立的实践库是排序由<
定义,只有<
。
@peterchen false
到底支持什么?我尝试true
以保持排序的稳定性,但结果搞砸了。
@gustafbstrom:排序谓词需要返回true
当且仅当a < b
,即false
代表a==b
。有关详细信息,请参阅此处:en.cppreference.com/w/cpp/concept/Compare --- 返回 true
here 会弄乱您的结果。试想一下:相等的测试是!(a<b) && !(b<a)
。您的 return true
将无法通过该测试。
@peterchen 有道理,感谢您抽出宝贵时间。
另外:即使您可以断言所有元素都是唯一的(因此在逻辑上不可能相等)我仍然观察到 a
和 b
可以引用同一个对象(至少,似乎没有相反的要求),因此返回false
仍然是必不可少的,原因与@peterchen 上述相同。【参考方案2】:
bool CompareData(const int& A, const int& B)
return (Records[A].Age < Records[B].Age) ||
((Records[A].Age == Records[B].Age) && (Records[A].Gender > Records[B].Gender)) ||
((Records[A].Age == Records[B].Age) && (Records[A].Gender == Records[B].Gender) &&
(strcmp(Records[A].Surname, Records[B].Surname) < 0));
这首先按年龄进行比较,如果 A 应该根据年龄出现在 B 之前,则返回 true。
如果年龄相等,则按性别进行比较,如果根据性别(A 是女性,B 是男性),A 应该出现在 B 之前,则返回 true。
如果年龄和性别相同,则按姓氏进行比较(使用strcmp
,尽管如果您使用std::string
而不是char 数组,您可能只使用<
),并返回true如果 A 应该按姓氏字母顺序出现在 B 之前。
【讨论】:
谢谢 :) 这真的很有帮助。【参考方案3】:所有唱歌跳舞比较器的另一个选择是确保您的排序是稳定的排序(快速排序不一定是稳定的),并且每次使用不同的比较器进行多次排序。
例如
bool CompareAge (const record& l, const record& r)
return l.age < r.age;
bool CompareGender (const record& l, const record& r)
return l.gender < r.gender;
std::stable_sort(X, X+100, &CompareGender);
std::stable_sort(X, X+100, &CompareAge);
这可能会稍微慢一些,但可以让您更灵活地使用排序顺序
【讨论】:
但是快速排序不是稳定的。 (编辑后)正如我所说,std::sort
不稳定;你应该改用std::stable_sort
。
可以的。取决于实现
根据实现来回答根本不是一个好的答案。 ;)
std::sort 也不能保证稳定 -> 改为 stable_sort
技术上的快速排序可能是稳定的,具体取决于您选择枢轴的方式,它只是通常不使用稳定的实现,因为它们速度较慢。由于 OP 正在实施他们自己的快速排序,我认为最好不要假设【参考方案4】:
简单的C++解决方案是
struct Record
std::string Surname;
char Initial;
unsigned short int Gender; //0 = male | 1 = female
unsigned short int Age;
operator<(Record const& rhs) const
return std::tie(Gender, Age, Surname) < std::tie(rhs.Gender, rhs.Age, rhs.Surname);
;
但是,std::tie
直接按字段值排序。这意味着您不能使用 char[20]
并且男性将首先排序。一个简单的变体解决了这个问题:
struct Record
char Surname[20];
char Initial;
unsigned short int Gender; //0 = male | 1 = female
unsigned short int Age;
operator<(Record const& rhs) const
return std::make_tuple(~Gender, Age, std::string(Surname)) <
std::make_tuple(~rhs.Gender, rhs.Age, std::string(rhs.Surname));
;
使用make_tuple
,我们可以传递表达式。
【讨论】:
您还可以混合使用这些方法来尽可能避免复制,首先将转换后的值存储到函数局部变量中,然后返回std::tie
的结果,尽可能使用现有成员和转换后的值除此以外。或者反转它,使用std::make_tuple
,但将未修改的参数包装在std::ref
/std::cref
中,以使生成的tuple
包含引用而不是值的副本。在这种情况下,没有真正的需要(因为只有一个成员是非值类型,并且必须将其转换为 string
[或在 C++17 上,string_view
]),但在其他情况下很有用。跨度>
如果你想混合引用和表达式,你也可以使用std::forward_as_tuple
。【参考方案5】:
最好像这样实现比较器:
bool CompareRecords(const Record& a, const Record& b)
if (a.Age < b.Age)
return true;
else if (a.Age > b.Age)
return false;
if (a.Gender < b.Gender)
return true;
else if (a.Gender > b.Gender)
return false;
if (strcmp(a.Surname, b.Surname) < 0)
return true;
return false;
这使您可以轻松使用std::sort
算法。排序本身将如下所示:
std::sort(X, X + 100, &CompareRecords);
编辑
您甚至可能希望为此结构实现operator <
——在这种情况下,您通常可以将Record
结构的两个对象与运算符<
进行比较。然后就不需要将第三个参数添加到std::sort
。好吧,有了它并实现了operator ==
,您可以进行所有可能的比较。 :)
【讨论】:
所以 std::sort 比使用我自己的 Quicksort 更好? 是的,std::sort
是更好的解决方案——只要您不想学习如何实现 Quicksort 本身。但在那种情况下,这样的排序结构并不是一个好主意。 ;)
@Johnny std::sort
比您自己的快速排序更好,主要是因为您不必编写它。除非您编程的目的是自学排序算法,否则您通常应该使用已经存在的算法。
但是我的 CompareData 返回 bool
@Johnny 关于std::sort
,您的函数的问题不是它不返回bool
,而是它没有将const Record&
作为参数。 std::sort
函数需要为其比较器提供实际的记录结构以进行比较,而不是像您的函数现在采用的那样对记录数组进行索引。以上是关于基于 C++ 中的多个事物进行排序的主要内容,如果未能解决你的问题,请参考以下文章
pandas读取csv数据sort_index函数基于多层行索引对数据排序(设置level参数基于多层索引中的多个层行索引进行数据排序ascending参数指定不同层的排序方向)
如何在 Iphone SDK 中基于单个数组对多个数组进行排序