概念:
- 二叉搜索树又称二叉排序树,它或是一颗空树,或者具有如下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
操作-查找:
- 根据二叉搜索树的性质,我们让find指向根结点,进行循环判断,我们要找的key如果比find的key小,我们就往find的左子树找,如果key比find的key大,我们就往find的右子树找。
实现:
//查找
public Node search(int key) {
Node find = this.root;
while (find != null) {
if(key < find.key) {
find = find.left;
} else if(key > find.key) {
find = find.right;
} else {
//找到了
return find;
}
}
return null;
}
操作-增加:
- 在新增结点的过程中,由于二叉搜索树的性质,我们需要为我们要新加的结点找合适的位置,所以就会出现俩种情况
- 再找合适位置的时候发现此时二叉搜索树已有待插入结点的key,此时我们需要将旧的value进行覆盖。
- 找到我们要插入结点的父节点,此时我们需要进行判断如果待插入的key比父节点的key大,我们就是插到父结点的right,反之,插到父结点的left。
实现:
//增加
public boolean insert(int key, int value) {
if(this.root == null) {
//如果此时为空树
this.root = new Node(key, value);
return true;
}
Node find = root;
Node prev = null;
while (find != null) {
if(key < find.key) {
prev = find;
find = find.left;
} else if (key > find.key) {
prev = find;
find = find.right;
} else {
//key相等,我们使value覆盖
find.value = value;
return true;
}
}
//结束循环,说明此时找到要插入该结点的父节点
Node newNode = new Node(key, value);
if(key < prev.key) {
prev.left = newNode;
}else {
prev.right = newNode;
}
return true;
}
假设我们要插入的数据为:
int[] array = new int[] {10,1,1,3,0,4,6,7,9};
结果便是:
操作-删除(难点):
- 我们假设cur是待删除的结点,parent就是待删除结点的父结点
- 找到待删除结点和待删除结点的父结点。
- 进行分类讨论删除操作,我们主要分为三个大类第一待删除结点的左子树为空,第二待删除结点的右子树为空,第三待删除结点的左右子树都不为空。具体我们进行如下操作
实现:
//删除
public void delete (int key) {
if(this.root == null) {
return;
}
Node find = this.root;
Node parent = null;
while (find != null) {
if(key < find.key) {
parent = find;
find = find.left;
} else if (key > find.key) {
parent = find;
find = find.right;
} else {
//找到了要删除的结点
//分为三大类,find左子树为空,右子树为空,都不为空
if(find.left == null) {
//待删除结点的左子树为空,分三种结果1。是根结点 2。是parent的左结点 3.是parent的右节点
if(find == this.root) {
this.root = find.right;
return;
} else if (find == parent.left) {
parent.left = find.right;
return;
} else {
parent.right = find.right;
return;
}
} else if (find.right == null) {
//带删结点的右子树为空,分三种结果1。是根结点 2。是parent的左结点 3.是parent的右节点
if (find == this.root) {
this.root = find.left;
return;
} else if (find == parent.left) {
parent.left = find.left;
return;
} else {
parent.right = find.left;
return;
}
} else {
//待删除结点左右都不为空,我们找到其左子树的最小结点,与待删除结点就行赋值,然后删除最小结点
Node goatParent = find;
Node goat = find.right;
while (goat.left != null) {
goatParent = goat;
goat = goat.left;
}
//就行赋值
find.key = goat.key;
find.value = goat.value;
//删除这个最小值的结点,分俩种情况,1.goat是goatparent的左结点2.goat是goatparent的右节点
if(goat == goatParent.left) {
//那么goat的左子树一定为空
goatParent.left = goat.right;
return;
} else {
//如果goat是goatparent的右节点,那么goatparent一定还指向find结点,而且此时goat的左结点还是为空
goatParent.right = goat.right;
return;
}
}
}
}
}
测试:
public class Test {
public static void preOrder(BinarySearchTree.Node root) {
if (root == null) {
return;
}
System.out.print(root.key + " ");
preOrder(root.left);
preOrder(root.right);
}
public static void inOrder(BinarySearchTree.Node root) {
if (root == null) {
return;
}
inOrder(root.left);
System.out.print(root.key + " ");
inOrder(root.right);
}
public static void main(String[] args) {
BinarySearchTree tree = new BinarySearchTree();
int[] array = new int[] {10,1,1,3,0,4,6,7,9};
for (int i : array
) {
System.out.println(tree.insert(i, 90));
}
preOrder(tree.root);
//System.out.println();
//inOrder(tree.root);
//System.out.println(tree.search(8).key);
tree.delete(6);
tree.delete(10);
tree.delete(3);
tree.delete(9);
System.out.println();
preOrder(tree.root);
}
}
结果:
true
true
true
true
true
true
true
true
true
10 1 0 3 4 6 7 9
1 0 4 7
性能分析:
- TreeSet TreeMap底层实现就是二叉搜索树(考虑了平衡性,红黑树)插入 、查找、删除操作的时间复杂度都是O(logN)
- 如果没有考虑平衡性,插入查找删除就是O(N)