二叉查找树中,每个节点的左子树中的所有节点都小于该节点,而其右子树中的所有节点都大于该节点。通常情况下,二叉树的平均深度都约为C*log(N),C为常数。所以其读取、插入删除的复杂度都为log(N)。但是在极端情况下二叉查找树会退化为一条链表,比如将一系列已经排好序的节点插入一棵树中时。
二叉查找树的Java实现如下。
public class BinarySearchTree<Key extends Comparable<Key>, Value> {
private Node root;
private class Node {
private Key k;
private Value v;
private Node left;
private Node right;
private int size;
public Node(Key k, Value v, int size) {
this.k = k;
this.v = v;
this.size = size;
}
}
/**
* 在树中插入一键值对,先查询键存不存在,若存在,就更新对应的值
* 若不存在,就创建一个新的结点
* 若v值为null,就删除相应的键
* @param k 需要插入的键
* @param v 对应的需要插入的值
*/
public void put(Key k, Value v) {
if(k == null) {
throw new IllegalArgumentException("k can not be null");
}
if(v == null) {
delete(k);
}
root = put(k, v, root);
}
/**
* @param k 需要插入的键
* @param v 对应的需要插入的值
* @param node 在以node为根结点的子树中插入
* @return 插入该键值对之后的子树的根结点
*/
private Node put(Key k, Value v, Node node) {
if(node == null) {
return new Node(k, v, 1);
}
int cmp = k.compareTo(node.k);
if(cmp < 0) {
node.left = put(k, v, node.left);
} else if(cmp > 0) {
node.right = put(k, v, node.right);
} else {
node.v = v;
}
node.size = size(node.left) + size(node.right) + 1;
return node;
}
/**
* 如果键k为空,抛异常
* @param k 需要查找的键
* @return 与键相对应的值,如果树中不存在键k,返回null
*/
public Value get(Key k) {
if(k == null) {
throw new IllegalArgumentException("k can not be null");
}
return get(k, root);
}
/**
* @param k 需要查找的键
* @param node 在以node为根结点的子树中查找
* @return 子树中与键对应的值,若子树中不存在,返回null
*/
private Value get(Key k, Node node) {
if(node == null) {
return null;
}
int cmp = k.compareTo(node.k);
if(cmp == 0) {
return node.v;
} else if(cmp < 0) {
return get(k, node.left);
} else {
return get(k, node.right);
}
}
public void delete(Key k) {
if(k == null) {
throw new IllegalArgumentException("k can not be null");
}
root = delete(k, root);
}
private Node delete(Key k, Node node) {
if(node == null) {
return null;
}
int cmp = k.compareTo(node.k);
if(cmp < 0) {
node.left = delete(k, node.left);
} else if(cmp > 0) {
node.right = delete(k, node.right);
} else {
if(node.left == null) return node.right;
if(node.right == null) return node.left;
Node temp = node;
node = min(temp.right);
node.left = temp.left;
node.right = deleteMin(temp.right);
}
node.size = size(node.left) + size(node.right) + 1;
return node;
}
/**
* @return 二叉树中的最小键
*/
public Key min() {
Node n = min(root);
if(n == null) return null;
return n.k;
}
/**
* @param node 子树的根结点
* @return 以node为根结点的子树中最小键对应的结点
*/
private Node min(Node node) {
if(node == null || node.left == null) {
return node;
}
return min(node.left);
/*
if(node == null)
return node;
Node n = node;
while(n.left != null) {
n = n.left;
}
return n;
*/
}
/**
* @return 二叉树中的最大键
*/
public Key max() {
Node n = max(root);
if(n == null) return null;
return n.k;
}
/**
* @param node 子树的根结点
* @return 以node为根结点的子树中最大键对应的结点
*/
private Node max(Node node) {
if(node == null || node.right == null) {
return node;
}
return max(node.right);
/*
if(node == null) {
return node;
}
Node n = node;
while(n.right != null) {
n = n.right;
}
return n;
*/
}
/**
* 向下取整
* @param k 查找的键
* @return 小于等于键k的最大键
*/
public Key floor(Key k) {
if(k == null) {
throw new IllegalArgumentException("k can not be null");
}
Node n = floor(k, root);
if(n == null)
return null;
return n.k;
}
/**
* 向下取整所对应的键的结点
* @param k 查找的键
* @param node 子树的根结点
* @return 以node为根结点的子树中,小于等于键k的最大键所对应的结点
*/
private Node floor(Key k, Node node) {
if(node == null)
return null;
int cmp = k.compareTo(node.k);
if(cmp == 0) {
return node;
} else if(cmp < 0) {
return floor(k, node.left);
} else {
Node temp = floor(k, node.right);
if(temp == null)
return node;
return temp;
}
}
/**
* 向上取整
* @param k 查找的键
* @return 大于等于键k的最小键
*/
public Key celling(Key k) {
if(k == null) {
throw new IllegalArgumentException("k can not be null");
}
Node n = celling(k, root);
if(n == null)
return null;
return n.k;
}
/**
* 向上取整所对应的键的结点
* @param k 查找的键
* @param node 子树的根结点
* @return 以node为根结点的子树中,大于等于键k的最小键所对应的结点
*/
private Node celling(Key k, Node node) {
if(node == null)
return null;
int cmp = k.compareTo(node.k);
if(cmp == 0) {
return node;
} else if(cmp > 0) {
return celling(k, node.right);
} else {
Node temp = celling(k, node.left);
if(temp == null)
return node;
return temp;
}
}
/**
* @return 二叉树的结点总数
*/
public int size() {
return size(root);
}
/**
* @param node 需要求结点总数的子树的根结点
* @return 以该结点为根的子树中结点的总数
*/
private int size(Node node) {
if(node == null)
return 0;
else
return node.size;
}
/**
* 查询秩为k的键,以0开始计数(即树中从小到大第k+1个键)
* @param k 秩数
* @return 树中秩为k的键
*/
public Key select(int k) {
Node n = select(k, root);
if(n == null)
return null;
return n.k;
}
/**
* 查询秩为k的键
* @param k 序数
* @param node 子树的根结点
* @return 以node为根结点的子树中秩为k的键对应的结点
*/
private Node select(int k, Node node) {
if(node == null)
return null;
int t = size(node.left);
if(k == t)
return node;
else if(k < t)
return select(k, node.left);
else
return select(k-t-1,node.right);
}
/**
* 查询小于键K的个数
* @param k 待查询的键k
* @return 树中小于键k的节点数
*/
public int rank(Key k) {
return rank(k, root);
}
/**
* 在以node为根结点的子树中查询小于键K的个数
* @param k 待查询的键
* @param node 子树的根结点
* @return 树中小于键k的节点数
*/
private int rank(Key k, Node node) {
if(node == null)
return 0;
int cmp = k.compareTo(node.k);
if(cmp < 0)
return rank(k, node.left);
else if(cmp > 0)
return rank(k, node.right) + size(node.left) + 1;
else
return size(node.left);
}
public void deleteMin() {
root = deleteMin(root);
}
private Node deleteMin(Node node) {
if(node == null)
return null;
if(node.left == null)
return node.right;
node.left = deleteMin(node.left);
node.size = size(node.left) + size(node.right) + 1;
return node;
}
public void deleteMax() {
root = deleteMax(root);
}
private Node deleteMax(Node node) {
if(node == null)
return null;
if(node.right == null)
return node.left;
node.right = deleteMax(node.right);
node.size = size(node.left) + size(node.right) + 1;
return node;
}
}