树表的查找
当表插入,删除操作频繁时,为维护表的有序性,需要移动表中很多记录
改用动态查找表----几种特殊的树
表结构在查找过程中动态生成
对于给定值key
若表中存在,则成功返回;否则,插入关键字等于key的记录
二叉排序树
二叉树排序树(Binary Sort Tree)又称为二叉搜索树、二叉查找树
定义:
二叉排序树或是空数,或是满足以下性质的二叉树:
- 若其左子树非空,则左子树上所有节点的值均小于根节点的值;
- 若其右子树非空,则右子树上所有节点的值均大于等于根节点的值;
- 其左右子树本身又各是一颗二叉排序树
中序遍历—左根右
二叉排序树的操作—查找
若查找的关键字等于根节点,成功
否则
- 若小于根节点,查其左子树
- 若大于根节点,查其右子树
在左右子树上的操作类似
算法思想
- 若二叉树排序为空,则查找失败,返回空指针。
- 若二叉树排序非空,将给定值key与根节点的关键字T->data.key进行比较
- 若key等于 T->data.key,则查找成功,返回根节点地址;
- 若key小于 T->data.key。则进一步查找左子树;
- 若key大于 T->data.key,则进一步查找右子树。
二叉排序树的查找分析
二叉排序树上查找某关键字等于给定值的节点过程,其实就是走了一条从根到该节点的路径
含有n个节点的二叉排序树的平均查找长度和树的形态有关
问题:如何提高形态不均衡的二叉排序树的查找效率?
解决办法:做“平衡化”处理,即尽量让二叉树的形状均衡!
二叉排序树的操作–插入
若二叉排序树为空,则插入节点做为根节点插入到空树中
否则,继续在其左、右子树上查找
-
树中已有,不在朝如
-
树中没有
- 查找直至某个叶子节点的左子树或右子树为空为止,则插入节点应为该叶子节点的左孩子或右孩子
插入的元素一定在叶节点上
二叉排序树的操作-生成
从空树出发,经过一些列的查找、插入操作之后,可生成一颗二叉排序树
例:设查找的关键字序列为{45,24,53,45,12,24,90}
一个无序序列可通过构造二叉排序树而变成一个有序序列。构造树的过程就是对无序序列进行排序的的过程。
插入的节点均为叶子节点,故无需移动其他节点。相当于在有序序列上插入记录而无需移动其他记录
但是:
关键字的输入顺序不同,建立的不同二叉排序树
按照中位数的选根节点的话,可以构造深度最小的二叉树
二叉排序树的操作-删除
从二叉排序树中删除一个节点,不能把以该节点为跟的子树都删去,只能删掉该节点,并且还应该保证删除后所得的二叉树仍然瞒住二叉排序树的性质不变
由于中序遍历二叉树可以得到一个递增有序的序列,name,在二叉排序树中删除一个节点相当于删除有序序列中的一个节点
- 将因删除节点而断开的二叉链表重新链接起来
- 防止重新链接后树的高度增加
平衡二叉树
平衡二叉树的定义
平衡二叉树(balanced binary tree)
- 又称AVL树(Adelson-Velskii and Landis)
- 一棵平衡二叉树或者是空树,或者是具有下列性质的二叉排序树:
- 左子树与右子树的高度之差的绝对值小于等于1;
- 左子树和右子树也是平衡二叉排序树
为了方便起见,给每个节点附加一个数字,给出该节点左子树与右子树的高度差。这个数字称为节点的平衡因子(BF)
平衡因子 = 节点左子树的高度-节点右子树的高度
根据平衡二叉树定义,平衡二叉树上所有节点的平衡因子只能是-1\0,或1
失衡二叉排序树的分析与调整
当我们在一个平衡二叉排序树上插入一个节点时,有可能导致失衡,即出现平衡因子绝对值大于1的节点,如:2、-2
LL型调整(类似于旋转)
RR型调整(同LL型)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2bGAuagY-1653734398259)(C:\Users\S\AppData\Roaming\Typora\typora-user-images\image-20220521172728599.png)]
LR型调整
RL型调整
例题:
输入关键字序列(16,3,7,11,9,26,18,14,15),给出构造一个AVL树的步骤
如果出现失衡,就那中间值节点作为“根”节点重新构建
散列表的查找
散列表的基本概念
基本思想:记录的存储位置与关键字之间存在的对应关系 对应关系–hash函数 Loc(i) = H(keyi)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RrUlpZJm-1653734398266)(C:\Users\S\AppData\Roaming\Typora\typora-user-images\image-20220521175841318.png)]
散列表的若干术语
散列方法(杂凑法)
选取某个函数,依该函数按关键字 计算元素的存储位置,并按此存放;
查找时,有同一个函数对给定值K计算地址,将k与地址单元中元素关键码进行对比,确定查找是否成功
散列函数(杂凑函数):散列方法中使用的的转换函数
散列表(杂凑表):按照上述思想构造的表
散列函数:H(key)=K
冲突:不同的关键码映射到同一个散列地址
key1 != key2,但是H(key1) = H(key2)
例:有6个元素的关键码分别为:(25,21,39,9,23,11)。
- 选取关键码与元素位置间的函数为H(k) = k mod 7(取余数)
- 地址编号从0-6
通过散列函数对6个元素简历散列表
同义词:具有相同函数值的多个关键字
使用散列表要解决好两个问题:
-
构造好的散列函数
- 所选函数尽可能简单,以便提高专管速度
- 所选函数对关键码计算出的地址,应在散列地址集中的均匀分布,以减少空间浪费
-
制定一个好的解决冲突的方案
查找时,如果从散列函数计算出的地址中查找不到关键码,则应当依据解决冲突的规则,有规律的查询其他相关单元
散列函数的构造方法
构造散列函数考虑的因素
- 执行速度(即计算散列函数所需的时间)
- 关键字长度;
- 散列表的大小;
- 关键字的分布情况
- 查找频率
根据元素集合的特性构造
直接定址法
Hash(key) = a * key +b (a,b为常数)
优点:以关键码key的某个线性函数值为散列地址,不会产生冲突
缺点:要占用连续的地址空间,空间效率低
除留余数法
Hash(key) = key mod p (p是一个整数)
关键:如何选取合适的p
技巧:设表长为m,取p<=m且为质数
处理冲突的方法
开放定址法(开地址法)
基本思想
有冲突时就去寻找下一个 空的散列地址,只要散列表足够大,空的散列地址总能找到,并将数据元素存入
例如:除留余数法 Hi=(Hash(key)+d)mod m di为增量序列
例:关键码集为{47, 7, 29, 11, 16, 92, 22, 8, 3},散列函数的除数 m=11;散列函数为 Hash(key)=key mod 11
;拟用线性探测法解决冲突,建散列表如下
ps:当冲突的时候,会拿到结果与原来的结果对比,逐次累加
二次探测法
伪随机探测法
Hi = (Hash(key)+dj) mod m (1<=i<m)
其中:m为散列表长度 di为伪随机数
链地址法(拉链法)
基本思想:相同散列地址的记录链成一单链表
m个散列地址就设m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构。
例如:一组关键字为{19, 14, 23, 1, 68, 20, 84, 27, 55, 11, 10, 79}
,散列函数为Hash(key)= key mod 13
链地址法建立散列表步骤
- step1:取数据元素的关键字key,计算其散列函数值(地址)。若该地址对应的连表为空,则将钙元素插入此链表;否则执行Step2解决冲突
- Step2:根据选择的冲突处理方法,计算关键字key的下一个存储地址。若改地址对应的连表不为空,则利用链表的前插法或后插法将改元素插入此连表
链地址法的优点
- 非同义词不会冲突,无“聚集”现象
- 连表上的节点空间动态申请,更适合于表长不确定的情况
散列表的查找
例题:
用链地址法处理冲突
关键字(19, 14, 23, 1, 68, 20, 84, 27, 55, 11, 10, 79)
n=12 无序表查找ASL?,有序表折半查找ASL?散列表上查找ALS
散列表的查找效率分析
使用平均查找长度ASL来衡量查找算法,ASL取决于
- 散列函数
- 处理冲突的方法
- 散列表的装填因子a
a=表中填入的记录数/哈希表的长度
a越大,表中记录数越多,说明表装的越满,发生冲突的可能性就越大,查找时比较次数就越多
结论
- 散列表技术具有很好平均性能,优于一些传统的技术
- 链地址法优于开地址法(查找效率,长度动态的,方便插入和删除)
- 除留余数法做散列函数优于其他类型的函数
0, 84, 27, 55, 11, 10, 79)`n=12 无序表查找ASL?,有序表折半查找ASL?散列表上查找ALS
散列表的查找效率分析
使用平均查找长度ASL来衡量查找算法,ASL取决于
- 散列函数
- 处理冲突的方法
- 散列表的装填因子a
a=表中填入的记录数/哈希表的长度
a越大,表中记录数越多,说明表装的越满,发生冲突的可能性就越大,查找时比较次数就越多
[外链图片转存中…(img-IECT5HYA-1653734398272)]
结论
- 散列表技术具有很好平均性能,优于一些传统的技术
- 链地址法优于开地址法(查找效率,长度动态的,方便插入和删除)
- 除留余数法做散列函数优于其他类型的函数