1-数据结构 - B - tree
before
首先要说明的是B树
和B - tree(Balance - Tree)
是一个概念,不要理解为"两颗树",中间的是连接符中横线,而不是减号!
B树和平衡二叉树稍有不同的是B树属于"多叉树",又名"平衡多路查找树",即查找路径不只有两个;数据库索引中大量使用的就是B树和B+树的数据结构,本篇来研究B树。
B树的应用
- 文件系统:
- Windows:HPFS文件系统
- Mac:HFS,HFS+文件系统
- Linux:ResiserFS,XFS,Ext3FS,JFS文件系统 - 数据库:
- Oracle
- MySQL
- SQL server
定义
来自维基百科的定义,一个 m 阶的B树是一个有以下属性的树:
- 每一个节点最多有 m 个子节点
- 每一个非叶子节点(除根节点)最少有 ⌈m/2⌉ 个子节点
- 如果根节点不是叶子节点,那么它至少有两个子节点
- 有 k 个子节点的非叶子节点拥有 k − 1 个键
- 所有的叶子节点都在同一层,整棵树的高度一致
如上图,每一个内部节点的键将节点的子树分开。例如,如果一个内部节点有3个子节点(子树),那么它就必须有两个键: a1 和 a2 。左边子树的所有值都必须小于 a1 ,中间子树的所有值都必须在 a1 和a2 之间,右边子树的所有值都必须大于 a2 。
内部节点
内部节点是除叶子节点和根节点之外的所有节点。它们通常被表示为一组有序的元素和指向子节点的指针。每一个内部节点拥有最多 U 个,最少 L 个子节点。元素的数量总是比子节点指针的数量少一(元素的数量在 L-1 和 U-1 之间)。U 必须等于 2L 或者 2L-1;因此,每一个内部节点都至少是半满的。U 和 L 之间的关系意味着两个半满的节点可以合并成一个合法的节点,一个全满的节点可以被分裂成两个合法的节点(如果父节点有空间容纳移来的一个元素)。这些特性使得在B树中删除或插入新的值时可以调整树来保持B树的性质。
根节点
根节点拥有的子节点数量的上限和内部节点相同,但是没有下限。例如,当整个树中的元素数量小于 L-1 时,根节点是唯一的节点并且没有任何子节点。
叶子节点
叶子节点对元素的数量有相同的限制,但是没有子节点,也没有指向子节点的指针。
一个深度为n+1 的B树可以容纳的元素数量大约是深度为 n 的B树的 U 倍,但是搜索、插入和删除操作的开销也会增加。和其他的平衡树一样,这一开销增加的速度远远慢于元素数量的增加。
一些平衡树只在叶子节点中存储值,而且叶子节点和内部节点使用不同的结构。B树在每一个节点中都存储值,所有的节点有着相同的结构。然而,因为叶子节点没有子节点,所以可以通过使用专门的结构来提高B树的性能。
生成
先说规则:
- 生成一个2度的B树,树的度表示一棵树中的节点最多有多少个查找路径,2度的树表示一颗树中的节点最多有2个查找路径。
我们分步骤使用下面的数据来演示一个2度的B树的创建过程:
[8, 11, 19, 27, 28, 51, 52, 55, 56, 72]
[3, 5, 8, 9, 10, 12, 13, 15, 17, 18, 25, 26, 28, 29, 30, 31, 33, 35, 36, 60, 65, 75, 79, 87, 90, 99]
- 在一棵空树中插入
8,11
,每个就节点的键从左小右大顺序排列的:
- 再插入
19
,因为是2度的树,此时的节点有3个键,所以,要进行分裂,提取8,11,19
中间的键11
作为父节点,并且遵循:父节点的左侧孩子节点比自己小;右侧孩子节点比自己大的规则:
- 继续插入
27,28
,因为这两个值都比父节点11
大,所以插在了父节点的右侧孩子节点,此时该孩子节点由19,27,28
三个键组成,继续根据中间的键分裂,将27
跟父节点11
合并组成11,27
节点(此时不满足分裂条件);此时父节点遵循:父节点左边的键的孩子节点比该键都小;右边的键的孩子节点比自己大;父节点的两个键中间的孩子节点满足比左爹(父节点的左键)大比右爹(父节点的右键)小的规则。
- 继续插入
51
成为27
的子节点,如下图1号;再插入52
和28,51
组成子节点,如下图2号所示,并提取中间值51
为父节点,此时父节点便成11,27,51
三个键组成的节点,如下图3号所示,符合分裂条件,提取中间值27
为父节点,此时需要注意的是19
和28
两个值怎么划分,首先19
小于27
,所以分给了11
这个叶子节点;而28
比27
大,所以分给了51
这个叶子节点,又因为比51
小,所以成为51
节点的左侧孩子节点,如下图4号所示。
- 继续插入
56
,此时56
和52
相安无事;再插入72
,和52,56
共同组成3个键的节点,提取中间的键56
和51
组成父节点,其过程如下图所示:
此时,一个高度为3的2度B树生成完毕,在理解其分裂过程时,要充分理解其遵循的规则,下图动态的展示了总的过程:
要牢记,上层的叶子节点都会保存其孩子节点的指针,只是在图中没有体现出来。
PS:Selenium生成B树的示例代码
import time
import random
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
# l = [1, 2, 5, 9, 11, 14, 16, 30, 35, 38, 39, 44, 49, 58, 60, 64, 65, 66, 79, 96]
l = [8, 11, 19, 27, 28, 51, 52, 55, 56, 72]
url = "https://www.cs.usfca.edu/~galles/visualization/BTree.html"
# max_degree 度数
max_degree_3 = "/html/body/div/div[2]/div[1]/table/td[9]/table/tr[1]/td/input"
max_degree_4 = "/html/body/div/div[2]/div[1]/table/td[9]/table/tr[2]/td/input"
max_degree_5 = "/html/body/div/div[2]/div[1]/table/td[9]/table/tr[3]/td/input"
max_degree_6 = "/html/body/div/div[2]/div[1]/table/td[9]/table/tr[4]/td/input"
max_degree_7 = "/html/body/div/div[2]/div[1]/table/td[9]/table/tr[5]/td/input"
driver = webdriver.Chrome()
driver.implicitly_wait(10)
wait = WebDriverWait(driver=driver, timeout=10)
try:
driver.get(url)
driver.maximize_window()
driver.find_element_by_xpath(max_degree_3).click() # 默认是 3
for i in l:
wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@id="AlgorithmSpecificControls"]/td[1]/input'))).send_keys(i)
driver.find_element_by_xpath('//*[@id="AlgorithmSpecificControls"]/td[2]/input').click()
time.sleep(3)
except Exception as e:
print(e)
finally:
time.sleep(60)
driver.quit()
搜索
B树的搜索和二叉搜索树类似。从根节点开始,从上到下递归的遍历树。在每一层上,搜索的范围被减小到包含了搜索值的子树中。子树值的范围被它的父节点的键确定。
下图展示了B树的搜索过程:
删除
that's all,see also:
平衡二叉树、B树、B+树、B*树 理解其中一种你就都明白了 | 编程之法:面试和算法心得:B树篇 | (重点)MySQL(入门篇25)MySQL BTree索引背后的数据结构及算法原理