std::set 与向量或映射的优势

Posted

技术标签:

【中文标题】std::set 与向量或映射的优势【英文标题】:advantages of std::set vs vectors or maps 【发布时间】:2013-04-23 14:17:29 【问题描述】:

这可能是一个愚蠢的问题,我对 C++ 和一般编程很陌生。 我想了解几个 STL 容器的使用,考虑到这一点,我想知道使用 std::set 与例如使用向量或映射相比有什么优势? 我似乎找不到这个问题的明确答案。我注意到集合使用地图,但为什么不总是使用地图或总是使用集合。相反,提供了 2 个非常相似的容器。 提前致谢。

【问题讨论】:

一个std::set类似于一个没有价值的std::map,与std::vector完全无关... 你需要一些good documentation。 他们都做不同的事情。选择一个你需要解决你的问题。这就像在问为什么我们应该在厨房里放盐、黄油和糖,而不是总是用橙汁。 【参考方案1】:

std::setstd::map 都是关联容器。不同之处在于std::sets 只包含键,而std::map 中有一个关联的值。选择其中一个主要取决于手头的任务是什么。如果你想建立一个包含所有出现在文本中的单词的字典,你可以使用std::set<std::string>,但是如果你还想计算每个单词出现的次数(即将一个值与键相关联),那么你会需要std::map<std::string,int>。如果您不需要关联该计数,那么拥有不必要的 int 是没有意义的。

【讨论】:

非常感谢大家,这就是我需要知道的全部内容 如果要检查set中是否存在值,是否与检查map中是否存在键一样快? @thomthom:要求是相同的,并且大多数实现都使用相同的底层数据结构(RB-tree),所以答案是无论从理论上还是在实践中,成本都是相同的。 值得注意的是 unordered_set/map 如果您不希望所有内容都按键排序,则可以更快地访问【参考方案2】:

一个集合对于存储独特的东西很有用,比如“typeOfFruits”的枚举

std::set<typeOfFruits> fruits;   
fruits.insert (banana);
fruits.insert (apple);
fruits.insert (pineapple);

//it's fast to know if my store sells a type of fruit.
if (fruits.find (pear) == fruits.end())
 std::cout<<"i don't have pear"; 

地图可用于存储独特的事物,以及“价值”

std::map<typeOfFruits, double /*unit price*/> fruits;  
fruits[banana] = 1.05;
fruits[apple] = 0.85;
fruits[pineapple] = 3.05;
//repeating pineapple will replace the old price (value)
fruits[pineapple] = 3.35;

//it's fast to know how much a fruit costs.
std::map<typeOfFruits, double /*unit price*/> itr = fruits.find(pineapple);
if (itr != fruits.end())
 std::cout<<"pineapples costs: $" <<itr->second; 

向量对于存储序列有序的东西很有用(push_back())。 假设您在结账时扫描您的水果,程序会跟踪此扫描。

std::vector<typeOfFruits> fruits;
fruits.push_back(apple);
fruits.push_back(apple); 
fruits.push_back(apple);
fruits.push_back(banana);
fruits.push_back(banana);
fruits.push_back(pineapple);
//i scanned 3 apples, 2 bananas and 1 pineapple.

【讨论】:

if (fruits.find (pear) == fruits.end()) 也可以更简单地表示为 if ( !fruits.count(pear) ),因为 set (et al.) 只能包含任何给定值的 0 或 1,并且任何半体面的库实现者都会意识到这一点并为我们实现find() != end 方面的count()(即在找到后不会继续迭代),从而使我们不必编写冗长的代码。 (无论如何,libstdc++ 8 都是如此) !fruits.contains(pear) 也是一个选项【参考方案3】:

没有人提到std::set 实际上是不可变的。您不应更改其中任何元素的值。 std::set 不会跟踪更改,因此当您编辑其中的元素时,您会在其背后进行更改,并且可能会更改其内部顺序。这是一种危险的行为。因此,如果您想在将元素放入容器后对其进行编辑,请使用std::map。确保您使用key 进行订购,之后您需要将所有内容更改为value

【讨论】:

你在说什么排序? @Daniel,容器中元素的排序。 嗯,但是使用集合和映射(dicts)时元素的顺序不是不存在吗? 没有人提到这一点,因为这很明显。 set 通过 const_iterators 返回元素,因此有效地按值而不是引用。所以:“你不应该改变其中任何元素的值。”我们怎么可能呢?并非没有颠覆语言并因此调用未定义的行为,所以讨论如果你这样做可能会发生什么是没有意义的。 @Daniel 听起来您来自 Python 并假设 - 没有根据且危险地 - 它的术语将 1:1 转换为 C++。他们不。在 C++ 中,setmap 在任何时候都是有序的。如果您只想要唯一性而不关心顺序,则可以使用unordered_setunordered_map【参考方案4】: vector 在容器后面的插入和删除更快。您可以通过运算符 [] 访问元素。 dequeuevector 类似,但具有前面插入和删除的功能。 set 仅具有密钥,而 map 具有 pair。这两个容器在容器中间插入和删除都更快。您还可以使用 STL 算法通过查找来访问元素。

【讨论】:

【参考方案5】:

这归结为您的应用程序最需要的复杂性保证,包括插入、删除、检索等。我强烈推荐 Scott Meyers 的 Effective STL

【讨论】:

以上是关于std::set 与向量或映射的优势的主要内容,如果未能解决你的问题,请参考以下文章

C++,复制集到向量

从两个地图创建一个 set_difference 向量

向量比链表有啥优势

如何返回包含不在集合中的元素的向量的副本?

将两个 std::vector<cv::Point> 向量和安全公共点与第三个 std::vector<cv::Point> 进行比较

在 C++ 中创建指针向量或指针值映射时遇到问题