定义
二叉查找树,又称二叉排序树,亦称二叉搜索树,一般情况下,查询效率比链表结构要高。
特点:
- 没有键值相等的结点;
- 若左子树不空,则左子树上所有结点的键值均小于它的根结点的键值;
- 若右子树不空,则右子树上所有结点的键值均大于它的根节点的键值;
- 左、右子树也分别为二叉查找树。
代码实现
import java.util.Deque;
import java.util.LinkedList;
public class BinaryTree<Key extends Comparable<Key>, Value> {
//记录根结点
private Node root;
//记录树中元素的个数
private int N;
//定义结点
private class Node {
//存储键
private Key key;
//存储值
private Value value;
//记录左子结点
private Node left;
//记录右子结点
private Node right;
public Node(Key key, Value value, Node left, Node right) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
}
//获取树中元素的个数
public int size() {
return N;
}
//向树中添加元素key-value
public void put(Key key, Value value) {
root = put(root, key, value);
}
//向指定的树x中添加key-value
private Node put(Node x, Key key, Value value) {
//如果x子树为空
if (x==null){
N++;
return new Node(key, value,null,null);
}
//如果x子树不为空
//比较x结点的键和key的大小
int cmp = key.compareTo(x.key);
if (cmp>0){
//如果key大于x结点的键,则继续找x结点的右子树
x.right = put(x.right,key,value);
}else if(cmp<0){
//如果key小于x结点的键,则继续找x结点的左子树
x.left = put(x.left,key,value);
}else{
//如果key等于x结点的键,则替换x结点的值为value即可
x.value = value;
}
return x;
}
//查询树中指定key对应的value
public Value get(Key key) {
return get(root, key);
}
//从指定的树x中,查找key对应的值
private Value get(Node x, Key key) {
//x树为null
if (x==null){
return null;
}
//x树不为null
//比较key和x结点的键的大小
int cmp = key.compareTo(x.key);
if (cmp>0){
//如果key大于x结点的键,则继续找x结点的右子树
return get(x.right, key);
}else if(cmp<0){
//如果key小于x结点的键,则继续找x结点的左子树
return get(x.left, key);
}else{
//如果key等于x结点的键,就找到了键为key的结点,只需要返回x结点的值即可
return x.value;
}
}
//删除树中key对应的结点
public void delete(Key key) {
root = delete(root, key);
}
//删除指定树x中的key对应的结点,并返回删除后的新树
private Node delete(Node x, Key key) {
//x树为null
if (x==null){
return null;
}
//x树不为null
int cmp = key.compareTo(x.key);
if (cmp>0){
//如果key大于x结点的键,则继续找x结点的右子树
x.right = delete(x.right,key);
}else if(cmp<0){
//如果key小于x结点的键,则继续找x结点的左子树
x.left = delete(x.left,key);
}else{
//如果key等于x结点的键,完成真正的删除结点动作,要删除的结点就是x;
//让元素个数-1
N--;
if (x.right==null){
return x.left;
}
if (x.left==null){
return x.right;
}
//找到右子树中最小的节点
Node minNode = x.right;
while(minNode.left!=null){
minNode = minNode.left;
}
//删除右子树中最小的结点
Node n = x.right;
while(n.left!=null){
if (n.left.left==null){
n.left=null;
}else{
//变换n结点即可
n = n.left;
}
}
//让x结点的左子树成为minNode的左子树
minNode.left = x.left;
//让x结点的右子树成为minNode的右子树
minNode.right = x.right;
//把minNode赋值给x
x = minNode;
}
return x;
}
//查找整个树中最小的键
public Key min(){
Node x = root;
//需要判断x还有没有左子结点,如果有,则继续向左找,如果没有,则x就是最小键所在的结点
while(x.left!=null) {
x = x.left;
}
return x.key;
}
//查找整个树中最大的键
public Key max(){
Node x = root;
//需要判断x还有没有右子结点,如果有,则继续向右找,如果没有,则x就是最大键所在的结点
while(x.right!=null) {
x = x.right;
}
return x.key;
}
//通过前序遍历获取整个树中所有的键
public Deque<Key> preErgodic(){
Deque<Key> keys = new LinkedList<>();
preErgodic(root, keys);
return keys;
}
//获取指定树x的所有键,并放到keys队列中
private void preErgodic(Node x,Deque<Key> keys){
if (x != null){
//把x结点的key放入到keys中
keys.offer(x.key);
//递归遍历x结点的左子树
preErgodic(x.left,keys);
//递归遍历x结点的右子树
preErgodic(x.right,keys);
}
}
//使用中序遍历获取树中所有的键
public Deque<Key> midErgodic(){
Deque<Key> keys = new LinkedList<>();
midErgodic(root,keys);
return keys;
}
//使用中序遍历,获取指定树x中所有的键,并存放到keys中
private void midErgodic(Node x,Deque<Key> keys){
if (x != null){
//先递归,把左子树中的键放到keys中
midErgodic(x.left,keys);
//把当前结点x的键放到keys中
keys.offer(x.key);
//再递归,把右子树中的键放到keys中
midErgodic(x.right,keys);
}
}
//使用后序遍历,把整个树中所有的键返回
public Deque<Key> afterErgodic(){
Deque<Key> keys = new LinkedList<>();
afterErgodic(root,keys);
return keys;
}
//使用后序遍历,把指定树x中所有的键放入到keys中
private void afterErgodic(Node x,Deque<Key> keys){
if (x!=null){
//通过递归把左子树中所有的键放入到keys中
afterErgodic(x.left,keys);
//通过递归把右子树中所有的键放入到keys中
afterErgodic(x.right,keys);
//把x结点的键放入到keys中
keys.offer(x.key);
}
}
//使用层序遍历,获取整个树中所有的键
public Deque<Key> layerErgodic(){
//定义两个队列,分别存储树中的键和树中的结点
Deque<Key> keys = new LinkedList<>();
Deque<Node> nodes = new LinkedList<>();
if (root==null) return keys;
//往队列中放入根结点
nodes.offer(root);
while(!nodes.isEmpty()){
//从队列中弹出一个结点,把key放入到keys中
Node n = nodes.poll();
keys.offer(n.key);
//判断当前结点还有没有左子结点,如果有,则放入到nodes中
if (n.left!=null){
nodes.offer(n.left);
}
//判断当前结点还有没有右子结点,如果有,则放入到nodes中
if (n.right!=null){
nodes.offer(n.right);
}
}
return keys;
}
//获取整个树的最大深度
public int maxDepth(){
return maxDepth(root);
}
//获取指定树x的最大深度
private int maxDepth(Node x){
if (x==null){
return 0;
}
//x的最大深度
int max=0;
//左子树的最大深度
int maxL=0;
//右子树的最大深度
int maxR=0;
//计算x结点左子树的最大深度
if (x.left!=null){
maxL = maxDepth(x.left);
}
//计算x结点右子树的最大深度
if (x.right!=null){
maxR = maxDepth(x.right);
}
//当前树的最大深度=左子树的最大深度和右子树的最大深度中的较大者+1
max = maxL > maxR ? maxL + 1 : maxR + 1;
return max;
}
}
测试
public class Test {
public static void main(String[] args) throws Exception {
BinaryTree<String, String> bt = new BinaryTree<>();
bt.put("E", "5");
bt.put("B", "2");
bt.put("G", "7");
bt.put("A", "1");
bt.put("D", "4");
bt.put("F", "6");
bt.put("H", "8");
bt.delete("H");
bt.put("C", "3");
Deque<String> queue = bt.layerErgodic();
for (String key : queue) {
System.out.println(key + "=" + bt.get(key));
}
System.out.println(bt.maxDepth());
}
}
E=5
B=2
G=7
A=1
D=4
F=6
C=3
4