我有一个班级
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;
我使用带字符串的运算符进行二进制搜索和运算符&lt;与Zaposlenik进行分类
我无法更改头文件类我只能在.cpp中编写代码。
我也有
class Firma {
private:
vector<Zaposlenik> zaposlenici;
public:
void sort();
我也不能改变那个类,我必须为它编写.cpp。 我将2 .cpp上传到自动评分服务器,该服务器将500 000 Zaposlenik输入到矢量zaposlenici,然后进行2 000 000次搜索。
我使用qsort和bsearch而且速度太慢了。我上传时不能超过3秒。
我写了重载的运算符,我相信它们很好,但显然qsort可以更快。
Vector按字符串prezime排序,名称从“aaaa”到“ZZZZ”,所以4个字母组合大小写字母。
string funkcija;
和double placa;
对排序没有任何意义。
有人能告诉我哪种比qsort快吗?请记住,我对主要内容没有任何控制权,因此我无法统计成员。
P.S。类中还有其他函数,但它们对此部分没有任何意义。 还有Bsearch的功能,但这个功能和我相信的一样快。
答案 0 :(得分:9)
三件事:
使用std::sort
代替std::qsort
,它更快,因为它可以内联对比较运算符的调用(如果您在标题中定义它或启用链接时优化)。
Override swap
为您的班级,以便它可以有效地交换,而不是通过临时变量复制。但是,这需要更改标头(因为您需要访问私有变量)。
由于您的排序字符串具有固定长度4,因此不同的排序算法将是有益的。经典的选择,相当容易实现,是radix sort。从你的一些评论来看,你的教授似乎希望你实现这一点。
答案 1 :(得分:3)
自动评分服务器,将500 000 Zaposlenik输入到矢量zaposlenici,然后进行2 000 000次搜索。我使用qsort和bsearch而且速度太慢了。
为了澄清一下,你没有在每次打电话给bsearch之前打电话给qsort,对吧?因为如果列表已经排序,则二进制搜索速度很快。如果您在每次搜索之前对列表进行排序,那么您的表现就会非常糟糕。
鉴于您列出的约束(所有字符串长度为四个字符),我只测试了std::sort
与自定义存储桶排序,而在一百万个元素上,存储桶排序速度提高了8倍。提示:四个字符的字符串可以用4 * 6 = 24位编码,因此您需要16.777.216个桶进行计数。
答案 2 :(得分:2)
有几件事需要考虑。首先和
最重要的是,无法在qsort
上使用std::vector<Zaposlenik>
。
qsort
使用memcpy
在交换时复制对象,以及
memcpy
仅适用于具有普通副本的对象。使用qsort
在这种情况下可能会更快,因为它没有正确复制
对象。您必须使用std::sort
;没有别的办法。
做到这一点:速度(或者至少是你可以参加的派对
({1}}的影响力取决于两件事:速度
你的比较,以及交换的速度。你没有表现出什么
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 );
}
。为了确保std::string
使用它,您
还应该提供一个重载的自由函数std::sort
(在
与swap
)相同的命名空间,它会调用您的成员
功能:
Zaposlenik
原因是:void
swap( Zaposlenik& lhs, Zaposlenik& rhs )
{
lhs.swap( rhs );
}
调用自由函数std::sort
。
答案 3 :(得分:1)
这里的问题实际上是你对类头的限制。我怀疑瓶颈是排序时的交换操作还是词法字符串比较(或可能两者)。如果您根本无法更改类定义,那么改进它将会很棘手,因为您必须在实现中添加大量辅助代码,并且一切都变得比以前更加复杂。
无论如何,这是我建议的方法:由于你是基于字符串进行排序,实现自己的Trie的专用版本,在按字典顺序排序序列时,你无法击败Trie的性能。您可以在.cpp文件中完全实现此数据结构,并使用Firma::sort
方法对其进行实例化。
由于您似乎专注于速度,您可能愿意在内存消耗方面进行权衡。因此,您将Treap中的每个节点实现为std::vector<std::shared_ptr<Trie>>
,其初始化为256(所有插槽初始化为nullptr
)或std::array<std::shared_ptr<Trie>,256>
。
您现在基本上将每个字符串插入到数据结构中,然后再次将它们全部读出。这种方法在所有字符串的总大小上是线性的(因而是最优的)。
旁注:
注意,当遍历Trie时(即,当写Firma::zaposlenici
成员时),每个节点处的256时隙表产生256的常数因子。如果您正在处理ASCII,可以将表大小减小到128或将单个字节分成半字节,从而产生2 * 16而不是256的开销。
编辑:如果您知道只会遇到来自a..z和A..Z的字符,那么您的基本字母大小为2 * 26 = 52而不是256.所以你的Trie的每个节点中的查找表只需要大小为52(即每个节点最多可以有52个子节点)。