哈希表和红黑树的对比

哈希表

          表: 存储数据 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集合中的TreeSetTreeMap,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)。
  • 16
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值