二叉搜索树基本概念
二叉搜索树(也叫BST树)是一种非常常用的树结构,其衍生出来的其他树如:AVL树,23树,红黑树都具有该树的基本特性。
特性:
1.每一个节点都应该由一个key和value
2.如果一个树不是空树那么当一个节点的左子树不为空时,左子树所有节点的Key值小于当前节点的key,当一个节点的右子树不为空时,右子树所有节点的Key值大于当前节点的key.
二叉搜索树的时间复杂度
设计树的结构是为了更好的查询一个数据,二叉搜索树的特点在于节点的有序性,从左往右key值依次增大,因此我们查找一个数据的时候可以使用二分法来查找数据,但是由于树结构的特点,我们每一次二分查找都不一定能排除调n/2个节点,因此BST树在查询一个节点最好的结构下,时间复杂度看达到log(2)N,但是在最坏的情况下便是一个单链表因此时间复杂度达到了N,因此为了将BST树结构更加合理化变衍生除了AVL,23,红黑等树结构。
二叉树的实现
package bst;
//搜索二叉树
public class BST <Key extends Comparable<Key>,Value>{
private Node root;
public class Node{
//键
private Key key;
//值
//private
private Value val;
//左右节点
//private
private Node left,right;
//该节点子树的节点数,包括自身
private int n;
public Node(Key key,Value val,int n) {
this.key = key;
this.val = val;
this.n = n;
}
}
//测试遍历时用
public Node getRoot() {
return root;
}
public int size() {
return size(root);
}
private int size(Node x) {
if(x==null)
return 0;
else
return x.n;
}
//二叉查找树的查询,类似二分查找
public Value get(Key key) {
return get(root,key);
}
//基于递归的遍历
private Value get(Node node,Key key) {
//没有该节点返回
if (node == null) return null;
//比较
int cmp = key.compareTo(node.key);
//key小于节点的key时
if (cmp<0) return get(node.left,key);
//key大于节点Key的
else if(cmp>0) return get(node.right, key);
//相等返回结果
else return node.val;
}
//插入
public void put(Key key,Value val) {
root = put(root,key,val);
}
//插入节点递归
private Node put(Node node,Key key,Value val) {
//如果是空节点则创建一个节点作为根节点
if(node==null) return new Node(key, val,1);
//比较
int cmp = key.compareTo(node.key);
//小于节点的‘key->左子树
if (cmp<0) node.left = put(node.left,key,val);
//大于节点的key->右子树
else if(cmp>0) node.right = put(node.right,key,val);
//如果key相等则直接覆盖值
else node.val = val;
//同时如果有新增的节点则要重新计算该子树的节点树
node.n = size(node.left)+size(node.right)+1;
return node;
}
//查询该树的小Key
public Key min() {
return min(root).key;
}
private Node min(Node node) {
if(node.left == null) return node;
else return min(node.left);
}
//查询该树的大Key
public Key max() {
return max(root).key;
}
public Node max(Node node) {
if(node.right==null) return node;
else return max(node.right);
}
//查询排名为第i的键
public Key select(int n) {
return select(root, n).key;
}
//遍历,从0开始,因此n表示比自己小的个数
public Node select(Node node,int n) {
if (node==null) return null;
int t = size(node.left);
//有t个节点大于排名n所在的节点
if (t>n) return select(node.left, n);
//当右子树+左子树
else if(t<n) return select(node.right,n-t-1);
return node;
}
public int rank(Key key) {
return rank(root,key);
}
//排名从0开始
private int rank(Node node,Key key) {
if(node==null) return 0;
int cmp = key.compareTo(node.key);
if(cmp < 0) return rank(node.left,key);
else if(cmp>0) return 1+size(node.left)+rank(node.right,key);
else return size(node.left);
}
public void deleteMin() {
root = deleteMin(root);
}
//删除最小的Key,与删除最大的类似
public Node deleteMin(Node node) {
//如果该节点已经是最小的节点了,返回他的右子树作为节点的新的左子树
if(node.left == null) return node.right;
node.left = deleteMin(node.left);
node.n = size(node.left)+size(node.right)+1;
return node;
}
public void delete(Key key) {
root = delete(root,key);
}
//删除一个节点
private Node delete(Node node,Key key) {
if(node==null) return null;
int cmp = key.compareTo(node.key);
//找到该节点
if(cmp>0) node.right =delete(node.right, key);
else if(cmp<0) node.left =delete(node.left, key);
//当找到该节点时
else {
//如果该节点没有左子树时可以将它的右子树作为左子树
if(node.left==null) return node.right;
//如果该节点没有右子树时可以将它的左子树作为右指数
if(node.right==null) return node.left;
//重点如果有左右子树那么此时应该将节点的右子树的最小节点作为一个树的根节点
Node parent = node;
//将要被删除的节点的右节点的最小节点作为这棵子树的根节点
node = min(parent.right);
//由于右子树的最小节点为根节点因此要移除右边节点最小节点的关联。
node.right = deleteMin(parent.right);
//将 要被删除节点的做左节点链接
node.left = parent.left;
}
//重新设置节点的n
node.n = size(node.left)+size(node.right)+1;
//返回该节点作为一个树的链接
return node;
}
}
其实现本身不难,因此讲解主要看注释即可.