文章目录

  • 前言
  • 代码模板
  • 二叉排序/查找/搜索树查找
    • 适用
    • 性能
    • 代码模板
  • 折半查找和二叉排序树查找总结
  • 平衡二叉(排序)树/AVL树
    • 构造相应层数的树至少需要的结点数
    • 平衡调整的过程
    • 平衡调整的类型
  • 平衡二叉排序树查找
    • 适用
    • 性能
  • 红黑(二叉排序)树
    • 相关概念
    • 红黑调整的基本操作
    • 插入过程
    • 删除过程
    • 删除过程总结
  • 红黑二叉排序树查找
    • 适用
    • 性能
  • 二叉排序树查找、平衡二叉排序树查找和红黑二叉排序树查找总结
  • B树/B-树/平衡多路查找树
    • 相关概念
    • 特性
    • 插入过程
    • 删除过程
  • B树查找
    • 适用
    • 过程
    • 性能
  • B+树
    • B+树与B树的区别
  • B+树查找
    • 适用
    • 方式
    • 性能
  • 其他特殊的多路查找树
  • 散列/哈希表
    • 相关概念
    • 散列函数的构造原则
    • 散列函数的构造方法
    • 冲突处理的方法
  • 散列查找
    • 适用
    • 性能
  • 总结
    • 树型查找
    • 散列查找
  • 参考资料
  • 作者的话

前言

“查找”学习提纲(二)——树型查找和散列查找。


代码模板

  • 二叉排序树的C++语言描述实现模板_夜悊的博客-CSDN博客

二叉排序/查找/搜索树查找

适用

  • 动态表->二叉排序树

性能

时间复杂度:O(logn)~O(n)。n为数据规模

  • 时间复杂度:树的深度/高度
  • O(logn)。n为数据规模(树是平衡树)
  • O(n)。n为数据规模(树是斜树)

空间复杂度:O(n)。n为数据规模

  • 空间复杂度:二叉排序树的大小或递归调用栈的规模/树的深度/高度:
  • O(1)。未使用额外辅助空间(不包括二叉排序树的大小;迭代法)
  • O(logn)。n为数据规模(不包括二叉排序树的大小;递归法,树是平衡树)
  • O(n)。n为数据规模(包括二叉排序树的大小;递归法,树是斜数)

插入和删除的时间复杂度:O(logn)。n为数据规模(平均情况)


代码模板

//查找   前序遍历   递归法
//适用:
//动态表->二叉排序树
//时间复杂度:O(logn)~O(n)。n为数据规模
//时间复杂度:树的深度/高度
// O(logn)。n为数据规模(二叉排序树是平衡树)
// O(n)。n为数据规模(二叉排序树是斜树)
//空间复杂度:
//空间复杂度:二叉排序树的大小或递归调用栈的规模/树的深度/高度:
// O(1)。未使用额外辅助空间(不包括二叉排序树的大小;迭代法)
// O(n)。n为数据规模(包括二叉排序树的大小;递归法)
bool searchBSTree(struct BSTNode *root, ELEM_TYPE key) //参数:根结点指针,需插入的关键字    返回值:查找成功为true,查找失败为false
{if (root == nullptr) //空树  无查找位置{return false; //查找失败}else //非空树   递归查找{if (key == root->key) //需插入的关键字等于根结点的关键字{return true; //查找成功}else if (key < root->key) //需插入的关键字小于根结点的关键字{return searchBSTree(root->lChild, key); //查找左子树}else if (key > root->key) //需插入的关键字大于根结点的关键字{return searchBSTree(root->rChild, key); //查找右子树}}return false;
}
//查找   前序遍历   迭代法
bool searchBSTree2(struct BSTNode *root, ELEM_TYPE key) //参数:根结点指针,需插入的关键字    返回值:查找成功为true,查找失败为false
{if (root == nullptr) //空树  无查找位置{return false; //查找失败}else //非空树   迭代查找{while (root != nullptr){if (key == root->key) //需插入的关键字等于根结点的关键字{return true; //查找成功}else if (key < root->key) //需插入的关键字小于根结点的关键字{root = root->lChild; //查找左子树}else if (key > root->key) //需插入的关键字大于根结点的关键字{root = root->rChild; //查找右子树}}}return false; //未查找成功返回,则查找失败
}
//删除
//对删除结点node:
// 1.是叶子节点:直接删除node
// 2.有一棵左子树或右子树:子树作为node的双亲结点的子树,删除node(子树拼接)
// 3.有左右两棵子树:
//取node的左子树的最大关键字结点或右子树的最小关键字结点替换node,删除node->第1或2情况(取左最小或右最大替换)
//取中序遍历时,node的直接前驱结点或直接后继结点替换node,删除node->第1或2情况(取直接前驱或直接后继替换)

