平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间,不过相对二叉查找树来说,时间上稳定了很多。
平衡二叉树大部分操作和二叉查找树类似,主要不同在于插入删除的时候平衡二叉树的平衡可能被改变,并且只有从那些插入点到根结点的路径上的结点的平衡性可能被改变,因为只有这些结点的子树可能变化。
平衡二叉树的基础操作:
- 获取元素的个数:写一个size()函数返回size
- 向树中添加新的元素:通过判断大小判断该元素在判断节点的左右,进行递归,知道为null,添加新元素,递归结束
- 找到指定的键:和添加的原理基本相同,当找到key时直接返回即可
- 删除键为key的元素:先查找,再删除,删除时先判断左右子树是否有空子树,有的话直接将不为空的数接在后面,否则将左子树最右侧的元素或右子树最左侧的元素提取出来放在删除元素的位子
- 找到树中最小的key:一路向左
- 找到树中最大的key:一路向右
- 前序遍历:根左右,建立一个辅助队列,先将根加入,然后递归左子树,再递归右子树,最后队列中元素的顺序即为前序遍历的结果
- 中序遍历:左根右,和前序遍历基本相同,先递归左子树,再加根,再递归右子树
- 后续遍历:右根左,先递归右子树,再加根,最后左子树
- 层序遍历:使用两个辅助队列, 一个用做BFS搜索,一个记录每次弹出的元素,记录的队列即为层序遍历的结果
- 判断树的最大深度:dfs深搜一下,基本就可以解决
代码如下(加注释)
节点类:
public class Node <Key,Value>{
//存储键
public Key key;
//储存值
public Value value;
//记录左节点
Node <Key,Value>left;
//记录右节点
Node <Key,Value>right;
public Node() {
super();
}
public Node(Key key, Value value, Node left, Node right) {
super();
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
}
BST:
package 二叉搜索树_b站;
import java.util.*;
public class BinaryTree <Key extends Comparable<Key>,Value>{
private Node<Key,Value> root;
private int N;
//获取树中元素的个数
public int size() {
return N;
}
//向树中添加元素key-value
public void put(Key key,Value value) {
root=put(root,key,value);
}
//向指定的树中添加元素,并返回添加元素后的新树
public Node<Key,Value> put(Node <Key,Value>x,Key key,Value value) {
//如果子树为空
if(x==null) {
N++;
return new Node(key,value,null,null);
}
//如果子树不为空
//比较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节点的键,替换原来节点的value
x.value=value;
}
return x;
}
//找到树中键为Key的value
public Value get(Key key) {
return get(root,key);
}
//在指定的树x中找到key对应的value
public Value get(Node<Key,Value>x,Key key) {
//如果x为空
if(x==null) {
return null;
}
//如果x不为空
//比较x节点的键和key的大小:
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节点的键,返回该节点的Value即可
return x.value;
}
}
//删除树中的key对应的值
public Node<Key,Value> delete(Key key){
return delete(root,key);
}
//删除树中的key对应的value,并返回删除的元素
public Node<Key,Value> delete(Node<Key,Value> x,Key key) {
//x树为空
if(x==null) {
return null;
}
//x树不为空
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 {
//元素个数减一
N--;
//如果key等于x节点的键,完成删除节点的动作,要删除的节点就是x
//找到右子树中最小的节点
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(x.left!=null) {//找到最小节点的父亲节点
if(n.left.left==null) {
n.left=null;
}else {
n=n.left;
}
}
//让x节点的左子树变成minNode的左子树
minNode.left=x.left;
//让x节点的右子树变为minNode的左子树
minNode.right=x.right;
//让x的父亲节点指向minNode
x=minNode;
}
return x;
}
//找到树中最小的Key
public Key min() {
return min(root).key;
}
//找到树x里面的最小key
public Node<Key,Value>min(Node<Key,Value>x){
if(x==null)return null;
Node min=x;
while(x.left!=null) {
x=x.left;
}
return x;
}
//找到树中最大的Key
public Key max() {
return max(root).key;
}
//找到树x里面的最小key
public Node<Key,Value>max(Node<Key,Value>x){
if(x==null)return null;
Node min=x;
while(x.right!=null) {
x=x.right;
}
return x;
}
//获取整个树中的所有键
public Queue<Key>preErgodic(){
Queue<Key>keys=new LinkedList<>();
preErgodic(root,keys);
return keys;
}
//获取指定树x中的所有键
public void preErgodic(Node<Key,Value>x,Queue<Key>keys){
if(x==null) {
return;
}
//把x节点的key放到keys中:根
keys.add(x.key);
//递归遍历x节点的左子树
if(x.left!=null) {
preErgodic(x.left,keys);
}
//递归遍历x节点的右子树
if(x.right!=null) {
preErgodic(x.right,keys);
}
}
//中序遍历获取树中的所有键
public Queue<Key>midErgodic(){
Queue<Key>keys=new LinkedList<>();
midErgodic(root,keys);
return keys;
}
//获取指定树x中的所有键
public void midErgodic(Node<Key,Value>x,Queue<Key>keys){
if(x==null) {
return;
}
//递归遍历x节点的左子树
if(x.left!=null) {
midErgodic(x.left,keys);
}
//把x节点的key放到keys中:根
keys.add(x.key);
//递归遍历x节点的右子树
if(x.right!=null) {
midErgodic(x.right,keys);
}
}
//后序遍历获取树中的所有键
public Queue<Key>afterErgodic(){
Queue<Key>keys=new LinkedList<>();
afterErgodic(root,keys);
return keys;
}
//获取指定树x中的所有键
public void afterErgodic(Node<Key,Value>x,Queue<Key>keys){
if(x==null) {
return;
}
//递归遍历x节点的左子树
if(x.left!=null) {
afterErgodic(x.left,keys);
}
//递归遍历x节点的右子树
if(x.right!=null) {
afterErgodic(x.right,keys);
}
//把x节点的key放到keys中:根
keys.add(x.key);
}
//层序遍历数中所有的键
public Queue<Key> layerErgodic(){
Queue<Node<Key,Value>>q=new LinkedList<>();
Queue<Key>qq=new LinkedList<>();
q.add(root);
while(q.size()!=0) {
Node<Key,Value> n=q.remove();
qq.add(n.key);
if(n.left!=null) {
q.add(n.left);
}
if(n.right!=null) {
q.add(n.right);
}
}
return qq;
}
//返回整个树的最大深度
public int maxDepth() {
return maxDepth(root);
}
//返回指定树的最大深度
public int maxDepth(Node n) {
if(n==null)return 0;
int max=0;
int maxl=0;
int maxr=0;
//查找左子树的最大深度
if(n.left!=null) {
maxl=maxDepth(n.left);
}
//查找右子树的最大深度
if(n.right!=null) {
maxr=maxDepth(n.right);
}
//判断最大深度
max=maxl>maxr?maxl+1:maxr+1;
return max;
}
}
实现类:
package 二叉搜索树_b站;
import java.util.*;
public class Main {
public static void main(String[] args) {
//text();//测试增删改查
//text1();//前序遍历
//text3();//中序遍历
//text4();//后序遍历
//text5();//层序遍历
text6();//最大深度
}
private static void text6() {
BinaryTree<String, String>tree=new BinaryTree<>();
tree.put("E", "5");
tree.put("B", "2");
tree.put("G", "7");
tree.put("A", "1");
tree.put("D", "4");
tree.put("F", "6");
tree.put("H", "8");
tree.put("C", "3");
System.out.println("ppp");
System.out.println(tree.maxDepth());
}
private static void text5() {
BinaryTree<String, String>tree=new BinaryTree<>();
tree.put("E", "5");
tree.put("B", "2");
tree.put("G", "7");
tree.put("A", "1");
tree.put("D", "4");
tree.put("F", "6");
tree.put("H", "8");
tree.put("C", "3");
Queue<String> keys=tree.layerErgodic();
for(String key:keys) {
System.out.println(key+" "+tree.get(key));
}
}
private static void text4() {//后序遍历
BinaryTree<String, String>tree=new BinaryTree<>();
tree.put("E", "5");
tree.put("B", "2");
tree.put("G", "7");
tree.put("A", "1");
tree.put("D", "4");
tree.put("F", "6");
tree.put("H", "8");
tree.put("C", "3");
//遍历
Queue<String> keys=tree.afterErgodic();
for(String key:keys) {
System.out.println(key+" "+tree.get(key));
}
}
private static void text3() {//中序遍历
BinaryTree<String, String>tree=new BinaryTree<>();
tree.put("E", "5");
tree.put("B", "2");
tree.put("G", "7");
tree.put("A", "1");
tree.put("D", "4");
tree.put("F", "6");
tree.put("H", "8");
tree.put("C", "3");
//遍历
Queue<String> keys=tree.midErgodic();
for(String key:keys) {
System.out.println(key+" "+tree.get(key));
}
}
private static void text1() {//前序遍历
BinaryTree<String, String>tree=new BinaryTree<>();
tree.put("E", "5");
tree.put("B", "2");
tree.put("G", "7");
tree.put("A", "1");
tree.put("D", "4");
tree.put("F", "6");
tree.put("H", "8");
tree.put("C", "3");
//遍历
Queue<String> keys=tree.preErgodic();
for(String key:keys) {
System.out.println(key+" "+tree.get(key));
}
}
private static void text() {
BinaryTree<Integer, String>tree=new BinaryTree<Integer, String>();
tree.put(1, "张三");
tree.put(2, "李四");
tree.put(3, "王五");
System.out.println(tree.size());
System.out.println(tree.get(2));
System.out.println("最小的键:"+tree.min());
System.out.println("最大的键:"+tree.max());
tree.delete(3);
System.out.println(tree.size());
System.out.println(tree.get(3));
}
}