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::set
和 std::map
都是关联容器。不同之处在于std::set
s 只包含键,而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++ 中,set
和 map
在任何时候都是有序的。如果您只想要唯一性而不关心顺序,则可以使用unordered_set
和unordered_map
。【参考方案4】:
vector
在容器后面的插入和删除更快。您可以通过运算符 [] 访问元素。
dequeue
与vector
类似,但具有前面插入和删除的功能。
set
仅具有密钥,而 map
具有 pair
。这两个容器在容器中间插入和删除都更快。您还可以使用 STL 算法通过查找来访问元素。
【讨论】:
【参考方案5】:这归结为您的应用程序最需要的复杂性保证,包括插入、删除、检索等。我强烈推荐 Scott Meyers 的 Effective STL。
【讨论】:
以上是关于std::set 与向量或映射的优势的主要内容,如果未能解决你的问题,请参考以下文章
将两个 std::vector<cv::Point> 向量和安全公共点与第三个 std::vector<cv::Point> 进行比较