TREE-INSERT
概述
TREE-INSERT 是向二叉搜索树中插入一个新节点的操作。算法从根节点出发,沿着树向下搜索合适的叶位置,将新节点作为某个已有节点的子节点挂载上去。利用 BST 性质,搜索路径的长度不超过树高 ,因此运行时间为 。插入操作保持 BST 性质不变。
定义
TREE-INSERT
给定一棵二叉搜索树 和一个待插入节点 ( 已设定,),
TREE-INSERT(T, z)将 插入到树中的正确位置,使 BST 性质保持不变。
伪代码
TREE-INSERT(T, z)
1 y = NIL
2 x = T.root
3 while x ≠ NIL
4 y = x
5 if z.key < x.key
6 x = x.left
7 else x = x.right
8 z.p = y
9 if y == NIL
10 T.root = z // 树为空,z 成为根
11 elseif z.key < y.key
12 y.left = z
13 else y.right = z
逐步执行示例
向以下 BST 中插入 :
初始树: 插入后:
6 6
/ \ / \
2 8 → 2 8
/ \ \ / \ / \
1 4 9 1 4 7 9
| 步骤 | x | y | z.key 与 x.key 比较 | 操作 |
|---|---|---|---|---|
| 1 | 6 | NIL | y=6, x=x.right=8 | |
| 2 | 8 | 6 | y=8, x=x.left=NIL | |
| 3 | NIL | 8 | — | 退出循环 |
| 4 | — | — | — | z.p = 8 |
| 5 | — | — | y.left = z |
新节点 7 成为节点 8 的左子节点。
核心性质
循环不变式
循环不变式
对于
TREE-INSERT中第 3-7 行的while循环:不变式:在每次循环迭代开始时,
- 是 的父节点(或 当 为根时)
- 若 存在于树中,则 应该被插入的位置是以 为根的子树中
初始化:第一次迭代前,,。 是 的”父节点”(根无父节点,用 NIL 表示),不变式成立。
保持:每次迭代中,先令 ( 成为当前 的父节点),然后根据比较结果将 移动到左子节点或右子节点。由 BST 性质, 的正确插入位置一定在新的 子树中。不变式保持。
终止:循环终止时 ,说明已经到达了叶节点的空子指针位置。此时 指向新节点的父节点, 应该作为 的左子节点或右子节点插入。
时间复杂度
时间复杂度:
while循环从根到叶,最多经过 次迭代( 为树高)。循环内每次操作为 ,循环外操作也为 ,因此总时间为 。
- 平衡 BST:
- 退化为链:
插入后 BST 性质保持
正确性
插入操作完成后,BST 性质仍然成立。原因如下:
- 新节点 被插入为某个叶节点的子节点()
- 的父节点 满足:若 是 的左子节点,则 ;若 是 的右子节点,则
- 没有子节点,因此不会违反任何以 为根的 BST 性质
- 树中其他节点的子树结构未变,BST 性质不受影响
生活化类比
将 BST 想象为一个图书馆的分类书架:
- 每本书(节点)按编号(key)排列
- 插入一本新书时,从书架入口(根)出发,每次比较编号决定走向”编号较小的一侧”还是”编号较大的一侧”
- 一直走到书架末端(空位),把新书放在那里
- 放入新书不会打乱已有书籍的排列顺序
章节扩展
TREE-INSERT 是 BST 动态集合操作的插入部分。与之配对的是删除操作,删除操作更为复杂,需要借助 TRANSPLANT 子程序:
- TREE-DELETE:删除节点并保持 BST 性质,分为三种情况(无子节点、一个子节点、两个子节点),时间
- TRANSPLANT:删除操作的核心子程序,用一棵子树替换另一棵子树在父节点中的位置
参见
- 二叉搜索树 — BST 的定义与性质
- 中序遍历 — 验证 BST 有序性的遍历方式
- TREE-SEARCH — 在 BST 中查找关键字
- TRANSPLANT — 删除操作的核心子程序
- 散列表 — 散列表的插入操作平均 ,但不维护有序性