C/C++编程:STL set、map、multiset、multimap源码学习

1060 篇文章 307 订阅

set

概述

  • set的特性是:所有元素都会根据元素的key自动被排序。
  • set的元素不像map那样可以同时拥有value和key,set元素的value就是value,value就是key。
  • set不允许两个元素有相同的键值

我们可以通过set的迭代器改变set的value吗?

  • 不行,因为set的value就是其key,关系到set元素的排列规则。如果任意改变set元素值,会严重破坏set组织
  • 因此,set<T>::iterator被定义为底层RB-tree的const_iterator,拒绝写入操作。
  • 换句话说1,set iterator是一种constant iterator

set拥有和list相同的某些性质:

  • 当客户端对它进行元素inset或者erase时,操作之前的所有迭代器,在操作完成之后仍然有效。当然,被删除的那个元素的迭代器是例外

set还提供了一组set/mutiiset相关算法,包括交集、联集、差集、对称差集。

实现

由于RB-Tree是一种平衡二叉搜索树,自动排序的效果很不错,所以标准的STL set就是以RB-tree为底层机制。又由于set所开发的各种操作接口,RB-tree也都提供了,所以几乎所有的set操作行为,都只是转调用RB-tree的操作行为而已
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实例

#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <string>
#include <set>

int main(){
    int i ;
    int a[5] = {0, 1, 4, 5, 3};

    std::set<int> iset(a , a + 5);

    for (std::set<int>::const_iterator pos = iset.begin(); pos != iset.end(); ++pos)
        std::cout << *pos << "\t";
    std::cout <<"\n";

    // set::count()是C++ STL中的内置函数,它返回元素在集合中出现的次数。由于set容器仅包含唯一元素,因此只能返回1或0。
    printf("size = %d, count = %d\r\n", iset.size(), iset.count(3));
    iset.insert(3);   //没有push_back,因为是自动排序的
    printf("size = %d, count = %d\r\n", iset.size(), iset.count(3));
    iset.erase(3);
    printf("size = %d, count = %d\r\n", iset.size(), iset.count(3));
    iset.insert(3);   //没有push_back,因为是自动排序的
    printf("size = %d, count = %d\r\n", iset.size(), iset.count(3));

    std::set<int>::const_iterator pos;
    for (pos = iset.begin(); pos != iset.end(); ++pos)
        std::cout << *pos << "\t";
    std::cout <<"\n";

    pos = iset.begin();
    // 企图通过迭代器改变pos,这是不被运行的
    //*pos = 9;

    // 使用STL算法find()来找元素,可以有效运作,但是不是好方法,效率低
    pos = std::find(iset.begin(), iset.end(), 3);
    if(pos != iset.end()){
        std::cout <<"3 found\n";
    }

    //对于管理式容器,应该使用其提供的find函数来找元素,效率更高。因为std::find只是遍历查找
    pos = iset.find(3);
    if(pos != iset.end()){
        std::cout <<"3 found\n";
    }

}

在这里插入图片描述

map

概述

  • map的特性是:所有元素都会根据元素的键值自动被排序
  • map和key用相同的性质:当客户端对它进行元素insert或者erase时,操作之前的所有迭代器,在操作完成之后仍然有效。当然,被删除的那个元素的迭代器是例外。
  • map的底层机制是RB-tree,每个节点的内容都是一个pair,同时拥有value和key。pair的第一元素被视为键值,第二元素被视为value。几乎所有的map操作,都是转到RB-tree而已
  • map不允许两个元素都拥有相同的key。

pair定义如下:

在这里插入图片描述
在这里插入图片描述

可以通过map的迭代器改变其元素内容吗?

可以改变value但是不可以改变key。

  • 因为map元素的键值关系到map元素的排列规则,任意改变map元素键值将会严重破坏map组织。
  • 而value不会影响map元素的排列规则

因此,map iterator既不是一种constant iterator,也不是一种mutable iterator

实现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

insert

在这里插入图片描述
此方法直接将工作转给底层的RB-tree的insert_unique()去执行。注意返回类型一个pair,有一个迭代器和一个bool值组成。后者表示插入成功与否,成功的话前者即指向被插入的那个元素

operator[]

operator[],用法有两种,可能为左值引用(内容可以被修改)、也可以为右值引用(内容不可以被修改)。如下:
在这里插入图片描述
左值和右值都适用的关键在于,返回值采用by reference传递形式。

另外,operator[]都是先根据键值找出其实值,再做打算,如下:

在这里插入图片描述
对于上面(A)式:

  • 实现根据key和value做出一个元素,由于value布置,所以产生一个与value类型相同的临时元素替代:value_type(k, T())
  • 再将该元素插入到map里面去:insert(value_type(k, T()))
    • 插入操作返回一个pair,其第一元素是个迭代器,指向插入妥当的新元素,或者指向插入失败点(键值重复)的旧元素。
    • 此时,如果下标操作符为左值(一般表示要添加新元素),我们正好以“待填value”的元素将位置卡好
    • 如果下标操作符为右值(一般表示要根据key取其value),此时的插入元素所返回的pair的第一个元素指向键值符合的旧元素
  • 然后取插入操作返回所返回的pair的第一个元素:insert(value_type(k, T())).first
  • 其第一个元素是个迭代器,指向被插入的元素。现在,提取其值:*insert(value_type(k, T())).first,获得了一个map元素,是一个由key和value组成的pair
  • 取其第二元素,就是其value:(*insert(value_type(k, T())).first).second。注意,这个value以by reference方式传递,所以它作为左值和右值都可以

multiset

multiset的特性以及用法与set完全相同,唯一的差别在于允许key重复,因为它插入操作采用的是最底层的RB-tree的insert_equal而不是insert_unique
在这里插入图片描述
在这里插入图片描述

multimap

multimap的特性以及用法与set完全相同,唯一的差别在于允许key重复,因为它插入操作采用的是最底层的RB-tree的insert_equal而不是insert_unique

在这里插入图片描述
在这里插入图片描述

unordered_set、unordered_map、unordered_multiset、unordered_multimap学习

为什么要引入unordered_xxx

因为类似map之类的函数底层是RB-tree,它虽然在插入、删除、查找等具有对数平均时间的表现,但是这样的表现有一个前提:输入数据有足够的随机性。

而unordered_xxx底层是一个hashtable,它也具有在插入、删除、查找等具有对数平均时间的表现,而且这种表现是以统计为基础,不需依赖输入元素的随机性

hashtable

  • hashtable:底层是一个vector
  • 哈希函数:负责将一个元素映射为底层数据的某个元素上
  • 哈希碰撞:可能有不同的元素被映射到相同的位置。解决方法有:线性探测、二次探测、拉链法等。STL 中使用的是拉链法+
    在这里插入图片描述

下面是hash table的节点定义:
在这里插入图片描述

在这里插入图片描述
可以看出,bucket所维护的linked list,并不是STL的list,而是自行维护上述hash table node。

unordered_set

unordered_set与set的唯一区别:set底层是红黑树,会自动排序; unordered_set底层是哈希桶,不会自动排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值