一、set简介
C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构操作。vector封装数组,list封装了链表,map和set封装了二叉树,pair封装二元结构体等,在封装这些数据结构的时候,STL按照程序员的使用习惯,以成员函数方式提供的常用操作,如:插入、排序、删除、查找等。让用户在STL使用过程中,并不会感到陌生。
set关联式容器,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。
应该注意的是set中数元素的值不能直接被改变。C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。
其实map和set是一个爸爸 - 红黑树爸爸,只不过set力气不够,不像map那么可以拥有一个主键(key)和实键(value).它只有一个键值,并且set当中不能存储相同的键值(set还具有强迫症). 其实map和set的区别差不多就完了. 他们的相同点,他们底层都是使用红黑树构造的。这使得他们的查找,插入,删除的效率都非常的高. 并且他们都是有序的,这些都非常关键! 这是一个好的容器的标准.当然set的禁忌也和map一样,set不能存储无法比较大小的数据. 因为构建红黑树需要比较大小. 而且在set中存储相同的键值 ,新的就会覆盖久的.set的特性是,所有元素都会根据元素的键值自动被排序. 我们可以通过set的迭代器改变set的元素值吗? 不行,因为set元素值就是其键值,关系到set元素的排列规则,如果任意改变set元素值,会严重破坏set组织. set的源代码之中看到,set<T>::iterator被定义为底层RB_tree的const_iterator,杜绝写入操作. 换句话说,set iterator是一种constant iterator(相对于mutable iterator).set拥有与list相同的某些性质: 当客户端对它进行元素新增操作或删除操作时,操作之前的所有迭代器,在操作完成之后都依然有效,当然
那个被删除元素迭代器必然是个例外.
关于set有下面几个问题:
(1)为何map和set的插入删除效率比用其他序列容器高?
大部分人说,很简单,因为对于关联容器来说,不需要做内存拷贝和内存移动。说对了,确实如此。set容器内所有元素都是以节点的方式来存储,其节点结构和链表差不多,指向父节点和子节点。结构图可能如下:
A
/ \
B C
/ \ / \
D E F G
因此插入的时候只需要稍做变换,把节点的指针指向新的节点就可以了。删除的时候类似,稍做变换后把指向删除节点的指针指向其他节点也OK了。这里的一切操作就是指针换来换去,和内存移动没有关系。
(2)为何每次insert之后,以前保存的iterator不会失效?
iterator这里就相当于指向节点的指针,内存没有变,指向内存的指针怎么会失效呢(当然被删除的那个元素本身已经失效了)。相对于vector来说,每一次删除和插入,指针都有可能失效,调用push_back在尾部插入也是如此。因为为了保证内部数据的连续存放,iterator指向的那块内存在删除和插入过程中可能已经被其他内存覆盖或者内存已经被释放了。即使时push_back的时候,容器内部空间可能不够,需要一块新的更大的内存,只有把以前的内存释放,申请新的更大的内存,复制已有的数据元素到新的内存,最后把需要插入的元素放到最后,那么以前的内存指针自然就不可用了。特别时在和find等算法在一起使用的时候,牢记这个原则:不要使用过期的iterator。
(3)当数据元素增多时,set的插入和搜索速度变化如何?
如果你知道log2的关系你应该就彻底了解这个答案。在set中查找是使用二分查找,也就是说,如果有16个元素,最多需要比较4次就能找到结果,有32个元素,最多比较5次。那么有10000个呢?最多比较的次数为log10000,最多为14次,如果是20000个元素呢?最多不过15次。看见了吧,当数据量增大一倍的时候,搜索次数只不过多了1次,多了1/14的搜索时间而已。你明白这个道理后,就可以安心往里面放入元素了。
二、set成员函数列表:
begin 返回一个迭代器,此迭代器指向set中的第一个元素。
cbegin 返回一个常量迭代器,此迭代器指向set中的第一个元素。
cend 返回一个迭代器,此迭代器指向set最后一个元素的下一个位置
clear 清除set的所有元素。
crbegin 返回一个常量迭代器,此迭代器指向反向set中的第一个元素。
crbegin 返回一个常量迭代器,此迭代器指向反向set中的第一个元素。
crend 返回一个常量迭代器,此迭代器指向反向set中最后一个元素之后的位置。
emplace 将就地构造的元素插入到set。
emplace_hint 将就地构造的元素插入到set,附带位置提示。
empty 如果set为空,则返回 true。
end 返回一个迭代器,此迭代器指向set最后一个元素的下一个位置
erase 从指定位置移除set中的元素或元素范围。
find 返回一个迭代器,此迭代器指向set中其键与指定键相等的元素的位置。
get_allocator 返回集合中与给定值相等的上下限的两个迭