折半查找和二叉排序树查找总结

  • 过程相似

折半查找:

  • 二叉判定树唯一
  • 插入和删除的时间复杂度为O(n)。n为数据规模
  • 适用有序顺序表

二叉排序树查找:

  • 二叉排序树不唯一(关键字相同,插入顺序不同)
  • 插入和删除的时间复杂度为O(logn)。n为数据规模(平均情况)
  • 适用动态表

平衡二叉(排序)树/AVL树

注意:平衡二叉树是特殊的二叉排序树

构造相应层数的树至少需要的结点数

由递推公式:

  • 0层:0
  • 1层:1(根结点在第1层)
  • 2层:2
  • h层:构造h-2层的树至少需要的结点数+构造h-1层的树至少需要的结点数+1

平衡调整的过程

  1. 确定最小失衡/不平衡子树:插入路径上离插入结点最近的平衡因子的绝对值大于1的结点为根的子树
  2. 调整子树的平衡

平衡调整的类型

LL型:

  • 名称:LL调整;右单旋转调整
  • 现象:插入结点在最小不平衡树根结点A的左(L)孩子B的左(L)子树上
  • 状态:左高右低
  • 思想:左上右下;右单旋转
  • 过程:B作根,B的右子树挂接到A的左子树,A成B的右孩子

RR型:

  • 名称:RR调整;左单旋转调整
  • 现象:插入结点在最小不平衡树根结点A的右(R)孩子B的右(R)子树上
  • 状态:左低右高
  • 思想:左下右上;左单旋转
  • 过程:B作根,B的左子树挂接到A的右子树,A成B的左孩子

LR型:

  • 名称:LR调整;先左后右双旋转调整
  • 现象:插入结点在最小不平衡树根结点A的左(L)孩子B的右(R)子树C上
  • 状态:左低中高右低
  • 思想:左下中上右下
  • 过程:C作根,C的左子树挂接到B的右子树,B成C的左孩子,C的右子树挂接到A的左子树,A成C的右孩子

RL型:

  • 名称:RL调整;先右后左双旋转调整
  • 现象:插入结点在最小不平衡树根结点A的右(R)孩子B的左(L)子树C上
  • 状态:左低中高右低
  • 思想:左下中上右下
  • 过程:C作根,C的左子树挂接到A的右子树,A成C的左孩子,C的右子树挂接到B的左子树,B成C的右孩子

插入和删除的平衡调整:

  • 过程和类型相似
  • 删除导致树高变化,可能需要回溯调整

平衡二叉排序树查找

适用

  • 动态表->平衡二叉排序树

性能

平均查找长度(ASL):

  • 平均查找长度(ASL):树的深度/高度
  • O(logn)。n为数据规模

时间复杂度: O(logn)。n为数据规模

  • 时间复杂度:树的深度/高度
  • O(logn)。n为数据规模

空间复杂度:O(n)。n为数据规模

  • 空间复杂度:平衡二叉排序树的大小或递归调用栈的规模/树的深度/高度
  • O(1)。未使用额外辅助空间(不包括平衡二叉排序树的大小;迭代法)
  • O(logn)。n为数据规模(不包括B树的大小;递归法)
  • O(n)。n为数据规模(包括平衡二叉排序树的大小)

插入和删除的时间复杂度:O(logn)。n为数据规模


红黑(二叉排序)树

注意:红黑树是特殊的二叉排序树。大致/非严格平衡

相关概念

性质:

  • 结点是红色或黑色
  • 根结点是黑色
  • 叶子结点/外部结点/虚拟结点/空结点是黑色
  • 不存在两个相邻的红色结点/红色结点的双亲结点和孩子结点是黑色
  • 每个结点到任一叶子结点的简单路径,黑色结点的数量相同

其他:

  • 引入n+1个叶子结点/外部结点/虚拟结点/空结点,保证内部结点的左、右孩子结点非空。即:外部结点无数据,黑色;内部结点有数据,红色或黑色
  • 结点的黑高:结点到任一叶子结点的简单路径,不包括该结点的黑色结点数量/黑色结点的数量-1(由性质5)
  • 结点的深度的差=结点到任一叶子结点的简单路径,红色结点数量的差(由性质2和性质5)
  • 红色结点数量影响树的深度/高度(由性质2和性质5)

