2019独角兽企业重金招聘Python工程师标准>>>

序.

数组是最常用的以常数时间插入、存取的一种数据结构。但是数组的索引值只能为正整数,当然对于字符串的处理,我们可以写一个映射函数,将字符串映射为一个index。但是,这样做会不切实际的导致数组的空间急剧增长,内存还是挺吃紧的,就自然不愿意这样干了。

站在巨人的肩膀上,推出了散列表。用武之地相当的广!核心思想是:将大数映射为小数,这样不仅继承了数组的访问、存取效率,还缩减了空间。当然,事物并非完美,散列表也存在瑕疵,但是我们已经在极力的权衡效率。

写在推敲之前

1.散列表元素的插入

散列表的插入很简单,通过映射函数(散列函数)将待存之物(这里没有写整数,因为我们也可以对字符串这样的类型操作)映射为hash key。这就不可避免的导致可能映射为同一个key(出现了碰撞)。

2.散列表元素的删除

至于散列表元素的删除,采用了惰性删除,何为惰性删除?——先将待删除的元素所占的位置给腾出来(不要占着坑嘛^-^),我们对它做一个删除记号,不要到时找不着。如果下次有相同key元素被插入,我们就可以直接把这个位置给它,与nginx里的如出一辙啊。如果我们进行了大量元素插入,进行了表格的重新整理(reshaping),此时在对它进行真正删除,因为在散列表中每一个元素不是独立的,对其他元素的位置是有影响的。概括一下删除:置空,贴标签,后续处理!

3.碰撞处理(也是散列表的性能关键之处)

在元素插入时我们提到了会产生碰撞,下面简述一些经典的解决碰撞的技术。

名词:负载系数loading factor = 元素个数/表格大小

a.线性探测

假设散列函数为key=f(i),常见的f(i)=i%N;此时对j进行散列时有f(i)=f(j),那么进行线性探测:key=f(j)+f(k),k=1,2,3…。

b.二次探测

二次探测就是将一次探测的k换成k^2。为什么这么做?因为在一次探测中,会不可避免的出现有一大块碰撞的表格,待下次进行插入时,我们需要先爬过这些沼泽地,最后才可能击中目标,如此重复,沼泽地面积越来越大,以后的过程更为艰难。这片沼泽地的术语为:主集团(primary clustering)。而二次探测是为缓解这个问题诞生的,但不是完美的解决方案。

c.开链法

开链法维护为每一个碰撞的槽位维护了一个链表。相同的的key被填入该槽位的链表中,(以头插方式维护)。

d.其他处理方法

推敲STL散列表hashtable

  • hashtable数据结构

STL是以开链法解决散列的碰撞的。先让我们hashtable的内部基本构成:

1.桶子(buckets)

这样的叫法我不经常见。至少对我来说比较新鲜。桶可以纳物,数组的特性相似。对于出现碰撞的桶子会维护一个桶子链表bucket list(开链了^-^)。

2.节点(nodes)

此处的节点是链表中的节点:(单链表结构)

template<class Value>

struct __hashtable_node{

__hashtable_node* next; //指向下一节点

Value val;

};

3.buckets底层是以vector实现的,因为其具有动态扩充能力

4.以素数来设计表格大小

static const unsigned long __stl_prime_list[__stl_num_primes] =
{  53,         97,           193,         389,       769,  1543,       3079,         6151,        12289,     24593,  49157,      98317,        196613,      393241,    786433,  1572869,    3145739,      6291469,     12582917,  25165843,  50331653,   100663319,    201326611,   402653189, 805306457,  1610612741, 3221225473ul, 4294967291ul
};

STL建了一个素数数组,假设插入元素个数为n,函数__stl_next_prime(n)会返回最接近并大于等于n的那个素数。即为bucket size

  • hashtable内存管理

内存管理重点谈谈函数resize的设计,因为它伴随着散列的每一次插入操作。

// 调整hashtable的容量

template <class V, class K, class HF, class Ex, class Eq, class A>
void  hashtable<V, K, HF, Ex, Eq, A>::resize(size_type num_elements_hint)
{  const size_type old_n = buckets.size();  // 如果新调整的大小当前大小才进行调整  if (num_elements_hint > old_n) {  const size_type n = next_size(num_elements_hint);  // 如果已经到达hashtable的容量的极限, 那么也不进行更改  if (n > old_n) {  // 建立新的线性表来扩充容量  vector<node*, A>  tmp(n, (node*) 0);  __STL_TRY {  // 重新进行元素的散列操作!for (size_type bucket = 0; bucket < old_n; ++bucket) {  node* first = buckets[bucket];  //每一个bucket聚合物while (first) {  size_type new_bucket = bkt_num(first->val, n);  //新的散列keybuckets[bucket] = first->next;  first->next = tmp[new_bucket]; //进行头插操作 tmp[new_bucket] = first;  first = buckets[bucket]; //已经指向下一个node }  }  buckets.swap(tmp);  }  }  }
}
  • 衍生容器set(hash_set)

