哈希表

          表: 存储数据 key –> value;

用表来存储数据结构的困难: 查找困难。一个一个key去比较去查找,效率不高。因此有了Hash算法加快查找。

       哈希表(Hash table,也叫散列表),是根据键值(Key)而直接进行访问的数据结构。也就是说,它通过把键值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

      散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key),adr = f(key)。查找时,根据这个确定的对应关系找到给定值key的映射f(key),若查找集合中存在这个记录,则必定在f(key)的位置上。我们把这种对应关系f称为散列函数,又称为哈希函数(Hash).按这个思想,采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间称为散列表或哈希表(Hash table)。关键字对应的记录存储位置称为散列地址。

哈希表的做法其实很简单,就是把Key通过一 个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标 的数组空间里,而当使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value,如此一来,就可以充分利用到数组的定位性能进行数据定位。

除留余数法

 最常用的构造散列函数的方法。对于散列表长为m的散列函数公式为:f(key) = key mod p(p≤m)这种方法不仅可以对关键字直接取模,也可在折叠、平方取中后再取模。**散列表长为m,通常p为小于或等于表长(最好接近m)的最小质数或不包含小于20质因子的合数。

举个例子,哈希函数是H(k)=k Mod m。m=12,k=100,H(100)=4。
      而如果m=2k,那么无论k是什么,H(K)的值都是一个0和奇数,也即是说只要奇数槽和0槽被占用,其他的偶数槽都是浪费掉了。如果m=2^r,那么H(k)的值就是k的低r位(化成二进制)。这样造成的后果是某一个槽有很多的关键字。所以来说一般的m取值尽量不要接近2的整数幂,而且还要是质数。

虽然我们不希望发生冲突(同一个key有多个value),但实际上发生冲突的可能性仍是存在的。当关键字值域远大于哈希表的长度,而且事先并不知道关键字的具体取值时。冲突就难免会发生。另外,当关键字的实际取值大于哈希表的长度时,而且表中已装满了记录,如果插入一个新记录,不仅发生冲突,而且还会发生溢出。因此,处理冲突和溢出是哈希技术中的两个重要问题。一般有开放地址法、链地址法。    

链地址法:将所有关键字为同义词的记录存储在一个单链表中,这种链表叫做同义词子表,使用除留余数法,就不存在冲突的问题了,只是在链表中增加一个结点。

     

红黑树:

Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。红黑树的应用比较广泛,主要是用它来存储有序的数据,它的时间复杂度是O(lgn)(二叉树最大查找次数等于树的深度),效率非常之高。例如,Java集合中的TreeSet和TreeMap,C++ STL中的set、map,以及Linux虚拟内存的管理,都是通过红黑树去实现的。

