从容器中获取前 5 个算法?
Posted
技术标签:
【中文标题】从容器中获取前 5 个算法?【英文标题】:Get top 5 algorithm from a container? 【发布时间】:2011-08-16 07:20:40 【问题描述】:我有一个类(对象),用户。该用户有 2 个私有属性,“姓名”和“人气”。我将对象存储到向量(容器)中。
从容器中,我需要找到前 5 个最受欢迎的用户,我该怎么做? (我有一个丑陋的代码,我会在这里发布,如果您有更好的方法,请告诉我。随意使用其他容器,如果您认为矢量不是一个好的选择,但请仅使用:map 或 multimap,列表、向量或数组,因为我只知道如何使用这些。)我目前的代码是:
int top5 = 0, top4 = 0, top3 = 0, top2 = 0, top1 = 0;
vector<User>::iterator it;
for (it = user.begin(); it != user.end(); ++it)
if( it->getPopularity() > top5)
if(it->getPopularity() > top4)
if(it->getPopularity() > top3)
if(it->getPopularity() > top2)
if(it->getPopularity() > top1)
top1 = it->getPopularity();
continue;
else
top2 = it->getPopularity();
continue;
else
top3 = it->getPopularity();
continue;
else
top4 = it->getPopularity();
continue;
else
top5 = it->getPopularity();
continue;
我知道代码很丑陋并且可能容易出错,因此如果您有更好的代码,请与我们分享(我们 == cpp 新手)。谢谢
【问题讨论】:
如果你不知道一些容器对这个任务有好处,我认为这是一个学习它的好机会,不要忽视它。 您的列表有多长?它必须有多快?对整个列表进行排序是否足够快? @svick:你会推荐什么?我不知道它将有多少个对象,但应该在 10000-40000 左右。前几天我发了关于容器的问题,你可以在这里查看:***.com/questions/7021529/dynamic-size-of-array-in-c。有人说 James Kanze 使用矢量直到它不起作用,因此我选择了矢量。 这是作业吗?如果不是(或者如果它是家庭作业并且您被允许使用它),您应该查看<algorithm>
标头中可用的例程。
@Michael:是的,这是一个家庭作业。但我自己做所有的代码。只有这部分代码,我觉得是错误的,或者应该说,不是很整洁,因此我想探索一种更优雅的方式。
【参考方案1】:
您可以使用std::partial_sort
算法对向量进行排序,以便前五个元素已排序,其余元素保持未排序。像这样的东西(未经测试的代码):
bool compareByPopularity( User a, User b )
return a.GetPopularity() > b.GetPopularity();
vector<Users> getMostPopularUsers( const vector<User> &users, int num )
if ( users.size() <= num )
sort( users.begin(), users.end(), compareByPopularity );
else
partial_sort( users.begin(), users.begin() + num, users.end(),
compareByPopularity );
return vector<Users>( users.begin(), users.begin() + num );
【讨论】:
+1 用于使用std::partial_sort
。但是,这可以通过考虑num
是否大于users.size()/2
来进一步改进。我的意思是,想象一下,如果num = users.size()-1
,那么partial_sort
对users.size()-1
项目进行排序,尽管如果它只对一个1
项目进行排序,但使用不同的比较函数,结果将是相同的。
@Frerich:嗨,您是说“前五个元素已排序,其余元素未排序”?我无法理解。是不是这样:比较所有对象的值,排序前 5 个,然后前 5 个对象可能会改变,但其余对象仍然存在?
@Frerich:与Itsik解决方案相比,它使用sort对整个向量进行排序,哪个更好?在性能方面?
@Frerich:只要您对特殊情况 users.size() <= num
进行完整排序,就会 +1。 (不能保证users[0]
是第一选择。)
@celta:对于像你这样的向量大小(10000-40000),任何性能差异都可能微不足道(免责声明:我没有分析它)。但是,我认为 partial_sort
在道德方面更胜一筹:它更清楚地传达了代码的意图(至少对我而言,您的里程可能会有所不同)。【参考方案2】:
您为什么不根据受欢迎程度对向量进行排序(std::sort
或您自己的快速排序实现)并取前 5 个值?
例子:
bool UserCompare(User a, User b) return a.getPopularity() > b.getPopularity();
...
std::sort(user.begin(), user.end(), UserCompare);
// Print first 5 users
【讨论】:
对不起,我是新手,需要澄清一些事情。我阅读了有关 std::sort 的信息,但不知道可以应用于对象。根据您的代码,我的向量将按降序排序,因此我只需从向量中循环 5 次即可获得前 5 个值,而前 5 个值是最受欢迎的前 5 个用户? @celta 你是对的,向量会降序,前 5 个值是最受欢迎的 5 个【参考方案3】:如果你只想要前 5 个流行的用途,那么使用 std::partial_sort()。
class User
private:
string name_m;
int popularity_m;
public:
User(const string& name, int popularity) : name_m(name), popularity_m(popularity)
friend ostream& operator<<(ostream& os, const User& user)
return os << "name:" << user.name_m << "|popularity:" << user.popularity_m << "\n";
return os;
int Popularity() const
return popularity_m;
;
bool Compare(const User& lhs, const User& rhs)
return lhs.Popularity() > rhs.Popularity();
int main()
// c++0x. ignore if you don't want it.
auto compare = [](const User& lhs, const User& rhs) -> bool
return lhs.Popularity() > rhs.Popularity(); ;
partial_sort(users.begin(), users.begin() + 5, users.end(), Compare);
copy(users.begin(), users.begin() + 5, ostream_iterator<User>(std::cout, "\n"));
【讨论】:
@J-16: LOL,你是STL的忠实粉丝:D @jagansai 哎呀,对不起。刚刚添加【参考方案4】:首先,缓存 it->getPopularity()
,这样您就不必重复它了。
其次(这一点更为重要):您的算法存在缺陷。当您找到新的top1
时,您必须先将旧的top1
推到#2 插槽,然后再保存新的top1
,但在此之前,您必须将旧的top2
推到# 3 个插槽等。这仅适用于新的top1
。您将不得不为新的top2
、新的top3
等执行类似的操作。唯一可以粘贴而不用担心将内容推到列表中的内容是当您获得新的top5
时。正确的算法是有毛的。也就是说,当您的 topN
是一个数组而不是一堆单独的值时,正确的算法更容易实现。
第三点(这甚至比第二点更重要):您不应该关心性能,至少最初不应该如此。做到这一点的简单方法是对整个列表进行排序,然后从顶部取出前五个。如果这个次优但简单的算法不会影响你的表现,那就完成。除非性能要求您将简单的解决方案扔出窗外,否则不要为丑陋但快速的 first N 算法烦恼。
最后(这是最重要的一点):只有当列表中的元素数量远远大于 5 时,这种快速的 first N 算法才很快。默认的排序算法非常快。在下推优先 N 算法变得有利之前,必须浪费大量时间对您不关心的数十/数百个项目进行排序。换句话说,下推插入排序算法很可能是过早失优化的情况。
【讨论】:
@大卫:谢谢你的建议。对于 #1 提示,“缓存”,我该怎么做?当我将来遇到类似情况时,提示#2和#3对我来说非常重要。又学到东西了,谢谢。 "Cache":放入局部变量。此外,最后一点非常重要。避免过早优化。最后一点,这比我已经提出的任何一点都更重要:看看是否有人还没有为你解决确切的问题,这永远不会有什么坏处。在这种情况下,正确的解决方案是std::partial_sort
中 C++ 库的一部分。
哎呀,对不起,我错过了那个。先生,我一定会记下来的。【参考方案5】:
对您的对象进行排序,如果允许,可以使用库,然后只需选择前 5 个元素。如果您的容器变得太大,您可能会使用 std::list 来完成这项工作。
编辑:@itsik 你打败了我 :)
【讨论】:
【参考方案6】:执行这个伪代码。
Declare top5 as an array of int[5] // or use a min-heap
Initialize top5 as 5 -INF
For each element A
if A < top5[4] // or A < root-of-top5
Remove top5[4] from top5 // or pop min element from heap
Insert A to top // or insert A to the heap
【讨论】:
【参考方案7】:好吧,我建议你通过使用数组或列表或向量来存储前五个来改进你的代码,就像这样
struct TopRecord
int index;
int pop;
Top5[5];
for(int i = 0; i<5; i++)
Top5[i].index = -1;
// Set pop to a value low enough
Top5[i].pop = -1;
for(int i = 0; i< users.size(); i++)
int currentpop = i->getPopularity()
int currentindex = i;
int j = 0;
int temp;
while(j < 5 && Top5[j].pop < currentpop)
temp = Top5[j].pop;
Top[j].pop = currentpop;
currentpop = temp;
temp = Top5[j].index;
Top[j].index = currentindex;
currentindex = temp;
j++;
【讨论】:
这不必对所有用户数组进行排序。【参考方案8】:如果您的目标是性能,您也可以考虑使用随机选择,因为最初随机选择对于有序统计来说已经足够好并且在线性时间内运行,您只需要运行 5 次。或者使用上面提供的 partial_sort 解决方案,无论哪种方式都很重要,取决于您的目标。
【讨论】:
@康斯坦丁:嘿,谢谢。我将检查随机选择。又学到了新东西,谢谢:D以上是关于从容器中获取前 5 个算法?的主要内容,如果未能解决你的问题,请参考以下文章
使用 LINQ 通过一个查询从数组中获取前五个元素和后五个元素