《算法导论》学习笔记——二叉搜索树
二叉搜索树
什么是二叉搜索树
对于任何节点x,其左子树中的关键字最大不超过\(x.key\),其右子树中的关键字最小不低于\(x.key\)。即:
设x是二叉搜索中的一个结点。如果y是x左子树中的任意结点,那么\(y.key<=x.key\)。如果y是x右子树中的任意结点,那么\(y.key>=x.key\)。
中序遍历
中序遍历可以按序输出二叉搜索树中的所有关键字。
public void inorderTreeWalk(Node x){
if(x != null){
inorderTreeWalk(x.left);
System.out.println(x.key);
inorderTreeWalk(x.right);
}
}
查询二叉搜索树
高度为h的二叉搜索树上进行SEARCH(普通查询)、MINIMUM(最大关键字)、MAXIMUM(最小关键字)、SUCCESSOR(后继)和PREDECESSOR(前驱)等查询操作,都可以在\(O(h)\)世间内完成。
- 普通查找
/**
* 递归实现的普通查找
* @param x 二叉搜索树的根结点
* @param key 查询结点的关键字
*/
public Node treeSearch(Node x, int key) {
if (x == null || key == x.key)
return x;
if (key < x.key) {
return treeSearch(x.left, key);
} else {
return treeSearch(x.right, key);
}
}
/**
* 迭代实现的普通查找
*/
public Node iterativeTreeSearch(Node x, int key) {
while (x != null && key != x.key) {
if (key < x.key) {
x = x.left;
} else {
x = x.right;
}
}
return x;
}
- 最大关键字元素和最小关键字元素
/**
* 获取最小关键字元素
* @param x 查询二叉树根结点
*/
public Node treeMinimum(Node x) {
while (x.left != null) {
x = x.left;
}
return x;
}
/**
* 获取最大关键字元素
*/
public Node treeMaximum(Node x) {
while (x.right != null) {
x = x.right;
}
return x;
}
-
后继和前驱
按中序遍历次序查找一个结点的后继和前驱。一个结点x的后继是大于x.key的最小关键字的结点,前驱是小于x.key的最大关键字的结点。
/**
* 返回一个结点的后继
*/
public Node successor(Node x) {
if (x.right != null) {
return treeMinimum(x.right);
}
Node y = x.p;
while (y != null && x == y.right) {
x = y;
y = y.p;
}
return y;
}
- 练习
12.2-5 证明:如果一颗二叉搜索树中的一个结点有两个孩子,那么它的后继没有左孩子,它的前驱没有右孩子。
证明如下: 根据二叉搜索树的性质,如果一个结点存在右子结点,则这个结点的后继是其右子树中的最小结点;假设这个结点的后继有左孩子,说明该后继结点不是右子树中的最小结点,即假设不成立,即这个结点的后继没有左孩子。同理可证明这个结点前驱没有右孩子。
插入和删除
- 插入
/**
* 插入一个结点
*
* @param tree 二叉搜索树
* @param z 待插入的结点
*/
public void treeInsert(BinarySearchTree tree, Node z) {
Node y = null;
Node x = tree.root;
while (x != null) {
y = x;
if (z.key < x.key) {
x = x.left;
} else {
x = x.right;
}
}
z.parent = y;
if (y == null) {
tree.root = z; // 二叉搜索树为空
} else if (z.key < y.key) {
y.left = z;
} else {
y.right = z;
}
}
- 删除
/**
* 在tree中删除结点z
*/
public void treeDelete(BinarySearchTree tree, Node z) {
if (z.left == null) {
transplant(tree, z, z.right);
} else if (z.right == null) {
transplant(tree, z, z.left);
} else {
Node y = treeMinimum(z.right);
if (y != z.right) {
transplant(tree, y, y.right);
y.right = z.right;
y.right.parent = y;
}
transplant(tree, z, y);
y.left = z.left;
y.left.parent = y;
}
}
/**
* 在tree中,用一棵以v为根的子树替换一棵以u为根的子树
* 替换完后,结点u的父结点变为结点v的父结点,并且结点v成为u的父结点的相应子结点
* 注意,这里并没有处理v的子结点更新;这些更新由此方法调用者来负责。
*/
public void transplant(BinarySearchTree tree, Node u, Node v) {
if (u.parent == null) {
tree.root = v;
} else if (u.parent.left == u) {
u.parent.left = v;
} else {
u.parent.right = v;
}
if (v != null) {
v.parent = u.parent;
}
}