考研408--数据结构--day14--B树&B+树&散列表

(以下内容全部出自上述课程)

目录

  • B树
    • [1. 引入](#1. 引入)
      • [1.1 m叉查找树](#1.1 m叉查找树)
      • [1.2 如何查找](#1.2 如何查找)
      • [1.3 如何保证查找效率](#1.3 如何保证查找效率)
    • [2. 定义](#2. 定义)
      • [2.1 B树](#2.1 B树)
      • [2.2 B树的高度](#2.2 B树的高度)
      • [2.3 小结](#2.3 小结)
    • [3. 基本操作](#3. 基本操作)
      • [3.1 B树的插入](#3.1 B树的插入)
        • [3.1.1 第一次溢出](#3.1.1 第一次溢出)
        • [3.1.2 第二次溢出](#3.1.2 第二次溢出)
        • [3.1.3 第三次溢出](#3.1.3 第三次溢出)
        • [3.1.4 第四次溢出](#3.1.4 第四次溢出)
        • [3.1.5 第五次溢出](#3.1.5 第五次溢出)
        • [3.1.6 第六次溢出](#3.1.6 第六次溢出)
      • [3.2 B树的删除](#3.2 B树的删除)
        • [3.2.1 终端结点](#3.2.1 终端结点)
        • [3.2.2 非终端结点](#3.2.2 非终端结点)
        • [3.2.3 兄弟够借](#3.2.3 兄弟够借)
        • [3.2.4 兄弟不够借](#3.2.4 兄弟不够借)
      • [3.3 小结](#3.3 小结)
  • B+树
    • [1. B+树](#1. B+树)
    • [2. B+树的查找](#2. B+树的查找)
    • [3. B+树 vs B树](#3. B+树 vs B树)
    • [4. 小结](#4. 小结)
  • 散列表
    • [1. 介绍](#1. 介绍)
      • [1.1 基本术语](#1.1 基本术语)
      • [1.2 如何处理冲突?(了解)](#1.2 如何处理冲突?(了解))
      • [1.3 小结](#1.3 小结)
    • [2. 散列函数的构造](#2. 散列函数的构造)
      • [2.1 设计散列函数时应该注意什么?](#2.1 设计散列函数时应该注意什么?)
      • [2.2 除留余数法](#2.2 除留余数法)
      • [2.3 直接定址法](#2.3 直接定址法)
      • [2.4 数字分析法](#2.4 数字分析法)
      • [2.5 平方取中法](#2.5 平方取中法)
      • [2.6 小结](#2.6 小结)
    • [3. 处理冲突的方法](#3. 处理冲突的方法)
      • [3.1 拉链法](#3.1 拉链法)
        • [3.1.1 插入操作](#3.1.1 插入操作)
        • [3.1.2 查找操作](#3.1.2 查找操作)
        • [3.1.3 删除操作](#3.1.3 删除操作)
        • [3.1.4 小结及拓展](#3.1.4 小结及拓展)
      • [3.2 开放定址法](#3.2 开放定址法)
        • [3.2.1 基本原理](#3.2.1 基本原理)
        • [3.2.2 插入、查找操作](#3.2.2 插入、查找操作)
        • [3.2.3 删除操作](#3.2.3 删除操作)
        • [3.2.4 小结及拓展](#3.2.4 小结及拓展)
    • [4. 散列查找的性能分析](#4. 散列查找的性能分析)
      • [4.1 性能分析](#4.1 性能分析)
      • [4.2 装填因子](#4.2 装填因子)
      • [4.3 聚集现象](#4.3 聚集现象)
      • [4.4 回顾](#4.4 回顾)

B树

1. 引入

1.1 m叉查找树

回顾二叉查找树,我们就会出现一个疑问:二叉查找树查找那么方便,变成m叉查找树 岂不更方便?

根据上面的想法,我们可以实现出一个5叉查找树,如图所示:

  • 一个结点最多可以有4个关键字,也就是5个分叉
  • 一个结点最少可以有1个关键字,也就是2个分叉

1.2 如何查找

和二叉查找树的模式一模一样,和结点中的数字比大小然后确认继续去那个组进行查找。

1.3 如何保证查找效率

最好的状态 :结点关键字多,树矮且平衡,效率高。
策略 :5叉查找树中,除了根节点外,任何结点至少有2个分叉,即至少含有1个关键字。

这里根节点只有一个元素,且是终端结点,那就只有两个分叉。

2. 定义

2.1 B树

满足上面要求的树,也就是m叉查找树,就是B树。

m阶B树:

  • 每个结点至多有m棵子树,即至多含m-1个关键字
  • 如果根节点不是终端结点,则至少有两棵子树
  • 除根节点外的所有非叶子结点至少有m/2个子树,即至多含m/2-1个关键字
  • 所有叶子节点都出现在同一个层次上,并且不带信息

  • 关键字从小到大排序;
  • 对于每个关键字 Ki:
  • 左边的子树(由 P_{i-1} 指向)中所有值都 < Ki
  • 右边的子树(由 P_i 指向)中所有值都 > Ki

例如:[5,11] 结点:

  • P₀ 指向 [1,3] → 所有值 < 5
  • P₁ 指向 [6,8,9] → 所有值 ∈ (5,11)
  • P₂ 指向 [13,15] → 所有值 > 11

2.2 B树的高度

最小高度(满):

最大高度(空):

2.3 小结

3. 基本操作

3.1 B树的插入

如图,一个结点最多有m-1个关键字,如果再塞进来一个就会溢出:

3.1.1 第一次溢出

一旦溢出,我们就可以取中间的位置,把所有的关键字分成左右两部分:

再插入新元素,就往终端结点上插入:

错误示范:不可以插在根节点上。

3.1.2 第二次溢出

那如果终端结点溢出了,该怎么办呢?

老样子从中间切开,把中间的标准提拔到根节点那里。

3.1.3 第三次溢出

和上一次相同,从中间切开,把标准提到根节点上。

3.1.4 第四次溢出


3.1.5 第五次溢出
3.1.6 第六次溢出

一层满了,就继续向上分化。


3.2 B树的删除

3.2.1 终端结点

终端结点:直接删掉

3.2.2 非终端结点

非终端结点:直接前驱/后继来代替。



3.2.3 兄弟够借

兄弟够借:自己不满足最低条件了,旁边有人满足,就可以借一个关键字。




3.2.4 兄弟不够借

兄弟不够借:旁边儿也自身难保,那就穷穷结合。



3.3 小结

B+树

1. B+树

B+树可以和分块查找对比学习,简单说就是都用到了索引 这个东西。

m阶B+树:

  • 每个分支结点最多有m棵子树
  • 非根结点至少有两棵子树,其他每个分支结点至少有m/2棵子树
  • 结点的子树 个数与关键字个数相同
  • 叶节点包含全部关键字记录,按大小顺序排列
  • 分支结点包含关键字的最大值 和指针

2. B+树的查找

查找9:

  • 可以从根节点往下查找
  • 也可以p指针从左往右查找

    查找7:
  • 可以从根节点往下查找
  • 也可以p指针从左往右查找
  • 不管成功还是失败,总会走到最下一层的结点

对比:

3. B+树 vs B树

第一组:

B+树:关键字是索引,是谁就指到谁

B树:关键字是分割线,分隔两个范围

第二组:

B+树:因为索引直接指向,所以没有-1(一个关键字就是一个关键字)

B树:因为是分隔,所以需要-1(一个关键字可以分两个分叉)
第三组:

B+树:索引,索引的书页对应书上的具体页码,所以自然而然会有重复

B树:因为是分隔,且关键字本身也是具体页码,所以没有重复
第四组:

B+树:只有最后一层才记录了有效数据(记录),因为前面的都是索引

B树:每个关键字都有记录,无索引概念,就相当于夹了书签的书

4. 小结

散列表

1. 介绍

1.1 基本术语

  • 散列表:一种数据结构。(就是一个表)
  • 散列函数 :决定了关键字,通过什么方式,被添加进这个散列表。
  • 冲突:通过同一种方式添加进散列表的时候,有两个数关键字可以添加进同一个表格。
  • 同义词 :会发生冲突的两个关键字。

1.2 如何处理冲突?(了解)




1.3 小结

2. 散列函数的构造

2.1 设计散列函数时应该注意什么?

注意点:

  • 涵盖所有关键字
  • 不能超出散列表的地址范围
  • 尽可能减少冲突
  • 尽可能简单

2.2 除留余数法

存入数/质数=存入地址

质数通常选择不大于表长,但最接近表长的质数。

  • 如果大了:就可能把要存入的数存到表长之外的位置
  • 如果太小了:浪费表格空间

适用场景 :通用

2.3 直接定址法

直接存入。
适用场景 :关键字分布基本连续

2.4 数字分析法

选分布均匀的部分作为散列地址。
适用场景 :关键字集合已知,且关键字的某几个数码位分布均匀。

2.5 平方取中法

取需要存入的数的平方值的中间几位作为散列地址。
适用场景 :关键字的每位取值都不够均匀。

2.6 小结

3. 处理冲突的方法

3.1 拉链法

发生冲突,可以把第二个关键字挂载在已经存入的数字下方/上方。

3.1.1 插入操作

头插法:从上往下插入。

3.1.2 查找操作

按照散列函数的逻辑,将需要查找的数字处理后得到地址,去这个地址里找有没有这个数。

查找长度:与关键字对比的次数。



3.1.3 删除操作

和查找一样,必须先找到这个关键字,确定有这个关键字,才能删除它。




3.1.4 小结及拓展


因为我们头插法插入的数字最后是乱序的,那么就可以在插入结束后按照大小顺序重新排列,提高查找效率。

3.2 开放定址法

3.2.1 基本原理


开放定址 :通过不同的方法,找到一个空位把这个新元素放进去。

  • 线性探测法:顺着往后找
  • 平方探测法:依次正负平方往后找
  • 双散列法:再加一个散列函数来限制查找空格
  • 伪随机序列 :自己设定好的按什么顺序找空格
3.2.2 插入、查找操作

线性探测法:

平方探测法:

双散列法:

伪随机序列法:

3.2.3 删除操作


删除15:

  • 先根据散列函数找到散列地址
  • 没找到就再用探测序列
  • 查找成功即可删除
  • 这里直接清空位置3,其实是错误的(往下看)

    删除1:
  • 按照之前的步骤
  • 当查找到位置3的时候发现是空的,就直接判定失败
  • 但其实1就在位置4
  • 所以上一步删除15时直接清空数据是不对的

    所以当我们删除数据的时候,我们应该将这个位置标为已删除,就是从逻辑上删除了,实际上还有,防止它变为空,截断查找。

    这样重新查找数据,就可以很顺利地找到了。

    当然,如果有太多标为已删除的数据,有的时候还会拉慢我们的查找速率,所以我们需要不定时地清理数据。
3.2.4 小结及拓展






4. 散列查找的性能分析

4.1 性能分析

成功ASL:

失败ASL:

4.2 装填因子

4.3 聚集现象

4.4 回顾


相关推荐
季明洵2 小时前
Java实现循环队列、栈实现队列、队列实现栈
java·数据结构·算法··队列
qq_454245032 小时前
基于ECS的工作流编排框架
数据结构·c#
iAkuya2 小时前
(leetcode)力扣100 74 数组中的第K个最大元素(快速选择\堆)
数据结构·算法·leetcode
云深处@2 小时前
【数据结构】排序
数据结构·算法·排序算法
努力努力再努力wz12 小时前
【Linux网络系列】:TCP 的秩序与策略:揭秘传输层如何从不可靠的网络中构建绝对可靠的通信信道
java·linux·开发语言·数据结构·c++·python·算法
重生之后端学习15 小时前
78. 子集
java·数据结构·算法·职场和发展·深度优先
kronos.荒15 小时前
滑动窗口+哈希表:最小覆盖子串
数据结构·python·散列表
wostcdk16 小时前
筛质数汇总
数据结构·算法
qq_4542450317 小时前
BuildTemplateGraph 函数深度解析:动态节点图构建的架构设计与核心价值
数据结构·c#