纸上得来终觉浅,绝知此事要躬行
性质
对于节点x
- 如果y是x的左孩子,则 x.key>=y.key
- 如果y是x的右孩子,则
x.key<=y.key
如图1:
查询操作
- 遍历操作:inorder-tree-walk(x)——中序, preorder-tree-walk(x)——先序,postorder-tree-walk(x)——后续
inorder-tree-walk(x) {
if(x != null) {
inorder-tree-walk(x.left)
print(x.key)
inorder-tree-walk(x.right)
}
}
- 查询操作:tree-search(x,k) :给定值k,返回值等于k的节点
tree-search(x,k){
if(x ==null || x.key == k) return x
else if(k > x.key) return tree-search(x.right,k)
else return tree-search(x.left,k)
}
- 查询操作:
- 1、tree-minimum(x),tree-maximum(x)————给定节点,返回相应节点
- 2、tree-successor(x),tree-predecessor(x)————给定节点,返回排序后的相应后一个或前一个值的节点
如图2: 6的后继是7;13的后继是15。
tree-successor(x) {
if(x.right != null) return tree-minimum(x.right)
y = x.parent
while(y.right == x && y != null) {
x = y
y = y.parent
}
return y
}
修改操作
- 修改操作:tree-insert(T,x)————输入树T,和节点x
//初始调用树节点为 T.root
tree-insert(T,x) {
if(T == null) T = x
else if(x > T.key) {
if(T.right != null) tree-insert(T.right,x)
else T.right = x
}
else {
if(T.left != null) tree-insert(T.left,x)
else T.left = x
}
}
- 修改操作:tree-delete(T,z)————输入树T,和节点z
分析:删除操作可分为3种情况
- 1、如果z没有孩子节点,直接删除z节点
- 2、如果z只有一个节点,只需要把z.parent指向z.left或者z.right
- 3、如果z有两个节点,我们先找到z节点的后继节点y(一定在z的右子树上,根据二叉排序树的性质),之后用y代替z节点。z的左子树做为y的左子树,z的右子树做为y的右子树。这里需要考虑两种情况:y == z.right ; y != z.right。
量化各种情况:
- 1、if z.left == null,没有左节点(图3.(a)) : 我们用z.right代替x节点。如果z.right != null,为一个右节点的情况;如果z.right == null,为没有节点的情况。
- 2、if z.left != null,只有一个左节点(图3.(b)) :我们用z.left节点代替z节点。
- 3、否则,z左右孩子都有。我们先找出z的后继y,一定是z的右子树上的节点,且此节点没有左孩子。这时候需要分两种情况:
- (1)、z.right == y (图3.(c)),只需要用y节点代替x节点。(外加变动左孩子的操作)
- (2)、z.right != y (图3.(d)),先用y.right节点代替y节点;再用y节点代替z节点。(外加变动左孩子的操作)
图3:
代码实现
定义一个用v节点替换u节点的操作:只修改了父节点指向,具体左右孩子的变动由调用者决定。
transplant(T, u, v) {
//u的父节点指向v
if u.p == null // 替换根节点的情况
T.root = v
else if u == u.p.left // 替换的节点是父节点的左孩子
u.p.left = v
esle if u.p.right = v
//v的父节点指向u父节点
if v != null // 节点v不为空节点
v.p = u.p
}
tree-delete(T,z) {
if z.left == null
transplant(T, z, z.right)
else if z.right == null
transplant(T, z, z.left)
else {
y = tree-minimum(z.right)
if(y.p != z) {
transplant(T, y, y.right)
y.right = z.right // 变动右孩子的操作
y.right.p = y
}
transplant(T, z, y)
z.left = y.left // 变动左孩子的操作
z.left.p = y
}
}
完全理解此代码是建立在自行构造数据跑一跑删除节点的代码的基础上的。