Binary Search Tree
目录
概述
二叉搜索树的操作,包括插入和删除等。
二叉搜索树,又叫二叉查找树。若想使得二叉树成为二叉查找树,则对于树中的每个节点X
- 它的左子树中所有关键字值小于X的关键字值
- 它的右子树中所有关键字值大于X的关键字值
下图就是一个BST
可以看到,BST中的元素按照一定的规律排列。由于树本身就是递归定义的,因此通常对树的操作都是用递归来做,当然也可以利用循环实现。
Search
因为BST的性质,查找操作还是比较简单的,假设树根为T,查找元素值为X:
- 如果T是NULL,那么直接返回NULL,否则
- 如果T的关键字是X,那么返回T,否则
- 对T的左子树或者右子树进行一次递归调用。
时间复杂度: O(logN)
代码如下:
public static BinarySearchTree find(Integer ele, BinarySearchTree root) {
if(root == null) return null;
if(ele < root.element) return find(ele, root.lchild);
if(ele > root.element) return find(ele, root.rchild);
return root;
}
Insert
插入也是很简单的
- 比树根关键字值小就往左子树插入,否则
- 比树根关键字值大就往右子树插入,否则
- 那肯定就是相等啦,啥也不干(树中元素关键字不可以重复)
public static BinarySearchTree insert(Integer ele, BinarySearchTree root) {
if(root == null) {
root = new BinarySearchTree();
root.element = ele;
root.lchild = root.rchild = null;
}
else if(ele < root.element) root.lchild = insert(ele, root.lchild);
else if(ele > root.element) root.rchild = insert(ele, root.rchild);
//if(ele == root.element) do nothing
return root;
}
// output
preOrder:
6 2 1 5 3 4 8
inOrder:
1 2 3 4 5 6 8
postOrder:
1 4 3 5 2 8 6
Delete
BST最麻烦的就是删除了,因为删除一个元素可能会破坏BST的特性,所以一般情况下都需要重新调整。
这里主要分三种情况:
- 删除的节点x是叶子节点,那么可以立刻删除,否则
- 删除的节点x只有一个孩子,则x的双亲节点指针直接指向x的孩子即可,否则
- 删除的节点x有两个孩子,那么
主要说明一下有两个孩子的情况,如上图所示,想要删除节点2,那么
- 首先找到节点2的右子树中元素最小的节点
- 得到节点3
- 把节点3的关键字值赋给节点2
- 删除节点3,也就是把节点4赋给节点3
/*
* 删除树中元素关键字值最小的节点,返回删除后的树
*/
public static BinarySearchTree deleteMin(BinarySearchTree root) {
if(root.lchild == null) return root.rchild;
root.lchild = deleteMin(root.lchild);
return root;
}
public static BinarySearchTree delete(Integer ele, BinarySearchTree root) {
BinarySearchTree tmpBst = null;
if(root == null) return null;
if(ele < root.element){
root.lchild = delete(ele, root.lchild);
} else if(ele > root.element) {
root.rchild = delete(ele, root.rchild);
} else { // 找到了节点了
if(root.lchild != null && root.rchild != null) { // 待删除节点有两个孩子
tmpBst = findMin(root.rchild); // 找到右子树中最小的元素
root.element = tmpBst.element; // 替换
root.rchild = deleteMin(root.rchild); // 更新右子树为删除最小元素后的树
} else { // 没有或者只有一个孩子
tmpBst = root;
if(root.lchild == null) root = root.rchild;
else if(root.rchild == null) root = root.lchild;
tmpBst = null; // for gc
}
}
return root;
}
// delete 2 output
preOrder:
6 3 1 5 4 8
inOrder:
1 3 4 5 6 8
postOrder:
1 4 5 3 8 6
Summary
BST的插入和删除的平均时间在O(logN),这只是平均时间。如果是一串预先排好序的数据插入BST,那么它会退化成一根链表,实现的代价巨大,所以才有平衡树的出现,保证任何节点的深度不会过深。前面已经讨论过AVL,还有更复杂的红黑树,有兴趣的可以研究下。