红黑树的特性:
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶节点是黑色。 [注意:这里叶节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的,红色节点的孩子和父亲都不能是红色
(5)任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

注意:特性(3)中的叶子节点,是只为空(NIL或null)的节点。
           特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对接近平衡的二叉树,并不是一个完美平衡二叉查找树。红黑树示意图如下:

预备知识

当查找树的结构发生改变时,红黑树的条件可能被破坏。

什么情况下会破坏红黑树的规则,什么情况下不会破坏规则呢?我们举两个简单的栗子:

1.向原红黑树插入值为14的新节点:

2.向原红黑树插入值为21的新节点:

由于父节点22是红色节点,因此这种情况打破了红黑树的规则4(每个红色节点的两个子节点都是黑色),必须进行调整,使之重新符合红黑树的规则。

旋转

需要通过调整使得查找树重新满足红黑树的条件。调整可以分为两类:一类是颜色调整,即改变某个节点的颜色;另一类是结构调整,集改变检索树的结构关系。结构调整过程包含两个基本操作:左旋(Rotate Left),右旋(RotateRight)

左旋:左旋的过程是将x的右子树绕x逆时针旋转,使得x的右子树成为x的父亲,同时修改相关节点的引用。旋转之后,二叉查找树的属性仍然满足。

右旋:右旋的过程是将x的左子树绕x顺时针旋转,使得x的左子树成为x的父亲,同时修改相关节点的引用。旋转之后,二叉查找树的属性仍然满足。

变色

为了重新符合红黑树的规则,尝试把红色节点变为黑色,或者把黑色节点变为红色。下图所表示的是红黑树的一部分,需要注意节点25并非根节点。因为节点21和节点22连续出现了红色,不符合规则4,所以把节点22从红色变成黑色:

但这样并不算完,因为凭空多出的黑色节点打破了规则5,所以发生连锁反应,需要继续把节点25从黑色变成红色:

此时仍然没有结束,因为节点25和节点27又形成了两个连续的红色节点,需要继续把节点27从红色变成黑色:


我们以刚才插入节点21的情况为例:

将插入的节点着色为红色,不会违背"特性(5):从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点"!少违背一条特性,就意味着我们需要处理的情况越少。接下来,就要努力的让这棵树满足其它性质即可;满足了的话,它就又是一颗红黑树了。
       将插入节点着色为"红色"之后,不会违背"特性(5)"。那它到底会违背哪些特性呢?
       对于"特性:每个节点或者是黑色,或者是红色",显然不会违背了。因为我们已经将它涂成红色了。
       对于"特性:根节点是黑色",显然也不会违背,插入操作不会改变根节点。所以,根节点仍然是黑色。
       对于"特性:每个叶子节点(NIL)是黑色",这里的叶子节点是指的空叶子节点,插入非空节点并不会对它们造成影响。
       对于"特性:如果一个节点是红色的,则它的子节点必须是黑色的",是有可能违背的!

from:https://www.cnblogs.com/skywang12345/p/3245399.html
那接下来,想办法使之"满足特性(4)",就可以将树重新构造成红黑树了。首先,我们需要做的是变色,把节点25及其下方的节点变色:

当前节点(25)的父节点是红色,叔叔节点15是黑色,且当前节点是其父节点的右孩子

处理策略
(01) 将“父节点”作为“新的当前节点”。
(02) 以“新的当前节点”为支点进行左旋。

我们把节点13看做X,把节点17看做Y,像刚才的示意图那样进行左旋转:

由于根节点必须是黑色节点,所以需要变色,变色结果如下:

这样就结束了吗?并没有。因为其中两条路径(17 -> 8 -> 6 -> NIL)的黑色节点个数是4,其他路径的黑色节点个数是3,不符合规则5。这时候我们需要把节点13看做X,节点8看做Y,像刚才的示意图那样进行右旋转:

最后根据规则来进行变色:

如此一来,我们的红黑树变得重新符合规则。这一个例子的调整过程比较复杂,经历了如下步骤:

变色 -> 左旋转 -> 变色 -> 右旋转 -> 变色

关键要懂得红黑树自平衡调整的主体思想。

from:https://www.sohu.com/a/201923614_466939

Hash与红黑树的区别:

权衡三个因素: 查找速度, 数据量, 内存使用,可扩展性,有序性。

hash查找速度会比RB树快,而且查找速度基本和数据量大小无关,属于常数级别;而RB树的查找速度是log(n)级别。并不一定常数就比log(n) 小,因为hash还有hash函数的耗时。当元素达到一定数量级时,考虑hash。但若你对内存使用特别严格, 希望程序尽可能少消耗内存,那么hash可能会让你陷入尴尬,特别是当你的hash对象特别多时,你就更无法控制了,而且 hash的构造速度较慢。

  1. 红黑树是有序的,Hash是无序的,根据需求来选择。
  2. 红黑树占用的内存更小(仅需要为其存在的节点分配内存),而Hash事先应该分配足够的内存存储散列表,即使有些槽可能弃用
  3. 红黑树查找和删除的时间复杂度都是O(logn),Hash查找和删除的时间复杂度都是O(1)。

哈希表和红黑树的对比相关推荐

  1. 哈希表、红黑树、B树、B+树基础

    一.哈希表 也叫散列表,是根据关键码值而直接进行数据访问的数据结构.(把关键码值映射到表中一个位置来访问记录)映射函数叫做散列函数,存放记录的数组叫做散列表. 散列查找过程分为两步: (1)在存储时通 ...

  2. redis的zset为什么用调表不用红黑树

    跳表和红黑树的区别 共同点:两者插入删除,删除,查找以及迭代输出时间复杂度红黑树和跳表的时间复杂度是一样的 跳表在区间查询的时候效率是高于红黑树的,跳表进行查找O(logn)的时间复杂度定位到区间的起 ...

  3. redis的zset为什么用跳表不用红黑树

    跳表是用空间换时间,实现简单. 区间查找快.跳表可以做到O(logn) 的时间复杂度定位区间的起点,然后在原始链表中顺序往后遍历就可以了. 并发环境优势.红黑树在插入和删除的时候可能需要做一些reba ...

  4. 牛客网Java刷题知识点之数组、链表、哈希表、 红黑二叉树

    不多说,直接上干货! 首先来说一个非常形象的例子,来说明下数组和链表. 上体育课的时候,老师说:你们站一队,每个人记住自己是第几个,我喊到几,那个人就举手,这就是数组. 老师说,你们每个人记住自己前面 ...

  5. HashMap-链表与红黑树转换触发条件

    JDK1.8对HashMap进行了很多优化. 例如当一个槽位slot上的链表个数过多时,则会将链表转换为红黑树,以提高查询检索的效率. 访问节点方式:先找到节点所在的数组index索引位置,然后判断节 ...

  6. Java 集合深入理解(17):HashMap 在 JDK 1.8 后新增的红黑树结构

    点击查看 Java 集合框架深入理解 系列, - ( ゜- ゜)つロ 乾杯~ 上篇文章我们介绍了 HashMap 的主要特点和关键方法源码解读,这篇文章我们介绍 HashMap 在 JDK1.8 新增 ...

  7. 【C++ 包装器类 map】C++ 标准库(std)中的map结构 哈希表(unordered_map)和黑红树(map)教程

    目录标题 1. 哈希表(unordered_map)和黑红树(map)简介以及初始化 1.1 哈希表的基本介绍 1.1.1 哈希表初始化接口示例 1.1.2 哈希表的键值的注意事项 1.1.3 自定义 ...

  8. HashMap中的红黑树

    转载自:http://blog.csdn.net/u011240877/article/details/53358305 张拭心 读完本文你将了解到: 点击查看 Java 集合框架深入理解 系列 - ...

  9. 红黑树、B(+)树、跳表、AVL对比

    在网上学习了一些材料. 这一篇:https://www.zhihu.com/question/30527705 AVL树:最早的平衡二叉树之一.应用相对其他数据结构比较少.windows对进程地址空间 ...

最新文章

  1. Python 过程式编程与函数式编程
  2. 2019年上半年收集到的人工智能LSTM干货文章
  3. 前端jQuery插件库
  4. 数据库索引的作用和长处缺点
  5. 分布式事务理论-二阶段提交(Two-phase Commit)
  6. MySQL高级最左前缀法则
  7. 【OS学习笔记】三十一 保护模式九:页目录、页表和页三者的关系详解
  8. window.print()打印网页中指定内容
  9. php 人像识别,基于OpenCV的PHP图像人脸识别技术
  10. 官网jquery压缩版引用地址
  11. 掌握好数据分析,99%的企业都不会拒绝你
  12. netcat,nmap常用例子
  13. 数学建模与数学实验3.4习题1
  14. matlab 白色变红色
  15. 普通人千万别把打工当赚钱,打工只是赚钱的一种方法
  16. L1-033 出生年-PAT 团体程序设计天梯赛 GPLT
  17. Linux服务器上的mongodb:/lib64/libc.so.6: version `GLIBC_2.14‘ not found (required by /app/hems/mong)
  18. c语言int转换成float,int怎么转化为float 将 int型变量n转换成float型变量的方法是...
  19. TIA博途中如何使用符号方式按位,字节,字访问非结构数据类型
  20. Cooapods为iOS项目配置SnapKit等第三方框架

热门文章

  1. 为什么说在国内考CISP比CISSP要好?
  2. 渗透测试入门25之一次完整的渗透测试实验
  3. 瑞晟蓝牙来电语音软件下载_拥有无数功能的工具箱软件
  4. python快捷键设置_Pycharm学习教程(5) Python快捷键相关设置
  5. C语言-第21课 - 指针基础
  6. JAVA中关于set()和get()方法的理解及使用
  7. linux shell学习四
  8. 组合数(codevs 1631)
  9. 从C语言中的指针看C#中委托
  10. FTP 1 协议分析