unordered_set底层原理

本文不介绍unordered_map容器的使用,只讲它的底层实现!!!

C++ STL 标准库中,不仅是 unordered_set 容器,所有无序容器的底层实现都采用的是哈希表存储结构(哈希表结构默认大家都会哈)。更准确地说,是用“链地址法”(又称“开链法”)解决数据存储位置发生冲突的哈希表,整个存储结构如图 1 所示。

可以看到,当使用无序容器存储键值对时,会先申请一整块连续的存储空间,但此空间并不用来直接存储键值对,而是存储各个链表的头指针,各键值对真正的存储位置是各个链表的节点。

注意,STL 标准库通常选用 vector 容器存储各个链表的头指针。

不仅如此,在 C++ STL 标准库中,将图 1 中的各个链表称为桶(bucket),每个桶都有自己的编号(从 0 开始)。当有新键值对存储到无序容器中时,整个存储过程分为如下几步:

  1. 将该键值对中键的值带入设计好的哈希函数,会得到一个哈希值(一个整数,用 H 表示);
  2. 将 H 和无序容器拥有桶的数量 n 做整除运算(即 H % n),该结果即表示应将此键值对存储到的桶的编号;
  3. 建立一个新节点存储此键值对,同时将该节点链接到相应编号的桶上。

另外值得一提的是,哈希表存储结构还有一个重要的属性,称为负载因子

负载因子 = 容器存储的总键值对 / 桶数

 默认情况下,无序容器的最大负载因子为 1.0。如果操作无序容器过程中,使得最大复杂因子超过了默认值,则容器会自动增加桶数,并重新进行哈希,以此来减小负载因子的值。需要注意的是,此过程会导致容器迭代器失效,但指向单个键值对的引用或者指针仍然有效。

 接下来我们来看一个例子:

#include <bits/stdc++.h>
using namespace std;
int main(){
    unordered_set<int> s;
    for(int i=0; i<4;i++){
        s.insert(i);
    }
    for(auto it=s.begin();it!=s.end();it++){
        cout<<*it<<" ";
    }

    return 0;
}

打印结果:

 

我们来看桶的数量:

cout<<s.bucket_count();

打印结果: 

 

可以看到,桶的数量是5个,如果我们讲上面的代码更改为插入5个元素,即将

    for(int i=0; i<4;i++){
        s.insert(i);
    }

 改为

    for(int i=0; i<5;i++){
        s.insert(i);
    }

各位看效果:

输出顺序变为4 0 1 2 3,而桶的数量也变为了11。

由此可以看出,当负载因子增大时,unordered_set的桶的数量会增加,进行重新哈希。

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值