第七章:查找

7.3 树表的查找

当表插入、删除操作频繁时,使用动态查找表,可以维护表的有序性。其中,表结构在查找过程中动态生成,给定key,若表中存在,则成功返回;否则,插入key。

7.3.1 二叉排序树

  1. 定义:二叉排序树(Binary Sort Tree)又称二叉搜索树、二叉查找树。非空二叉排序树应该满足以下条件:
    (1)若其左子树非空,则左子树上所有结点的值均小于根结点的值
    (2)若其右子树非空,则右子树上所有结点的值均大于等于根结点的值
    (3)其左右子树本身又各是一棵二叉排序树
    二叉排序树示例
  • 中序遍历二叉排序树递增有序序列
    根45,左子树(左3——>根12——>右24——>37),右子树(左null——>根53——>右(左61(左空——>根61——>右90(左78——>根90——>右空))——>根100——>右空))
    中序遍历二叉排序树
  1. 查找
    与根结点比较大小,若大于等于根结点,则在右子树;否则在左子树。递归查找,直到在树中查找到等于key的根结点,查找成功;若没有找到该值,返回空值,查找失败。
    二叉排序树的存储结构

    算法思想
    (1) 若二叉排序树为空,则查找失败,返回空指针。
    (2) 若二叉排序树非空,将给定key与根结点的关键字T->data.key进行比较:
    ① 若key = T->data.key,则查找成功,返回根结点地址;
    ② 若key < T->data.key,则进一步査找左子树;
    ③ 若key > T->data.key,则进一步查找右子树。
    查找/代码
    在树中查找关键字与给定key相等的结点,比较关键字次数=该结点所在层数(最多比较次数=树的深度)

  2. 二叉排序树的平均查找长度:
    含有n个结点的二叉排序树的平均查找长度ASL和树的形态有关。
    二叉排序树的平均查找长度

  • 如何提高形态不均衡的二叉排序树的查找效率?
    答:平衡化处理,尽量让二叉树的形状均匀。
  1. 插入
    首先,比较根结点和即将插入的key的大小,插入key小于根结点的关键字,则到左子树去找,否则去右子树找;再跟左子树的根结点比较,插入key小于根结点的关键字,则到左子树去找,否则去右子树找...如此循环进行,直到要比较的根结点为空,即在该位置插入。
    算法归纳
  • 若二叉排序树为空,则插入结点作为根结点插入到空树中
  • 否则,继续在其左、右子树上查找
    1)树中已有,不再插入
    2)树中没有,查找直至某个叶子结点的左子树或右子树为空为止,则插入结点应为该叶子结点的左孩子或右孩子
    插入
  1. 生成
    从空树出发,经过一系列查找插入操作,可以生成一棵二叉排序树。 那么,一个无序序列可以通过构造二叉排序树而变成一个有序序列。构造树的过程就是对无序序列进行排序的过程。
    :不同插入次序的序列生成不同形态的二叉排序树。

  2. 删除
    从二叉排序树中删除一个结点,不能把以该结点为根的子树都删去,只能删掉该结点。要保证删除后的二叉树仍然是二叉排序树。

  • 被删除的是叶子结点:直接删去该结点
    叶子结点

  • 被删除的结点只有左子树 or 只有右子树,用其左子树 or 右子树替换
    只有左子树

  • 被删除的结点包含左子树 and 右子树:一种是采用前驱替换,首先在左子树找到最大key的结点,将其作为左子树的双亲,同时删除该最大key的结点,修改其双亲的指针域为指向该node的孩子;另外一种是后继替换,首先在右子树找到最小key的结点,将其作为右子树的双亲,同时删除该最小key的结点,修改其双亲的指针域为指向该node的孩子。

因此,删除主要关注如何修改其双亲的指针域。删除之后的二叉排序树深度更小,查找效率更高,同时也让二叉树更平衡。

7.3.2 平衡二叉树

  1. 定义:平衡二叉树(balanced binary tree)又称AVL树(Adelson-Velskii and Landis),平衡二叉树一定是二叉排序树。一棵非空的平衡二又树是具有下列性质的二叉排序树
    ① 左子树与右子树的高度之差的绝对值小于等于1;
    ② 左子树和右子树也是平衡二叉排序树。
    平衡因子(BF):平衡二叉树的左子树的高度 - 右子树的高度。由定义可知,BF取值{-1,0,1}。
  • 判断平衡二叉树:
    判断平衡二叉树
    对于一棵有n个结点的AVL树,其高度保持在\(O(log_{2}n)\)数量级,ASL(平均查找长度)也保持在\(O(log_{2}n)\)数量级。
  1. 失衡二叉排序树的调整
    当我们在一个AVL树上插入一个结点后,有可能导致AVL树失衡,即出现平衡因子绝对值大于1的结点,如:2、-2.
    4种失衡
  • 4种失衡调整方法 平衡旋转
    插入一个结点,产生不止一个失衡结点,选择最小失衡子树(深度最小)的根结点作为失衡结点。
    ① LL型:插入在左子树的左子树上导致的失衡。\(\alpha\)\(\beta\)是B左子树和右子树,h表示深度。
    LL型
    LL型