区别于基于红黑树实现的set,hash_set没有自动排序机制。操作与set类似。此处不作赘述。

  • 衍生容器map(hash_map)

区别于基于红黑树实现的map,hash_map没有自动排序机制。操作与map类似。此处不作赘述。

http://my.oschina.net/stone8oy

转载于:https://my.oschina.net/stone8oy/blog/284893

STL札记3-2(hashtable关联容器set、map)相关推荐

  1. Effective STL 为包含指针的关联容器指定比较类型

    // 为包含指针的关联容器指定比较类型.cpp : 定义控制台应用程序的入口点. //#include "stdafx.h" #include <set> #inclu ...

  2. C++ 关联容器set | map | multiset | multimap

    前情提要 根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构.树型结构的关联式容器主要有四种:map.set.multimap.multiset.这四种容器的共同点是:使 ...

  3. C++ ——一文读懂:关联容器

    文章目录 使用关联容器 关联容器概述 定义关联式容器 pair类型 关联容器操作 关联容器迭代器 添加元素 删除元素 map的下标操作 访问元素 无序容器 管理桶 标准库提供8个关联容器: 类型map ...

  4. C++知识点30——使用C++标准库(关联容器map及其初始化,赋值,查找,添加,删除与迭代器失效)

    一.关联容器简介 关于顺序容器和关联容器的区别已经在博客https://blog.csdn.net/Master_Cui/article/details/107427911中提过 C++标准库中的关联 ...

  5. 第十二篇:实用的关联容器

    前言 我们可以用下标访问顺序容器的元素,也就是说在顺序容器实现中下标和元素的值相关联.那么能不能让别的值(而不是下标)与元素的值相关联呢?有的,实现这种功能的容器就叫做关联容器,而关联的本质就是某个特 ...

  6. 《C++ Primer 5th》笔记(11 / 19):关联容器

    文章目录 使用关联容器 使用map 使用set 关联容器概述 定义关联容器 初始化multimap 或 multiset 关键字类型的要求 有序容器的关键字类型 使用关键字类型的比较函数 pair类型 ...

  7. C++ primer 第11章 关联容器

    文章目录 使用关联容器 map示例 关联容器概述 定义关联容器 关联容器值初始化 multimap和multiset 关键字类型的要求 pair类型 pair上的操作 关联容器操作 关联容器额外的类型 ...

  8. C++关联容器总结一

    关联容器 关联容器: 通过键(key)储存与读取元素 顺序容器: 通过元素在容器中的位置顺序储存,访问 关联容器类型 map 关联数组:元素通过键来存储和读取 set 大小可变的集合,支持通过键实现的 ...

  9. C++ primer 4th 第10章《关联容器》总结

    表10-1 关联容器类型 map 关联数组:元素通过键来存储和读取 set 大小可变的集合,支持通过键实现的快速读取 multimap 支持同一个键多次出现的map类型 multimap 支持同一个键 ...

最新文章

  1. html5 网页游戏 开源,HTML5 网页游戏,基于 WebGL 打造
  2. HP-UX B.11.31从安装到VG配置
  3. 神器面世:让你快速在 iOS 设备上安装 Windows、Linux 等操作系统!
  4. java 反射 速度_Java反射,但速度更快
  5. 起点计算机网,《零起点计算机》网第5课.pdf
  6. assets bitmap 转_图片文件和Bitmap之间的转换
  7. UITextField 输入结束后的收起小键盘的方式
  8. 【C/C++开发】【VS开发】win32位与x64位下各类型长度对比
  9. Android OpenGL ES(十二):三维坐标系及坐标变换初步 .
  10. linux下如何部署php,linux如何部署php
  11. 国科大学习资料--操作系统(杨力祥)--2015年思考题(含解答)
  12. JavaWeb公交调度系统的设计与实现
  13. 联通pt952g 光猫管理员密码获取
  14. 微信小程序 绑定手机号获取验证码
  15. 使用python代码调用三汇语音卡硬件拨打电话
  16. Codeforces1548 D1. Gregor and the Odd Cows (Easy)(皮克公式+gcd+数学推导)
  17. Congestion 问题怎么解决?
  18. 小型企业、初创企业海外众筹指南
  19. 概念模型、逻辑模型和物理模型的区别
  20. python跳一跳脚本详解_跳一跳 python脚本 改进版

热门文章

  1. Permission denied error: unable to index file .vs/Trip2015/v15/Server/sqlite3/db.lock fatal: adding
  2. nacos enablediscoveryclient_Nacos入门指南03 服务发现实践
  3. mysql 去重 根据id_mycat数据库集群系列之mysql主从同步设置
  4. linux下kvm设备配置,Linux下为KVM 配置桥接设备
  5. docker hub 国内镜像_Mac设置docker国内镜像源
  6. 柱形图无数据可选中_这种漂亮的“连体”柱形图,99%的人不会做!
  7. python如何安装pip3_如何在安装pip3以及第三方python库
  8. ontological 词根词缀_英语中最常见的词缀(一)之 re
  9. 代码大全阅读笔记02
  10. python -- leetcode 刷题之路