STL关联式容器——set和map

“说话的方式简单点!”
set和map,面试中常被问及的两个容器。虽然看过多变,但没被问及,依旧千头万绪,不知从何说起。在此,我想尽可能简单通俗的再记录下这部分知识,算是总结和二次学习。

STL容器是什么就不说了,所谓容器就是用来放东西的,只不过这里的东西是指各种数据结构。STL中的容器大概可以分为两种,一种是序列式容器(线性的数据存放),一种是关联式容器(非线性的二叉树结构)。而关联式容器最具代表性的便是set和map,所以他们在面试中总是一起出现。

先说必须要知道的重点:set和map底层的实现(就是存放数据的样子)都是红黑树,之所以如此,是因为树型结构方便查找。(全文终。。。)

红黑树

那么先记住什么是红黑树。红黑树是一种(平衡)二叉查找树,所谓“红”和“黑”指的是节点的颜色,就是说树中的节点,除了存储了节点的值以外,还有一个存储位用来记录这个节点是红的还是黑的。(至于节点红色黑色到底是什么意思?红黑树又有哪些性质?你们还是自己去专门学习吧,毕竟这是一篇科普文。我建议学习过程是:二叉树->搜索二叉树->平衡二叉树->2-3树(这很关键,算了贴篇文章吧)->红黑树)。
而红黑树的操作无非是节点的插入、删除和查找,这很复杂而且记不住而且用不到(对我来说),所以不管了,反正重点就是不管你怎么插入、删除,树要保持平衡。由于RB-tree的各种操作时常需要上溯到其父节点,所以节点数据结构中要存一个指向父节点的指针。
总结一下,红黑树的节点数据结构看起来应该是这个样子:有红黑二色,有左右子节点,有父节点。

typedef bool _rb_tree_color_type;
const _rb_tree_color_type _rb_tree_red = false;     //红色为0
const _rb_tree_color_type _rb_tree_black = true;    //黑色为1

class _rb_tree_node{
    typedef _rb_tree_color_type color_type;

    color_type color;           //节点颜色
    _rb_tree_node*  parent;     //RB树的许多操作必须知道父节点
    _rb_tree_node*  left;       //指向左节点
    _rb_tree_node*  right;      //指向右节点
    //......
};

set

重要的事说第二遍:set和map的底层是用红黑树实现的。
set,中文我们叫“集合”。它按照元素的键值自动排序存放,而且元素值不能重复。比如讲{1,2,3,3,4,4,5,6}序列存入一个set,等同于存序列{1,2,3,4,5,6}在存入一个元素时,如果发现set中已经有了这个元素,就不会再重复存了,multiset允许重复值
元素存入后自动排好了序,这也很好理解,因为红黑树本身就是一种二叉查找树。不过这里需要注意的是,我们不能够通过set的迭代器来改变元素的值。因为元素是拍好了序存在红黑树中的,如果你改变了一个节点的值,那么整棵树就要重新排序,这严重破坏了树的结构。
然后需要记住的一点是:当对set进行元素新增或删除操作时,迭代器并不会失效。当然被删除的那个元素除外。我们都知道迭代器的实现很复杂,但我们不管,我们就理解迭代器是一个指向容器内节点的指针。而当我们进行插入删除操作的时候,改变的不是节点的存储位置,改变的是它的三个指针(左右子女和父节点)的指向,所以迭代器指向的还是那个节点,不会失效。当然如果被指向的节点被删除了,那自另当别论。
然后在实际的应用中,关于set我们常用的方法:

#include <set>      //包含头文件
......
ietrator begin();   //返回指向第一个元素的指针(最小的元素)
ietrator end();     //指向set最后一个元素紧接着的后面位置
clear();            //删除容器中所有元素
empty();            //return begin() == end();所以set为空返回true,不空返回false;
insert();           //插入一个元素
erase();            //删除一个元素;
                    //iterator erase( const_iterator pos )可以删除一个迭代器指向的位置,返回下一个位置的迭代器;
                    //或者指定删除某个值的元素,size_type erase( const key_type& key );
                    //或删除一个范围,void erase( iterator first, iterator last );
find();             //查找某个元素

map

重要的事说第三遍:set和map的底层是用红黑树实现的.
map,映射表。与set每个元素只有一个值不同的是,map每个元素存入的值都是一对值(两个值),pair<key,value>;第一个元素是键值(按照它来排序),第二个元素是实值,键值是不允许重复的,multimap允许重复键值
哎呀,作为一篇科普文,实在是没什么好说的了。因为map就是存入一对值,其它要注意的地方都和set差不多。不能通过map的迭代器更改元素键值内容,因为关系到元素的排列。插入、删除操作不会使迭代器失效,原因同set。
硬是要多学点的话,看看map的实际使用吧:

#include <map>//头文件

//用起来像这样
map<std::string, int> mymap;  //map存一对数,使用模板的时候自然要确定两个参数类型

begin();//不多谈
end();

empty();//还是return begin()==end();

clear();//清空

inset();//这个有点复杂
//插入一个对数据("michael",23333);有多种方法
//可以用构造
mymap("michael") = 23333;
//或者使用pair
mymap.inset(pair<string,int>("michael",23333));

find(); //查找某个键值

//还有使用元素的第一个值(键值)和第二个值(实值)的方法,利用迭代器it
it->first;
it->second;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值