一、定义
二叉查找树(BST)是一颗二叉树,其每个节点的值都比左孩子的任意节点大,比右孩子的任意节点小。
二、数据结构
public class BinarySearchTree<T extends Comparable<? super T>> {
private TreeNode<T> root;
public BinarySearchTree(TreeNode<T> root) {
this.root = root;
}
}
class TreeNode<T>{
T data;
TreeNode lchild;
TreeNode rchild;
public TreeNode(T data) {
this(data,null,null);
}
public TreeNode(T data, TreeNode lchild, TreeNode rchild) {
this.data = data;
this.lchild = lchild;
this.rchild = rchild;
}
}
三、contains方法
如果该树存在数据X则返回true,否则返回false。
我们只需要从根节点开始遍历,如果当前节点小于X,则向左递归,否则向右递归,直到找到X或到达树的最后。
public boolean contains(T x){
return contains(x,root);
}
private boolean contains(T x, TreeNode<T> root) {
if (root==null){
return false;
}
int result=x.compareTo(root.data);
if (result<0){
return contains(x,root.lchild);
}else if (result>0){
return contains(x,root.rchild);
}else {
return true;
}
}
四、查找最小值和最大值
一颗二叉查找树,其最小值一定是按左子树递归找到,直到树底
1.如果根节点为空,则返回空
2.如果左子树为空,则返回根节点
3.否则从左孩子开始,继续从1递归。
public TreeNode<T> findMax(TreeNode<T> root){
if (root==null){
return null;
}
while (root.rchild!=null){
root=root.rchild;
}
return root;
}
查找最大值则相反,从根节点向右子树循环查找。
public TreeNode<T> findMax(TreeNode<T> root){
if (root==null){
return null;
}
while (root.rchild!=null){
root=root.rchild;
}
return root;
}
五、插入一个节点
同样可以使用contains那种递归的方式。
如果找到X,则节点已存在,什么都不做;否则就插入到遍历路径上的最后一个节点。
public TreeNode<T> insert(T x){
if (x==null){
throw new IllegalArgumentException("参数为空");
}
return insert(x,root);
}
private TreeNode<T> insert(T x,TreeNode<T> t){
//当前节点为空,构建一个新的节点插入x
if (t==null){
return new TreeNode<>(x);
}
int result=x.compareTo(t.data);
if (result<0){
//向左递归
t.lchild=insert(x,t.lchild);
}else if (result>0){
//向右递归
t.rchild=insert(x,t.rchild);
}
return t;
}
六、删除一个节点
删除节点X大致可分为三种情况:
- 如果X是叶子节点,则直接删除
- 如果X有一个孩子,则将那个孩子放在当前节点位置
- 如果X有两个孩子,则找到右孩子中最小的一个节点Y,放到待删除节点X的位置,这样X的左孩子们仍然比它小,现在只需要用同样的方式删除右孩子的Y即可。
public void remove(T x){
if (x==null){
return;
}
remove(x,root);
}
/**
* 删除一个节点
* 1.如果是叶子节点,则直接删除
* 2.如果有一个孩子,让当前节点等于那个孩子
* 3.如果有两个孩子,让当前节点的值等于右孩子中最小的一个节点的值,然后再同样的方法删除右孩子最小值的节点
* @param x
* @param root
* @return
*/
private TreeNode<T> remove(T x,TreeNode<T>root){
if (root==null){
return null;
}
int result=x.compareTo(root.data);
if (result<0){
root.lchild=remove(x,root.lchild);
}else if (result>0){
root.rchild=remove(x,root.rchild);
}else if (root.lchild!=null&&root.rchild!=null){ //找到待删除节点,且有两个孩子
//让当前节点值等于右孩子最小的节点值
root.data= (T) findMin(root.rchild).data;
//在右孩子中删除上一步找到的最小的值
root.rchild=remove(root.data,root.rchild);
}else {
root=root.lchild==null?root.rchild:null;
}
return root;
}
求节点的高度就很简单了:
public int height(TreeNode<T> root){
if (root==null){
return -1;
}
return 1+Math.max(height(root.lchild),height(root.rchild));
}
总结:
使用二叉查找树的运行时间取决于树的形态,而树的形态又取决于数据被插入的顺序。因此最好的情况下是一颗完全二叉树;但最坏情况下,搜索路径上有N个节点,即左右节点都在一条路径下排开。
最坏情况下运行时间的增长数量级:
查找: N
插入: N
平均情况下:
查找命中:1.39lgN
插入: 1.39lgN
支持有序性相关操作