② LR型:插入在左子树的右子树上导致的失衡。
LR型
LR型

③ RL型:插入在右子树的左子树上导致的失衡。
RL型
RL型案例

④ RR型:插入在右子树的右子树上导致的失衡。
RR型
RR型

  • 调整原则:1)降低高度 2)保持二叉排序树性质。LL型中顺时针把A拽下来(C<B<A),LR型把C拽上去(B<C<A),RL型把C拽上去(A<C<B),RR型中逆时针把A拽下来(A<B<C)。规律就是,最小的key结点在左子树,最大的key结点在右子树,中间key的结点上升。
    调整原则
  1. 构建一个平衡二叉树
    根据输入关键字序列,构建二叉排序树;如果有失衡,则进行调整。再根据调整后的二叉树进行插入。这里每一步都要计算结点的平衡因子。

7.3.3 红黑树

B-树
B+树
键树

7.4 哈希表(散列表)的查找

7.4.1 基本定义

  1. 基本思想:记录的存储位置与关键字之间存在对应关系(Hash函数)求的是存储位置

$Loc(i)=H(keyi)$

根据keyi关键字查Hash表中的值Loc(i),该值为keyi存储的位置。若查不到则返回一个特殊值(空指针或空记录)
优点:查找效率高
缺点:空间效率低 e.g H(key)=k

  1. 散列方法:选取某个函数H(key),根据该函数输入key计算元素的存储位置,并按此存放。查找时,比较H(key)的关键码 与给定值k是否相等,来确定查找是否成功。其中,H(key)=k称为散列函数。根据上述思想构建的表称为散列表

  2. 冲突:不同的关键码映射到同一个散列地址。
    H(k)=k mod 7(关键字除以7取余,该余数为存储地址)
    冲突

  • 同义词:具有相同函数值的多个关键字。如图示的39、25和11。
    在散列查找方法中,冲突是不可避免的,只能尽可能减少。

7.4.2 散列函数的构造方法

  1. 使用散列表要解决好两个问题,1)构造好的散列函数:(a)所选函数尽可能简单,以便提高转换速度;
    (b)所选函数对关键码计算出的地址,应在散列地址集中致均匀分布,以减少空间浪费。2)制定一个好的解决冲突的方案:查找时,如果从散列函数计算出的地址中查不到关键码,则应当依据解决冲突的规则,有规律地查询其它相关单元。

  2. 构造散列函数考虑的因素:
    ①执行速度(即计算散列函数所需时间);
    ②关键字的长度;
    ③散列表的大小(希望散列的地址空间尽量少)
    ④关键字的分布情况(希望存放元素尽量均匀,避免冲突);
    ⑤查找频率。
    常用的构造方法有:直接定址法、数字分析法、平方取中法、折叠法、除留余数法、随机数法

  • 直接定址法:
    直接定址法
  • 除留余数法:更加常用(比其他散列函数好)
    除留余数法

7.4.3 处理散列地址冲突的方法

处理冲突的方法:开放定址法(开地址法)链地址法(拉链法)、再散列法(双散列函数法)、建立一个公共溢出区。

  • 开放定址法:
    【基本思想】有冲突时就去寻找下一个空的散列地址,只要散列表足够大空的散列地址总能找到,并将数据元素存入。\(d_{i}\)为增量序列,定义增量序列有几种常用方法:线性探测法、二次探测法、伪随机探测法

开放定址法例子

一个线性探测法例子:散列表的长度为11。按照顺序存储输入序列的关键码,当遇到冲突时,采用线性探测法构造\(H_{1}\),其中\(d_{i}\)取1,存储位置往后移一位,如果该位置为空,则在此位置存储关键码;否则继续后移(使用更新的\(H_{i}\)),直到找到空位置。(先到先得,先安排最前的关键码)
开放定址法解决冲突

平均查找长度ASL:

一个二次探测法例子:基于\(H_{1}\)找到的散列地址仍然冲突,则继续尝试\(H_{2}\)寻找新的散列地址...直到找到空的散列地址来存储关键码。
二次探测法

  • 链地址法:比开放地址法要好
    【基本思想】相同散列地址的记录,将其链成一单链表。m个散列地址就设m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构。
    链地址法

具体实现步骤
① 取数据元素的关键字key,计算其散列函数值(地址)。若该地址对应的链表为空,则将该元素插入此链表;否则,执行②解决冲突。
② 根据选择的冲突处理方法,计算关键字key的下一个存储地址。若该地址对应的链表不为空,则利用链表的前插法后插法将钙该元素插入此链表。

优点:1)非同义词(由于探测导致的,原同义词向非同义词位置移动,而产生冲突)不会冲突,无“聚集”现象;2)链表上结点空间动态申请,更适合于表长不确定的情况。

7.4.4 散列表的查找

  • 和处理冲突的思路一致:
    散列表的查找

  • 散列表的查找效率分析:ASL与装填因子α有关。
    查找效率分析
    ASL与装填因子α有关

posted @ 2024-11-29 22:09  soffiya  阅读(57)  评论(0)    收藏  举报