【问题标题】:Keep smallest n elements in set, discard others保留集合中最小的 n 个元素,丢弃其他元素
【发布时间】:2015-01-15 04:35:06
【问题描述】:

我有一个 C++ STL 集。

set<unsigned> baseSet;

我在里面插入了一堆数字。

for(auto incoming : it->second.in) {
    baseSet.insert(incoming);
}

现在我只想要集合中最小的 n 个元素。 (如果有帮助,我可以将集合的比较器切换到 std::greater。)集合似乎没有像向量这样的调整大小功能。我怎样才能缩短它?就地会很棒,我肯定想利用集合已排序的事实,而不是再次进行所有排序。

【问题讨论】:

  • std::set 等基于节点的容器用于特殊用途,您通常不需要也不应该使用它们。使用a vector and nth_element 可能会更好。
  • 与向量不同,集合是专门为您不想要重复元素的情况制作的,这正是我的应用程序所需要的。
  • 即便如此,向量可能更好。只需使用std::unique。请参阅 Why you shouldn't use set (and what you should use instead)Why you should avoid Linked Lists。 (第二个链接指的是链表,但该信息也可以应用于 std::set。)
  • @bames53 的观点是构造集合具有复杂性O(N lg K),其中N 是插入的总数,K 是不同元素的数量。将所有内容都放入一个向量中,然后将 sortuniqueresize 转换为 n 具有复杂性 O(N lg N) 但更有利的常数因子。如果K 没有明显小于N,或者N 不是“大”,则使用向量几乎肯定会更快。

标签: c++ c++11 stl set


【解决方案1】:

选项 1

if(baseSet.size() > n) {  // Use appropriate case if necessary
  auto it = base.begin();  // std::set<unsigned>::iterator
  std::advance (it, n);
  baseSet.erase(it, baseSet.end());
}

找到第 nth 个最小元素并擦除从该元素到末尾的范围。

选项 2

以后,你也可以这样做(感谢bames53 的评论)

baseSet.erase(next(begin(baseSet), n), end(baseSet));

选项 3

另一个选项(不太推荐)是在设置达到n 的大小后继续删除元素。

for(auto incoming : it->second.in) {
    baseSet.insert(incoming);  // Insert conditionally if you bother about efficiency
    if(baseSet.size() > n) baseSet.erase(baseSet.rbegin());  // rbegin not to be confused with begin
}

【讨论】:

  • 要增加迭代器一定次数,您可以使用next(it, n) (C++11)。 baseSet.erase(next(begin(baseSet), n), end(baseSet));
猜你喜欢
  • 2023-01-30
  • 1970-01-01
  • 2019-06-21
  • 2017-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多