定义:
一颗二叉查找树是一颗二叉树,其中每个结点都包含一个Comparable的键(以及向关联的值)且每个结点的键都大于其左子树中的任意结点的键而小于右子树中任意结点的键
插入实现:
先递归找出新结点插入的位置;逻辑是如果树是空的,就返回一个含有该键值对的新节点,如果要插入的键小于根结点的键,就继续在左子树递归查找插入改键,否则在右子树中插入该键
private Node put(Node x,Key key,Value value) {
if(x==null)
return new Node(key,value);
int cmp = key.compareTo(x.key); //找出要插入的结点在二叉树中的位置
if(cmp < 0)
x.left = put(x.left,key,value);
else if(cmp > 0)
x.right = put(x.right,key,value);
return x;
}
删除:
二叉查找树用递归方法实现的话,我们在方法参数里面接受一个指向结点的链接,然后返回一个指向结点的链接。
第一个方法 deleteMin():删除一棵树中的最小结点,输入树的根结点,返回删除之后的替换结点
private Node deleteMin(Node x) {
if(x.left==null)
return x;
x.left = deleteMin(x.left);
return x;
}
不断遍历根结点的左子树直到遇见一个空链接,然后将指向该结点(即找到的左子树为空的结点)的链接指向该结点的右子树;此时已经没有任何链接指向要被删除的结点,因此它就会被垃圾收集器清理掉
第二个方法 delete(Node x,Key key):
如果要删除的结点只有一个子树,我们可以直接用上面的方法类似的原理直接删除;但是如果要删除的结点有两个子树就要考虑删除之后保证二叉树的平衡;主要方法就是用待删除结点x的后继节点来替换x的位置;因为x右子树不为空,因此它的后继节点就是其右子树中的最小节点。
主要分四个步骤:
- 将指向被删除结点的链接保存为t
- 设置被删除结点的后继结点min(.right)为x设置被删除结点的后继结点min(.right)为x
- 将x的右链接(原本指向一颗所有结点都大于x.key的二叉查找树)指向dele-teMin(t.right),也就是用deleteMin(t.right)来作为替换结点的右子树
- 再让x的左链接指向被删除结点的左子树(t.left),因为主要操作在右子树,左子树无需变化
private Node delete(Node x,Key key) { //Node为根节点,Key即为要删除的节点的值
if(x==null)
return null;
int cmp = key.compareTo(x.key);
if(cmp < 0) //找到要删除的节点的位置
x.left = delete(x.left,key);
else if(cmp > 0)
x.right = delete(x.right,key);
else
{
//判断删除节点是否有两个孩子
if(x.right == null) //如果只有一个孩子,则直接返回另一个孩子的链接,即用x的孩子直接替换x节点
return x.left;
if(x.left == null)
return x.right;
Node t = x; //将x赋给t
x = min(t.right); //用x的后继节点替换x
//用被删除节点删除后继节点(后继节点原本存在于被删除节点的右子树中)之后的右子树来作为替换节点的右子树
x.right = deleteMin(t.right);
x.left = t.left; //把被删除节点的左子树作为替换节点的左子树
}
return x;
}
完整代码
public class BST <Key extends Comparable<Key>,Value>{
private Node root;
private class Node{
private Key key;
private Value value;
private Node left,right;
public Node(Key key,Value value){
this.key =key;
this.value = value;
}
}
public void put(Key key,Value value) {
root = put(root,key,value);
}
public void delete(Key key) {
root = delete(root,key);
}
//查找数中最小节点
private Node min(Node x) {
if(x.left == null) //一直递归遍历二叉查找数的左子树,直到为null
return x;
return min(x.left);
}
private Node deleteMin(Node x) {
if(x.left==null)
return x;
x.left = deleteMin(x.left);
return x;
}
private Node put(Node x,Key key,Value value) {
if(x==null)
return new Node(key,value);
int cmp = key.compareTo(x.key); //找出要插入的结点在二叉树中的位置
if(cmp < 0)
x.left = put(x.left,key,value);
else if(cmp > 0)
x.right = put(x.right,key,value);
return x;
}
private Node delete(Node x,Key key) { //Node为根节点,Key即为要删除的节点的值
if(x==null)
return null;
int cmp = key.compareTo(x.key);
if(cmp < 0) //找到要删除的节点的位置
x.left = delete(x.left,key);
else if(cmp > 0)
x.right = delete(x.right,key);
else
{
//判断删除节点是否有两个孩子
if(x.right == null) //如果只有一个孩子,则直接返回另一个孩子的链接,即用x的孩子直接替换x节点
return x.left;
if(x.left == null)
return x.right;
Node t = x; //将x赋给t
x = min(t.right); //用x的后继节点替换x
//用被删除节点删除后继节点(后继节点原本存在于被删除节点的右子树中)之后的右子树来作为替换节点的右子树
x.right = deleteMin(t.right);
x.left = t.left; //把被删除节点的左子树作为替换节点的左子树
}
return x;
}
}
输出方方法可以自己选择遍历方式实现,和二叉树一样