二叉搜索树
二叉搜索树不是空树就包含以下性质:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值。
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值。
- 它的左右子树也分别为二叉搜索树。
也就是说根节点左边比根节点小右边比根节点大。
二叉搜索树的模拟实现
一般实现的二叉搜索树中都不含有相同值的节点。
接口实现
实现的接口(成员方法)如下:
public class BinarySearchTree {
// 插入一个元素
public boolean insert(int key) {}
//查找key是否存在
public TreeNode search(int key) {}
//删除key的值
public boolean remove(int key) {}
}
成员变量
我们使用内部类来表示二叉树的每一个节点。并定义出来根节点。
static class TreeNode {
public int key;
public TreeNode left;
public TreeNode right;
TreeNode(int key) {
this.key = key;
}
}
public TreeNode root;
插入一个元素
逻辑就是插入一个元素总能在空节点位置找到该插入的位置。
就是下图中由黑线连接的空节点。
实现思路:
- 如果当前树为空则插入节点为根节点。
- 依次向下找到该插入的位置,同时要获得该位置的父亲节点。
- 最后插入。
注意:**一般实现的二叉搜索树中都不含有相同值的节点。**所以插入已有元素是不会成功的。
// 插入一个元素
public boolean insert(int key) {
TreeNode node = new TreeNode(key);
if(root == null) root = node;
TreeNode pre = null;
TreeNode cur = root;
while (cur != null){
if(cur.key == key) {
return false;
} else if(cur.key > key) {
pre = cur;
cur = cur.left;
} else {
pre = cur;
cur = cur.right;
}
}
if(pre.key > key) pre.left = node;
if(pre.key < key) pre.right = node;
return true;
}
查找key是否存在
逻辑与二分查找类似。
代码实现思路:
- 当前节点等于key,返回当前节点。
- 当前节点比key大就去左子树找。
- 当前节点比key小就去右子树找。
- 都没找到返回空。
//查找key是否存在
public TreeNode search(int key) {
TreeNode cur = root;
while(cur != null) {
if(cur.key == key) return cur;
if(cur.key < key) cur = cur.right;
if(cur.key > key) cur = cur.left;
}
return null;
}
查询分析:
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:logN。
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N / 2。
删除key的值
实现思路:
-
设待删除结点为 cur, 待删除结点的双亲结点为 pre。
-
cur.left == null:
1) cur 是 root,则 root = cur.right,
2)cur 不是 root,cur 是 parent.left,则 parent.left = cur.right,
3)cur 不是 root,cur 是 parent.right,则 parent.right = cur.right。 -
cur.right == null:
1) cur 是 root,则 root = cur.left,
2)cur 不是 root,cur 是 parent.left,则 parent.left = cur.left,
3)cur 不是 root,cur 是 parent.right,则 parent.right = cur.left。 -
cur.left != null && cur.right != null:
两个思路:在左子树找最大值进行替换,右子树找最小值进行替换。
//删除key的值
public boolean remove(int key) {
TreeNode node = new TreeNode(key);
if(root == null) throw new NullPointerException();
TreeNode pre = null;
TreeNode cur = root;
while (cur != null){
if(cur.key == key) {
removeNode(cur,pre);
return true;
} else if(cur.key > key) {
pre = cur;
cur = cur.left;
} else {
pre = cur;
cur = cur.right;
}
}
return false;
}
private void removeNode(TreeNode cur, TreeNode pre) {
if(cur.left == null) {
if(cur == root) {
root = cur.right;
} else if(cur == pre.right) {
pre.right = cur.right;
} else {
pre.left = cur.right;
}
} else if(cur.right == null) {
if(cur == root) {
root = cur.left;
} else if(cur == pre.right) {
pre.right = cur.left;
} else {
pre.left = cur.left;
}
} else {
//找到右树最小值替换
TreeNode target = cur;
pre = cur;
cur = cur.right;
while (cur.left != null) {
pre = cur;
cur = cur.left;
}
//排除待删除节点左子树为空
if(pre.right == cur) {
pre = cur;
return;
}
target.key = cur.key;
cur = null;
}
}
TreeMap 和 TreeSet 即 java 中利用搜索树实现的 Map 和 Set;实际上用的是红黑树,而红黑树是一棵近似平衡的二叉搜索树,即在二叉搜索树的基础之上 + 颜色以及红黑树性质验证。
练习
题目描述:
题解
解题思路:
要有一个前驱pre节点。 递归到上一级的时候 当前的根节点(pRootOfTree)就是pre的后继。
按中序遍历来遍历二叉搜索树即可。
public class Solution {
TreeNode pre = null;
TreeNode head = null;
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree ==null) return null;
Convert(pRootOfTree.left);
if(head == null) {
head = pRootOfTree;
}
if(pre != null) {
pRootOfTree.left=pre;
pre.right=pRootOfTree;
}
pre=pRootOfTree;
Convert(pRootOfTree.right);
return head;
}
}