一、定义
二叉搜索树:二叉搜索树(Binary Search Tree),它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
二、具体操作
1、查找。
2、插入。
3、删除。具体分析如下图所示
4.一般不建议修改。因为对于二叉搜索树来说,修改操作不能针对key修改,最多修改value。如果实在要修改key,可以先删key,再插入一个新的key
三、特性
1.二叉搜索树的中序遍历是一个有序序列
2.最优情况下平均比较次数:log2N,最差情况下平均比较次数:N/2
3.代码实现的圈复杂度非常高
四、代码实现
public class BinarySearchTree {
public static class Node{
public int key;
public int value;
Node left;
Node right;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return "Node [key=" + key + ", value=" + value + "]";
}
}
//创建一个根节点,初始情况为空树,根节点指向null
private Node root = null;
//查找
public Node search(int key) {
//从root出发,比较key和root中key的大小关系
//根据这个关系决定接下来在左子树中找还是右子树中找
//如果遇到null,说明查找结束
Node cur = root;
while(cur != null) {
if(key == cur.key) {
//找到了
return cur;
}else if (key < cur.key) {
//左子树找
cur = cur.left;
}else {
//右子树找
cur = cur.right;
}
}
//没找到
return null;
}
//插入
//1.先找到合适位置
//2.把新节点放入该位置
//插入失败约定方式:
//(1)如果当前key已存在,则插入失败(TreeSet)
//(2)如果当前key存在,直接修改value(TreeMap)
public boolean insert(int key,int value) {
//1.如果当前为空树,直接用root指向新节点
if(root == null) {
root = new Node(key, value);
return true;
}
//2.非空树,先找合适的位置,查找过程中随时记录当前节点的父节点
Node cur = root;
//parent一直指向cur的父节点
Node parent = null;
while(cur != null) {
if(key == cur.key) {
//找到了,按照约定方式一解决
return false;
}else if (key < cur.key) {
//左子树中找
parent = cur;
cur = cur.left;
}else {
//右子树中找
parent = cur;
cur = cur.right;
}
}
//循环结束,cur一定是null,新节点应当插入到parent下方
//循环操作的关键就在于:找到parent是谁
Node newNode = new Node(key, value);
if(key < parent.key) {
//插入到左子树的位置
parent.left = newNode;
}else {
//插入到右子树的位置
parent.right = newNode;
}
return true;
}
//删除
public boolean remove(int key) {
Node cur = root;
Node parent = null;
//查找要删除的元素的位置
while(cur!=null) {
if(key == cur.key) {
//找到了
removeNode(parent,cur);
return true;
}else if (key < cur.key) {
//向左找
parent = cur;
cur = cur.left;
}else {
//向右找
parent = cur;
cur = cur.right;
}
}
//未找到对应节点
return false;
}
//辅助删除函数
private void removeNode(Node parent,Node cur) {
//1.没有左子树
if(cur.left == null) {
if(cur == root) {
//1.1 直接让root指向cur.right
root = cur.right;
}else if (cur==parent.left) {
//1.2 直接让parent的left指向cur的right
parent.left = cur.right;
}else {
//1.3 直接让parent的right指向cur的right
parent.right = cur.right;
}
}else if (cur.right == null) {
//2.没有右子树
if(cur == root) {
//2.1 直接让root指向cur.left
root = cur.left;
}else if (cur==parent.left) {
//2.2 直接让parent的left指向cur的left
parent.left = cur.left;
}else {
//1.3 直接让parent的right指向cur的left
parent.right = cur.left;
}
}else {
//3.左右子树都不为空
Node scapeGoat = cur.right;
Node goatParentNode = cur;
//3.1 去待删除节点的右子树找最左侧元素
while(scapeGoat.left!=null) {
goatParentNode = scapeGoat;
scapeGoat = scapeGoat.left;
}
//循环结束,scapeGoat就是替罪羊结点
//3.2 把替罪羊的key、value赋给待删除节点
cur.key = scapeGoat.key;
cur.value = scapeGoat.value;
//3.3 删除替罪羊结点,它一定没有左子树
// 还需根据替罪羊是父节点左还是右,进一步判断如何删除
if(scapeGoat == goatParentNode.left) {
//替罪羊节点是父亲的左子树
goatParentNode.left = scapeGoat.right;
}else{
//当前替罪羊节点正好是cur.right
goatParentNode.right = scapeGoat.right;
}
}
}
public static void main(String[] args) {
BinarySearchTree tree = new BinarySearchTree();
int[] arr = {9,5,2,7,3,6,8};
for (int x : arr) {
tree.insert(x,0);
}
tree.remove(5);
preOrder(tree.root);
System.out.println();
inOrder(tree.root);
System.out.println();
System.out.println(tree.search(9));
}
public static void preOrder(Node root) {
if(root == null) {
return;
}
System.out.print(root.key+" ");
preOrder(root.left);
preOrder(root.right);
}
public static void inOrder(Node root) {
if(root == null) {
return;
}
inOrder(root.left);
System.out.print(root.key+" ");
inOrder(root.right);
}
}