目录
1、什么是二叉搜索树?
二叉搜索树也叫作二叉排序树,可以使一颗空树,也可以是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树的所节点的值都小于根节点的值
- 若它的右子树不为空,则右子树的所节点的值都大于根节点的值
- 它的左右子树也分别是二叉搜索树
举例:
2、手动模拟二叉搜索树
2.1、整体代码
public class BinarySearchTree {
static class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
//搜索树的根
public TreeNode root;
/**
* 查找key是否存在于二叉搜索树当中
* @param key
* @return 当前节点找到返回地址,否则返回null
*/
public TreeNode search(int key) {
}
/**
* 插入节点
* @param key
*/
public boolean insert(int key) {
}
/**
* 删除节点
* @param key
*/
public void remove(int key) {
}
}
2.2、查找数据
代码:
/**
* 查找key是否存在于二叉搜索树当中
* @param key
* @return 当前节点找到返回地址,否则返回null
*/
public TreeNode search(int key) {
TreeNode cur = root;
while(cur != null) {
if(cur.val > key) {
cur = cur.left;
} else if(cur.val < key) {
cur = cur.right;
} else {
return cur;
}
}
return null;
}
2.3、插入数据
- 树为空,直接插入
- 树不为空,按照查找逻辑确定插入位置,如下:
代码:
/**
* 插入节点
* @param key
*/
public boolean insert(int key) {
TreeNode node = new TreeNode(key);
if(root == null) {
root = node;
return true;
}
TreeNode cur = root;
TreeNode parent = null;
while (cur != null) {
if(cur.val < key) {
parent = cur;
cur = cur.right;
} else if(cur.val > key) {
parent = cur;
cur = cur.left;
} else {
//相等,搜索树中,无法存放值相等的节点
return false;
}
}
//cur == null
if(key > parent.val) {
parent.right = node;
} else {
parent.left = node;
}
return true;
}
2.4、删除数据
设待删除结点为
cur,
待删除结点的双亲结点为
parent
1. cur.left == null
- cur 是 root,则 root = cur.right
- cur 不是 root,cur 是 parent.left,则 parent.left = cur.right
- cur 不是 root,cur 是 parent.right,则 parent.right = cur.right
2. cur.right == null
- cur 是 root,则 root = cur.left
- cur 不是 root,cur 是 parent.left,则 parent.left = cur.left
- cur 不是 root,cur 是 parent.right,则 parent.right = cur.left
3. cur.left != null && cur.right != null
- 需要使用替换法进行删除,即在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题
代码:
/**
* 删除节点
* @param key
*/
public void remove(int key) {
TreeNode cur = root;
TreeNode parent = null;
while (cur != null) {
if(cur.val < key) {
parent = cur;
cur = cur.right;
} else if(cur.val > key) {
cur = cur.left;
} else {
//该节点需要删除
//1、cur.left == null
if(cur.left == null) {
if(cur == root) {
root = cur.right;
} else if(cur == parent.left) {
parent.left = cur.right;
} else {
parent.right = cur.right;
}
}//2、cur.right = null
else if(cur.right == null) {
if(cur == root) {
root = cur.left;
} else if(cur == parent.left) {
parent.left = cur.left;
} else {
parent.right = cur.left;
}
}
//3、cur.left != null && cur.right != null
else {
TreeNode targetParent = cur;
TreeNode target = cur.right;
//替换时,可以找左子树的最大值,也可以是右子树的最小值
//我这里是,左子树的最大值
while (target.left != null) {
targetParent = target;
target = target.left;
}
cur.val = target.val;
if(targetParent.left == target) {
targetParent.left = target.right;
} else {
targetParent.right = target.right;
}
}
}
}
}
3、性能分析
- 插入操作和删除操作都会必须先查找,查找效率代表了二叉搜索树中各个操作的性能
- 在二叉搜索树中,如果每个元素查找的概率相等,则二叉搜索树平均查找的长度是结点在二叉搜索树的深度的函数,即节点越多,比较的次数越多
- 对于同一个关键码集合,若插入次序不同,就可能会得到结构不同的二叉搜索树,如下:
这时,最优情况下,二叉搜索树为完全二叉树,平均比较次数为logN
最差情况下,二叉搜索树退化为单支树,平均比较次数为N/2
好啦,我们下期见咯~~~【AVL树】