什么是二叉树
简单来说,二叉树首先是一棵树,然后有以下条件
每个节点最多有2个孩子节点
二叉树有
- 一个根节点
- 一个左子树(可能为空)
- 一个右子树(可能为空)
特例
完全二叉树
叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树
满二叉树
一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树
二叉查找树
每个节点有一个键,每个节点的键
* 比它的左子树要大
* 比它的右子树要小
实现
一个Node包含四个元素:
- key和value
- 左子树和右子树的引用
get
类似binary search。
public V get(K key){
Node x = root;
while (x!=null){
int cmp = key.compareTo(x.key);
if(cmp <0 ) x = x.left;
else if(cmp > 0) x = x.right;
else return x.value;
}
return null;
}
put
首先也是对key进行get操作,然后分两种情况:
- key在树里=>重置value
- key不在树里=>增加新节点
public void put(K key,V value){
root = put(root,key,value);
}
private Node put(Node x,K key,V 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);
else
x.value = value;
return x;
}
Floor&ceiling
- Floor 小于等于给定Key的最大的key
- Ceiling 大于等于给定Key的最小的key
计算floor
有三种情况
- k等于root
返回k - k小于root
floor(k)在左子树 - k大于root
floor(k)在右子树:
如果有任何key<=k : 返回key
否则返回root
public Key floor(Key key){
Node x = floor(root,key);
if(x == null) return null;
return x.key;
}
private Node floor(Node x,Key key){
if(x == null) return null;
int cmp = key.compareTo(x.key);
if (cmp ==0 ) return x;
if (cmp <0 ) return floor(x.left,key);
Node t = floor(x.right,key);
if(t!= null) return t;
return x;
}
minimum&maximum
- min: 最左边非空节点
- max: 最右边非空节点
public Key min(){
return min(root).key;
}
private Node min(Node x){
//如果x的左子树为空,那么key最小的节点就是x;否则最小的节点就是左子树的最小节点
if(x.left == null) return x;
return min(x.left);
}
删除最小的节点
- 一直向左直到找到一个节点的左子树为空
- 让它的右子树替换该节点
- 更新子树的数量
public void deleteMin(){
root = deleteMin(root);
}
private Node deleteMin(Node x){
//找到没有左子树的节点,将它的右子树替代它
if(x.left == null) return x.right;
//删除之后,为parent设置合适的链路
x.left = deleteMin(x.left);//因为x.left可能指向了x.left.right
//更新节点的数量
x.N = size(x.left) + size(x.right) + 1;
return x;
}
删除节点
删除key为k的节点:首先查询含有k的节点t。如果找到了,那么分3种情况:
- 无孩子节点
将t设为null即可 - 有一个孩子节点
将t的父节点的指向t的孩子节点即可
有两个孩子节点
- 找到t的后继者x(t的右子树中最小的节点)
- 删除x所在的节点
- 将x替换到t的位置
- > 满足BST的定义
private Node delete(Node x,Key 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) return x.left;
if(x.left == null) return x.right;
//有两个孩子节点
Node t = x;//t临时指向x
x = min(t.right);//x的后继者
//x的后继者替换x的位置
x.right = deleteMin(t.right);
x.left = t.left;
}
x.N = 1 + size(x.left) + size(x.right);
return x;
}
## 完整代码
package com.algorithms.tree;
/**
* Create by yinjingwei on 2018/2/20/020.
*/
public class BST<Key extends Comparable<Key>,Value> {
private Node root;//BST树的根节点
private class Node{
private Key key;
private Value value;
private Node left,right;
private int N; //nodes in subtree rooted here
public Node(Key key,Value value,int N){
this.key = key;
this.value = value;
this.N = N;
}
}
public int size(){
return size(root);
}
private int size(Node x){
if(x == null) return 0;
return x.N;
}
public Value get(Key key){
return get(root,key);
}
private Value get(Node x, Key key){
//从x节点往下查找Key为key的节点,未找到返回null
if(x != null){
int cmp = key.compareTo(x.key);
if( cmp <0 ) return get(x.left,key);
else if(cmp >0) return get(x.right,key);
else return x.value;
}
return null;
}
public void put(Key key,Value val){
root = put(root,key,val);
}
private Node put(Node x, Key key, Value val){
//如果找到了key,则改变相应节点的值;否则插入
if(x == null) return new Node(key,val,1);
int cmp = key.compareTo(x.key);
if( cmp <0 ) x.left = put(x.left,key,val);
else if(cmp >0) x.right = put(x.right,key,val);
else x.value = val;
x.N = size(x.left) + size(x.right) + 1;//会往上递归调用回去
return x;
}
public Key min(){
return min(root).key;
}
private Node min(Node x){
//如果x的左子树为空,那么key最小的节点就是x;否则最小的节点就是左子树的最小节点
if(x.left == null) return x;
return min(x.left);
}
public Key floor(Key key){
Node x = floor(root,key);
if(x == null) return null;
return x.key;
}
private Node floor(Node x,Key key){
if(x==null) return null;
int cmp = key.compareTo(x.key);
if(cmp == 0) return x;
if(cmp<0) return floor(x.left,key);
Node t = floor(x.right,key);
if(t == null) return x;
else return t;
}
public void deleteMin(){
root = deleteMin(root);
}
private Node deleteMin(Node x){
//找到没有左子树的节点,将它的右子树替代它
if(x.left == null) return x.right;
//删除之后,为parent设置合适的链路
x.left = deleteMin(x.left);//因为x.left可能指向了x.left.right
//更新节点的数量
x.N = size(x.left) + size(x.right) + 1;
return x;
}
public void delete(Key key){
root = delete(root,key);
}
/**
* 删除key为k的节点:首先查询含有k的节点t。如果找到了,那么分3种情况:
* 无孩子节点
* 将t设为null即可
* 有一个孩子节点
* 将t的父节点的指向t的孩子节点即可
* 有两个孩子节点
* 找到t的后继者x(t的右子树中最小的节点)
* 删除x所在的节点
* 将x替换到t的位置
* @param x
* @param key
* @return
*/
private Node delete(Node x,Key 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) return x.left;
if(x.left == null) return x.right;
//有两个孩子节点
Node t = x;//t临时指向x
x = min(t.right);//x的后继者
//x的后继者替换x的位置
x.right = deleteMin(t.right);
x.left = t.left;
}
x.N = 1 + size(x.left) + size(x.right);
return x;
}
public void print(){
print(root);
}
private void print(Node x){
if(x == null) return;
print(x.left);
System.out.println(x.key+" ");
print(x.right);
}
}