结论:

  • 根结点到叶子结点的最长路径不大于最短路径的两倍(由性质4和性质5)
  • n个内部节点,树的高度不大于2log以2为底的(n+1)(由结论1)

红黑调整的基本操作

  • 旋转:左旋转和右旋转
  • 着色:红色和黑色

插入过程

插入操作可能破坏性质4

有:

  • 插入结点Z
  • 插入结点Z的双亲结点P
  • 插入结点Z的叔叔结点Y
  • 插入结点Z的爷爷结点PP
  1. Z涂红色
  2. 补充Z的叶子结点
  3. 插入:转分类讨论A

分类讨论A:判断Z和P

  • Z是根结点:Z涂黑色
  • Z不是根结点,P是黑色:不操作
  • Z不是根结点,P是红色:转分类讨论B(Z和P破坏性质4

分类讨论B:判断Y、PP、P和Z

前提:Z是红色,P是红色,PP是黑色(插入前是合法的红黑(二叉排序)树,由性质2和4得)

  • Y是黑色,Z是PP的左孩子的左孩子(LL):右旋转,P涂黑色,PP涂红色
  • Y是黑色,Z是PP的右孩子的右孩子(RR):左旋转,P涂黑色,PP涂红色

LL型:P作根,P的右子树挂接到PP的左子树,PP成P的右孩子;红红黑->红黑红
RR型:P作根,P的左子树挂接到PP的右子树,PP成P的左孩子;黑红红->红黑红

  • Y是黑色,Z是PP的左孩子的右孩子(LR):左旋转,右旋转,P涂黑色,PP涂红色
  • Y是黑色,Z是PP的右孩子的左孩子(RL):右旋转,左旋转,P涂黑色,PP涂红色

LR型:Z的左子树挂接到P的右子树,Z成PP的左孩子,P成Z的左孩子,成LL型
RR型:Z的右子树挂接到P的左子树,Z成PP的右孩子,P成Z的右孩子,成RR型

  • Y是红色:Y涂黑色,P涂黑色,PP涂红色(局部恢复性质4),将PP作为插入结点,转分类讨论A

删除过程

删除操作可能破坏性质2、4和5

  1. 转二叉排序树的删除操作
  2. 转作双色结点
  3. 转双色讨论

二叉排序树的删除操作:对删除结点node

  • 是叶子节点:直接删除node
  • 有一棵左子树或右子树:子树作为node的双亲结点的子树,删除node(子树拼接)
  • 有左右两棵子树:取node的左子树的最大关键字结点或右子树的最小关键字结点替换node,删除node->第1或2情况(取左最小或右最大替换)。或取中序遍历时,node的直接前驱结点或直接后继结点替换node,删除node->第1或2情况(取直接前驱或直接后继替换)

有:

  • 删除结点Z
  • 二叉排序树的删除操作后,替换删除结点Z的替换结点X
  • X的双亲结点P
  • X的兄弟结点W
  • W的左孩子结点WL
  • W的右孩子结点WR
  • X是P的左孩子,W是P的右孩子

作双色结点:

  • 假设替换结点X有两种颜色:原来的颜色(红色或黑色)和增加的黑色

从Z继承增加的黑色:因为若Z是红色,删除Z不会破坏性质;若Z是黑色,删除会破坏性质

假设替换结点X有两种颜色的目的:恢复性质5,破坏性质1

后续操作的核心:恢复双色结点为单色结点/性质1

双色讨论:

  • X是红色和黑色:删除红色,保留黑色
  • X是黑色和黑色,X是根结点:不操作
  • X是黑色和黑色,X不是根结点,转分类讨论A

分类讨论A:

  • 转W是红色
  • 转W是黑色

W是红色:

  • 由性质4:W是红色,则P、WL和WR是黑色
  • 处理:P涂红色,W涂黑色(P和W交换颜色)。X是P的左孩子,则P/W左旋转(调整P到X一侧)。转W是黑色

W是黑色:

  • WL是红色,WR是黑色:转WL是红色,WR是黑色
  • WL是红色或黑色,WR是红色:转WL是红色或黑色,WR是红色
  • WL是黑色,WR是黑色:转WL是黑色,WR是黑色

WL是红色,WR是黑色:

  • RL型:红色结点WL是爷爷结点的右孩子W的左孩子
  • RL型:W涂红色,WL涂黑色(W和WL交换颜色)。WL右旋(调整W为WL的右孩子)。转WL是红色或黑色,WR是红色

WL是红色或黑色,WR是红色:

  • W涂P的颜色,P涂黑色(W和P交换颜色),WR涂黑色(保持W一侧的黑高)。X是P的左孩子,P/W左旋转(补充一黑色结点P到X一侧)。结束

WL是黑色,WR是黑色:

  • X和W各提取一重黑色:X是黑色,W是红色
  • 由性质5,需要补偿该一重黑色:P有两种颜色:原来的颜色(红色或黑色)和增加的黑色
  • 将P作为替换结点X,转双色讨论

删除过程总结

一、二叉排序树的删除操作:保持二叉排序树的性质

二、作双色结点:恢复性质5,破坏性质1

三、双色讨论:恢复性质1。转123

1 若X红色+黑色:删除红色,保留黑色

2 若X黑色+黑色,是根结点:不操作

3 若X黑色+黑色,不是根结点:性质5真正被破坏。转(1)(2)

(1) 若W红色:W涂黑色,P涂红色,W左旋。转(2)

由性质4:W红色,P、WL和WR黑色
W涂黑色,W左旋:W成根替换P的位置,保持黑高
P涂红色,W左旋:P成W的左孩子,保持黑高
W左旋:W的WL子树挂接到P的右子树,WL成X的兄弟
目的:转换X的兄弟为黑色

(2) 若W黑色

① WL红色,WR黑色:WL涂黑色,W涂红色,WL右旋。转②

由性质4:WL红色,P黑色
WL涂黑色,WL右旋:WL成根替换W的位置,保持黑高
P涂红色,W右旋:W成WL的右孩子,保持黑高
WL右旋:WL的右子树挂接到W的左子树,WL成X的兄弟
先:RL型。后:RR型
目的:转换W的右孩子为红色

② WR红色:W涂红色,P涂黑色,WR涂黑色,W左旋

由性质4:WR红色,W黑色
W涂红色,W左旋:W成根替换P的位置,原W侧的黑高-1,破坏性质5
P涂黑色,W左旋:P成W的左孩子,X侧的黑高+1,X恢复单色,恢复性质1
W左旋:W的左子树挂接到P的右子树,WR成X的兄弟
WR涂黑色:原W侧的黑高+1,恢复性质5
目的:调整一黑色结点到X侧

③ WL黑色,WR黑色:W涂红色,P作双色结点。转二

X恢复单色,W涂红色:各提取一重黑色
P作双色结点:从X和W提取的黑色增加到P,保持黑高。P替换为X继续处理

注意:X是P的右孩子,W是P的左孩子时,(1)(2)①②③有对称情况

总结(1)(2)①②③:

关于黑色提取:
(2)③:有重复黑色可提取,调整操作向上委托的同时能够保持黑高
(1):W红色,不能提取黑色
(2)①:WL红色,WR黑色,由性质4,W黑色。若W提取黑色,W成红色,则W->WL和W->WR的黑高不同,破坏性质5
(2)②:WR红色,由性质4,W黑色。若W提取黑色,W成红色,则W和WR红色,破坏性质4

关于目的:
(1):转换为(2)
(2)①:转换为(2)②
(2)②:调整结束
(2)③:调整操作向上委托

关于执行情况:
(2)③:唯一可重复执行的情况,每执行一次委托向上一层,最多次数是树的深度/高度,为logn
有流程:(1)->(2)③,结束
最少流程:(2)②,结束
最多流程:(1)->(2)①->(2)②,结束
最多流程时,执行常数次的着色操作和至多三次的旋转操作


红黑二叉排序树查找

适用

  • 动态表->红黑二叉排序树

性能

时间复杂度:O(logn)。n为数据规模

  • 时间复杂度:树的深度/高度
  • O(logn)。n为数据规模

空间复杂度:O(n)。n为数据规模

  • 空间复杂度:红黑二叉排序树的大小或递归调用栈的规模/树的深度/高度
  • O(1)。未使用额外辅助空间(不包括红黑二叉排序树的大小;迭代法)
  • O(logn)。n为数据规模(不包括红黑二叉排序树树的大小;递归法)
  • O(n)。n为数据规模(包括红黑二叉排序树的大小)

插入和删除的时间复杂度:O(logn)。n为数据规模


二叉排序树查找、平衡二叉排序树查找和红黑二叉排序树查找总结

  • 平衡二叉排序树查找和红黑二叉排序树查找的一般性能优于二叉排序树查找
  • 若插入和删除操作相对少,查找操作相对多,适用平衡二叉排序树查找
  • 若插入和删除操作相对多,查找操作相对少,适用红黑二叉排序树查找

B树/B-树/平衡多路查找树

注意:-无意义,不念做B减树,念做B树

B树是平衡多路查找树,但限制更强:叶子结点在同一层

相关概念

  • 树的阶/度m:结点的分支数的最大值
  • 树的深度/高度/外存存取次数h(一般不包括叶子结点层):log以m为底的(n+1) <= h <= log以[m/2]上取整为底的[(n+1)/2]+1。n为关键字数

特性

  • 若结点有n个子树,则有n-1个关键字
  • 根结点不是叶子结点,则至少有2个子树。若结点是非根非叶子结点,则至少有[m/2]上取整个子树。结点至多有m个子树
  • 非叶子结点的结构:(n,P0,K1,P1,K2,P2…Kn,Pn)。Ki(i=1,2,…,n)为结点的关键字,K1<K2<…Kn。Pi(i=0,1,…,n)为子树根结点指针,P(i-1)所指子树中所有结点的关键字<Ki,Pi所指子树中所有结点的关键字>Ki。n为结点的关键字数
  • 叶子结点在同一层,为外部结点/虚拟结点/空结点,不包含信息

其他:

  • 若有n个关键字,则有n+1个叶子结点(叶子结点对应查找失败的情况)

插入过程

  1. 确定结点中关键字数的范围:n为关键字数,有[m/2]上取整-1 <= n <= m-1
  2. 查找插入位置:若无插入位置,插入失败;若有插入位置,插入
  3. 检查结点中关键字数:若n <= m-1,插入完成;若n > m-1,结点拆分

注意:
查找时,查找到叶子结点,表明有插入位置
插入时,插入到相应的最底层的非叶子结点

结点拆分:

  • 结点中关键字数:n = m > m-1
  • 取第[m/2]上取整个关键字K
  • K的左指针指向第1~[m/2]上取整-1个关键字
  • K的右指针指向第[m/2]上取整+1~m个关键字
  • K插入双亲结点的相应位置中
  • 检查双亲结点中关键字数:若n <= m-1,插入完成;若n > m-1,结点拆分

删除过程

  1. 确定结点中关键字数的范围:n为关键字数,有[m/2]上取整-1 <= n <= m-1
  2. 查找删除位置:若无删除位置,删除失败;若有删除位置,检查结点中关键字数
  3. 检查结点中关键字数:删除讨论1和2

注意:
查找时,查找到非叶子结点,表明有删除位置
删除时,在相应的的非叶子结点上删除

删除讨论1:删除的关键字不在最底层的非叶子结点上

  • 转换:删除的关键字在最底层的非叶子结点上
  • 类比二叉排序树的删除操作:在中序遍历中,查找删除结点的左子树的最大值或右子树的最小值结点/直接前驱或直接后继结点
  • 关键字交换:取删除关键字X的相邻关键字Y,Y替换X(X不在最底层的非叶子结点上),删除Y(Y在最底层的非叶子结点上)

删除讨论2:删除的关键字在最底层的非叶子结点上

  • 若n > [m/2]上取整-1:直接删除
  • 若n = [m/2]上取整-1,删除关键字结点的左兄弟结点右兄弟结点的关键字 > [m/2]上取整-1:关键字借位ie。关键字流向:兄弟结点->双亲结点->当前结点
  • 若n = [m/2]上取整-1,删除关键字结点的左兄弟结点右兄弟结点的关键字 = [m/2]上取整-1:结点合并。关键字流向:当前结点+双亲结点+兄弟结点;注意:可能导致双亲结点的关键字数不合法

删除讨论总结:

  • 直接删除
  • 关键字交换
  • 关键字借位
  • 结点合并

B树查找

适用

  • 动态表->B树

过程

类似二叉排序树查找

  1. 在B树中查找结点(在外存进行)
  2. 在结点中查找关键字(在内存进行)。可使用线性查找

性能

时间复杂度:O(logn)。n为数据规模

  • 时间复杂度:树的深度/高度
  • O(logn)。n为数据规模

空间复杂度:O(n)。n为数据规模

  • 空间复杂度:B树的大小或递归调用栈的规模/树的深度/高度
  • O(1)。未使用额外辅助空间(不包括B树的大小;迭代法)
  • O(logn)。n为数据规模(不包括B树的大小;递归法)
  • O(n)。n为数据规模(包括B树的大小)

B+树

B+树与B树的区别

  • 若结点有n个子树,则有n个关键字
  • 根结点关键字数n的取值范围:2 <= n <= m;除根结点关键字数n的取值范围:[m/2]上取整 <= n <= m
  • 叶子结点包含信息:全部关键字和全部记录指针
  • 非叶子结点只起索引作用,索引项:有子树的最大关键字和子树指针,无最大关键字对应记录的存储地址
  • 有一个指针指向最小关键字的叶子结点,所有叶子结点链接成线性链表

B+树查找

适用

  • 动态表->B+树

方式

  • 从根结点开始:树型查找
  • 从最小关键字叶子结点开始:线性查找

性能

时间复杂度:O(logn) | O(mlogn) | O(logmlogn)。n为数据规模,m为树的阶

时间复杂度:查找结点时间(树型查找)×查找结点中关键字时间(线性查找)

  • 查找结点时间:O(logn)。n为数据规模(树的深度/高度)
  • 查找结点中关键字时间:O(m)。m为树的阶(顺序查找)
  • 查找结点中关键字时间:O(logm)。m为树的阶(折半查找等)

时间复杂度:访问外存的输入/输出(I/O)次数

  • 一次访问外存的输入/输出(I/O)读取一页
  • 页大小 = 非叶子结点大小
  • 访问外存的输入/输出(I/O)次数 = 树的深度/高度:O(logn)。n为数据规模

空间复杂度:O(n)。n为数据规模

  • 空间复杂度:B+树的大小或递归调用栈的规模/树的深度/高度
  • O(1)。未使用额外辅助空间(不包括B+树的大小;迭代法)
  • O(logn)。n为数据规模(不包括B+树的大小;递归法)
  • O(n)。n为数据规模(包括B+树的大小)

其他特殊的多路查找树

  • 2-3树:3阶B树
  • 2-3-4树:4阶B树

散列/哈希表

相关概念

  • 查找表:存储记录/关键字
  • 散列表:类比索引表和映射表,存储关键字和散列表地址的映射关系
  • 映射:h(key) = addr。key为关键字,h()为散列函数 ,addr为散列表地址
  • 冲突/碰撞:两个或两个以上的不同关键字,通过散列函数,映射到散列表相同地址
  • 同义词:对某个散列函数,能够发生冲突的不同关键字
  • 装填因子:散列表的装填程度。α = n/m。α为装填因子,n为散列表中的记录数,m为散列表的大小

注意:散列表的平均查找长度(ASL)与α有关,与n和m无关


散列函数的构造原则

  • 定义域包含查找表的所有关键字,值域取决于散列表的大小/地址范围
  • 简单:能够在较短时间计算出结果
  • 尽量避免冲突:计算的散列表地址能等概率和均匀地分布在散列表中(理解

另:

  • 散列表的大小
  • 关键字的大小/长度/位数
  • 关键字的分布
  • 计算散列表地址的时间
  • 关键字查找的频率

散列函数的构造方法

直接定址法:

  • 方法:h(key) = key或h(key) = a × key + b(a和b为常数)
  • 特点:简单,均匀(关键字映射的散列表地址在散列表中分布均匀),不会产生冲突
  • 适用:查找表的规模比较小;关键字的分布基本连续

除留余数法:

  • 方法:h(key) = key % p(m为散列表的大小,p为质数,有p <= m。一般取p为小于或等于散列表的大小的最大质数或不包含小于20质因子的合数,能够尽量避免冲突:若p > m,则取余的散列表地址可能越界;质数的取余运算可以使散列表地址尽量均匀)

平方取中法:

  • 方法:h(key) = key²的中间几位数
  • 适用:未知关键字的分布;关键字的位数比较少,可能小于散列表地址需要的位数;关键字的每位取值比较不均匀

数字分析法:

  • 方法:取关键字的若干数位,可进行旋转、反转和叠加等操作
  • 适用:关键字的位数比较多;关键字若干数位的取值比较均匀

折叠法:

  • 方法:关键字从左到右分割成位数相等的几部分(最后一部分位数不够可短些),各部分求和,依据散列表的大小,取和的后几位
  • 适用:未知关键字的分布;关键字位数比较多

随机数法:

  • 方法:h(key) = random(key)。random()为随机函数。即关键字的随机函数值为散列表地址
  • 适用:各关键字的位数不相等

注意:关键字不是数字,是符号,可通过ASCII码、Unicode码等转换为数字


冲突处理的方法

开放定址法:

  • 含义:空闲散列表地址向同义词开放,也向非同义词开放
  • 理解:同义词通过散列函数映射到散列表地址A(固有的),非同义词通过散列函数映射到散列表地址B(固有的),在B发生冲突,通过冲突处理能够映射到A(变化的)
  • 发生冲突的散列表地址作自变量,通过冲突处理函数,映射到新的散列表地址
  • 数学递推公式:hi = (h(key) + di) % m。m为散列表的大小,i为发生冲突的次数/新散列表地址的计数值,0 <= i <= m,di为增量序列,key为关键字,h()为散列函数,h(key)为散列表地址,hi为新散列表地址
  • 数学递推公式中,确定di->确定冲突处理方法
  • 有线性探测法,平方探测法,双散列法,伪随机序列法
  • 注意:对散列表,不能物理删除关键字/关键字-散列表地址映射(因为会截断其他该散列表地址的关键字的探测地址)。能逻辑删除关键字/关键字-散列表地址映射(使用标记;需要定期维护散列表避免未利用表项过多)

线性探测法:

  • 方法:di = 0,1,…,m-1
  • 缺点:易出现聚集/堆积问题(同义词和非同义词映射到相同的散列表地址)

平方探测法/二次探测法:

  • 方法:di = 0²,1²,-1²,…,k²,-k²。k <= m/2,m为可表示成4×k+3的质数
  • 缺点:不能探测到所有散列表地址,至少能探测到一半的散列表地址

双散列法:

  • 方法:di = i × h2(key)
  • 特点:最多m-1次探测回到第一次发生冲突的位置/原散列表地址

伪随机序列法:

  • 方法:di = 伪随机数
  • 伪随机数:随机种子相同,每过程(冲突处理过程和查找过程)生成的伪随机数数列相同,数列中的伪随机数互不相同

链地址法/拉链法/链接法:

  • 方法:同义词链接成单链表/同义词子表,散列表的散列表地址表项不存储关键字,存储单链表头指针
  • 优点:散列表不会满
  • 缺点:增加遍历单链表的时间

再散列函数法:

  • 方法:每冲突时,更换散列函数再计算
  • 优点:不易出现聚集/堆积问题
  • 缺点:增加散列函数再计算时间

公共溢出区法:

  • 方法:每冲突时,将关键字顺序存储到另一溢出表中
  • 适用:冲突少->溢出表相对散列表的规模小

散列查找

适用

  • 查找性能要求高,记录关系无要求

性能

影响因素:

  • 散列函数
  • 冲突处理方法
  • 装填因子

平均查找长度(ASL)/平均比较次数:

  • 查找成功:依据关键字计算:每关键字的比较次数的和/关键字数。每关键字的比较次数的和:(计算散列表地址)无冲突比较1次,有1次冲突(计算新散列表地址)比较2次,以此类推
  • 查找失败:依据散列表地址计算:每可以映射到的散列表地址上,比较到空散列表地址(比较空散列表地址算作比较1次)比较次数的和/可以映射到的散列表地址数

时间复杂度:O(1)

空间复杂度:O(m)。m为散列表的大小


总结

树型查找

名称 适用 时间复杂度 空间复杂度
二叉排序树查找 动态表->二叉排序树 O(logn)~O(n) O(n)
平衡二叉排序树查找 动态表->平衡二叉排序树 O(logn) O(n)
红黑二叉排序树查找 动态表->红黑二叉排序树 O(logn) O(n)
B树查找 动态表->B树 O(logn) O(n)
B+树查找 动态表->B+树 O(logn) | O(mlogn) | O(logmlogn) O(n)

散列查找

名称 适用 时间复杂度 空间复杂度
散列查找 查找性能要求高,记录关系无要求 O(1) O(m)

参考资料

  • 《2023版数据结构高分笔记》主编:率辉
  • 《2023年计算机组成原理考研复习指导》组编:王道论坛
  • 《大话数据结构》作者:程杰
  • B+ 树搜索时间复杂度到底是什么:mlogmN / logN? - 知乎 (zhihu.com)

作者的话

  • 感谢参考资料的作者/博主
  • 作者:夜悊
  • 版权所有,转载请注明出处,谢谢~
  • 如果文章对你有帮助,请点个赞或加个粉丝吧,你的支持就是作者的动力~
  • 文章在描述时有疑惑的地方,请留言,定会一一耐心讨论、解答
  • 文章在认识上有错误的地方, 敬请批评指正
  • 望读者们都能有所收获

“查找”学习提纲(二)——树型查找和散列查找相关推荐

  1. 数据结构与算法 ~ 查找 ~ 散列查找(哈希~线性探查法和二次探查法)

    数据结构与算法 ~ 查找 ~ 散列查找(哈希~线性探查法和二次探查法) /*search-hash*/ #include<math.h> #include<stdio.h> # ...

  2. PTA 查找(散列查找)

    散列查找: 根据散列函数确定关键词key的存储位置,将key存储在一张hash哈希表内,然后根据一定的规则(线性探测,平方探测,分离链接等)来规避相同key的数据之间的冲突. 如上图,散列表的性能和装 ...

  3. 哈希表(散列查找)(c/c++)

    通过哈希表进行查找的特点是:不需要比较关键字,而是通过哈希函数计算出关键字的位置.一般来讲,为了进行高效率的查找,要求哈希函数简单均匀.空间利用率高.关键字之间的冲突少. 关于散列查找的实现需要着重考 ...

  4. 查找算法【哈希表】 - 散列查找及性能分析

    查找算法[哈希表] - 散列查找及性能分析 散列表虽然建立了关键字和存储位置之间的直接映像,但冲突不可避免,在散列表的查找过程中,有的关键字可以通过直接定址1次比较找到,有的关键字可能仍然需要和若干关 ...

  5. 散列查找(重点讲解查找失败的ASL) 习题集

    更新日期:2021年10月10日 写在前边的话:你的支持是我写作的动力,有帮助到你的话麻烦点赞加收藏呦.感激不尽!如有错误也请留言指正. 考研数据结构练习,欢迎订阅我的专辑<考研数据结构题型分类 ...

  6. PTA数据结构题目集 第十一周——散列查找

    目录 11-散列1 电话聊天狂人 (25分) 思路 代码 测试点 11-散列2 Hashing (25分) 思路 代码 测试点 11-散列3 QQ帐户的申请与登陆 (25分) 题目大意 思路 代码 测 ...

  7. 数据结构之查找算法:散列查找

    查找算法:散列查找 思维导图: 散列函数和散列表: 构造散列函数的要求: 构造散列函数的方法: 直接定址法: 除留取余法: 数字分析法: 平方取中法: 折叠法: 解决冲突的方法: 开放定址法: 线性探 ...

  8. PyQt(Python+Qt)学习随笔:树型部件QTreeWidget中当前列currentColumn和选中项selectedItems访问方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 当前列访问方法 树型部件QTreeWidget的currentColumn()方法返回当前项中得到焦 ...

  9. 散列查找 散列表(哈希表)

    哈希表的平均查找长度是()的函数. A.哈希表的长度 B.哈希表的装填因子 C.哈希函数 D.表中元素的多少 装填因子 = 关键字个数 / 表长 符号表:是 "名字(Name)–属性(Att ...

最新文章

  1. 顺序表应用6:有序顺序表查询
  2. linux bunzip2命令
  3. “chaos”的算法--之直接插入排序
  4. [转]PHP: 深入pack/unpack
  5. mysql 二十条记录_记 MySQL优化 20条
  6. 增加SYSTE表空间数据文件
  7. [VN2020 公开赛]CSRe
  8. 程序员的搞笑日常:写给1024的程序员们,现在的你们还在加班吗?
  9. SQL Server之游标
  10. Pass4side CompTIA PDI+ Beta Exam PD1-001 DEMO 免费下载
  11. OPPO Find X,一款(可能)被全面屏“耽搁”了的AI手机
  12. 英伟达驱动更新记录_英伟达更新驱动最好的方式 - 卡饭网
  13. 聊聊旷厂黑科技 | 更真切感受影像世界的美好,旷视实时双超AI算法还原你的“夏日回忆”...
  14. win7无权限连接网络计算机,win7系统出现无权限访问网络的完美解决技巧
  15. 【2022 年第十二届 MathorCup杯数学建模】D 题 移动通信网络站址规划和区域聚类问题 赛后总结、论文及代码
  16. 格式化U盘提示Windows 无法完成格式化
  17. uni-app生成app的兼容性调研报告
  18. CentOS cowsay “会说话的小动物”
  19. yaml及使用PyYaml操作yaml文件详解
  20. python中for in的用法python中for in的用法

热门文章

  1. docker基础:非root用户操作并将数据目录进行nfs mount
  2. 大年初八 | 开工大吉!
  3. ideavim 使用分享
  4. 关于linux的档案随笔
  5. Mutex对象使用时发现的问题
  6. 以简单的方式消除 Java 的冗长
  7. 水晶球 | 贪心、排序
  8. Windows 中 使用python 一键下载android s代码
  9. 利用结构体设计游戏背包属性的思路
  10. Java设计模式之结构型-桥接模式 (Bridge)