比qsort更快的c++排序类
C++ sorting classes faster than qsort
我有一个class
class Zaposlenik {
private:
string prezime;
string funkcija;
double placa;
public:
bool operator==(const string& prezime) const;
bool operator<(const string &prezime) const;
bool operator<(const Zaposlenik &other) const;
我使用operator与string进行二进制搜索和operator<使用Zaposlenik进行排序>使用Zaposlenik进行排序>
我不能改变头类,我只能在。cpp中编写代码。
我还有
class Firma {
private:
vector<Zaposlenik> zaposlenici;
public:
void sort();
我也不能改变这个类,我必须为它写。cpp。我将2 .cpp上传到自动分级服务器,该服务器将500,000 Zaposlenik输入矢量zaposlenici,然后进行200万次搜索。
我使用qsort和bsearch,它太慢了。上传时不能超过3秒
我已经编写了重载操作符,我相信它们很好,但显然qsort可以更快。
Vector按字符串prezme排序,名称从"aaaa"到"ZZZZ",所以4个字母的大字母和小字母组合。
string funkcija;
和double placa;
对于排序没有任何意义。
谁能告诉我哪种排序比qsort更快?请记住,我对main没有任何控制,并且在创建成员时无法计数。
注:类中还有其他函数,但它们对这部分没有任何意义。它也有搜索功能,但我相信这是最快的。
三件事:
-
使用
std::sort
代替std::qsort
,它更快,因为它可以内联调用比较运算符(如果你在头文件中定义它或启用链接时间优化)。 -
覆盖
swap
为您的类,使它可以有效地交换,而不是通过临时变量复制。但是,这需要更改头文件(因为您需要访问私有变量)。 -
由于排序字符串的长度是固定的4,因此使用不同的排序算法将是有益的。经典的选择是基数排序,这很容易实现。从你的一些评论来看,你的教授似乎希望你实现这个。
自动分级服务器,将500000 Zaposlenik输入矢量zaposlenici,然后进行200000次搜索。我用了qsort和bsearch,但是太慢了。
只是澄清一下,您没有在每次调用搜索之前调用qsort,对吗?因为二分查找只有在列表已经排序的情况下才快速。如果你在每次搜索之前对列表进行排序,你会得到糟糕的性能。
考虑到您概述的约束(所有字符串都是四个字符长),我刚刚测试了std::sort
与自定义桶排序,在一百万个元素上,桶排序快了8倍。提示:四个字符的字符串可以用4 * 6 = 24位编码,因此您将需要16.777.216个桶来计数。
有几件事需要考虑。第一次和首先,不能在std::vector<Zaposlenik>
上使用qsort
。qsort
在交换时使用memcpy
复制对象,并且memcpy
只适用于具有琐碎副本的对象。使用qsort
在这种情况下可能会更快,因为它不会正确地复制对象。你必须使用std::sort
;其他都不行
这样做了:速度(或者至少是你可以参加的派对)std::sort
的影响取决于两个因素:速度你的比较,和交换的速度。你没有展示什么bool Zaposlenik::operator<( Zaposlenik const& other ) const;
,所以我们只能猜测。如果它比return
prezime, other.prezime )
做更多的事情,那么你应该写一个单独的比较函数,并使用它调用std::sort
。另一个方面是交换:std::sort
最终使用std::swap
,其默认的实现类似于:
template <typename T>
void
std::sort( T& lhs, T& rhs )
{
T tmp( lhs );
lhs = rhs;
rhs = lhs;
}
对于许多类,这涉及到大量额外的复制;为例如,std::string
,这将执行三个深度拷贝对象的动态分配和释放内存。然而,std::string
有一个成员函数swap
;在许多情况下,它可以通过交换来实现交换指针,而不是深度复制。这可能导致一个显著的加速。你的类Zaposlenik
不行但是,任何优化std::swap
的事情都可以,这样您就可以深入了解副本。您应该提供一个成员函数swap
:
void Zaposlenik::swap( Zaposlenik& other )
{
swap( prezime, other.prezime );
swap( funkcija, other.funkcija );
swap( placa, other.placa );
}
此函数将使用系统优化的swapstd::string
。为了确保std::sort
使用它,您还应该提供一个重载的自由函数swap
(在与Zaposlenik
相同的名称空间),它调用您的成员功能:
void
swap( Zaposlenik& lhs, Zaposlenik& rhs )
{
lhs.swap( rhs );
}
原因:std::sort
调用了一个自由函数swap
这里真正的问题是你对类头的限制。我怀疑瓶颈要么是排序时的交换操作,要么是词法字符串比较(也可能两者都有)。如果你根本不能改变类的定义,那么改进它将会很棘手,因为你将不得不在你的实现中添加大量的帮助代码,并且一切都变得比它必须的更复杂。
无论如何,这是我建议的方法:因为你是基于字符串排序的,实现一个专门的Trie版本,当按字典顺序排序序列时,你不能打败Trie的性能。你可以完全在你的。cpp文件中实现这个数据结构,并在你的Firma::sort
方法中实例化它。
当您似乎关注速度时,您可能愿意在内存消耗方面做出权衡。因此,您将Treap中的每个节点实现为初始化为256长度的std::vector<std::shared_ptr<Trie>>
(所有插槽初始化为nullptr
)或std::array<std::shared_ptr<Trie>,256>
。现在基本上就是将每个字符串插入到数据结构中,然后再次将它们全部读出。这种方法在所有字符串组合的总大小上是线性的(因此是最优的)。
注:注意,在遍历Trie时(即在写入Firma::zaposlenici
成员时),每个节点上的256个槽表会产生256个常数因子。如果您处理的是ASCII,您可以将表大小减少到128或将单个字节分成半字节,从而产生2*16的开销,而不是256。
Edit:如果你知道你只会遇到a..z和a..z中的字符,那么你的基本字母大小为2*26 = 52而不是256。因此,Trie的每个节点中的查找表的大小只需为52(也就是说,每个节点最多可以有52个子节点)。
- 二叉排序树无法编译
- 仅使用绝对值对数组进行排序,并在C++中显示实际值
- C++选择排序算法中的逻辑错误
- 使用C++程序合并排序没有得到正确的输出
- 计算排序向量的向量中唯一值的计数
- 排序算法c++
- 使用2个键的cpp-stl::优先级队列排序不正确
- 将结构向量排序为子组
- 在c++中尝试对对象数组进行排序时,出现std:bad_alloc错误
- 如何对点云数据进行排序
- 对字符串进行排序时,在c++中处理sort()
- 是否有类似std::lower_bound的函数,而不需要排序/分区输入
- 下面是排序算法O(n)吗
- std::sort()函数无法对向量的一部分进行排序
- shell排序中的交换和比较
- clang格式:禁用排序包含
- 显示错误输出的简单数组排序程序
- 为什么我的排序算法会更改数组值
- 试图在c++中对数字列表进行排序
- 如何在C++中对数组进行冒泡排序