我知道看我博客的大家都已经了解了树的知识,为了简介,一些细枝末节的基础直接跳过,有问题直接私聊我
首先我们先需要记住树的基本概念:
一个数是一些节点的集合。这个节点可以是空集,若不是空集,则树由称作根(root)的节点以及0个或多个非空(子)树组成,其下级极为儿子,按辈分排下去了,连接的线即是边(edge),没有子级别的孩子,即为树叶(leaf)
从根到目标节点的边的数量,机位深度,从目标节点到当前最远树叶的边的数量为高度。
树的节点有点类似于链表的节点,用java代码实现一个最简单的二叉树节点,方便理解:
这里我给二叉树的实现跳过了,需要的私聊我,直接上AVL平衡树,因为旋转的问题让我一直很头疼,所以分享给大家
class BinaryNode{
Object element;//当前节点数据
BinaryNode left;//其左儿子
BinaryNode right;//其右侧儿子}
关于二叉查找树大家结合链表的节点一起看,二叉树是只记录当前节点的左子和右子,那么当他查询的时候的时间理想状态为O(logN),但实际情况并不会特别理想,这里贴个图是理想状态下二叉树和不理想状态:
因为插入的一直是比当前节点小的数据,二叉树变成了单枝树,这时候就出现了平衡的需求
平衡二叉树,Avl树
所谓平衡就是需要保证树的深度需是O(logN) ,既最优深度。
一颗AVL树是其每个节点的左子树和又子树的高度最多差1的二叉树找树(空树的高度定为-1)。
大家需要记住最多差一即可,是想尽量满足2^k-1的理想平衡树(perfectly balanced tree).
既然要平衡,那么就涉及到一个概念—旋转(rotation)。
旋转分两种:单旋转和双旋转
单旋转情况:对节点的左儿子的左子树进行一次插入 —> 向右转
对节点的右儿子的右子树进行一次插入 —> 向左转
双旋转情况:对节点的右儿子的左子树进行一次插入 —> 先右转 —> 再左转
对节点的左儿子的右子树进行一次插入 —> 先左转 —> 再右转
注:下面图片为转发图片,侵权删
下面给大家贴一个AVL旋转树的基本实现代码,默认树中存储的都是数字:
代码下载地址:
https://github.com/shenyang312/shenyang/blob/dev/shen/src/main/java/com/shen/my_j_u/MyAVLTree.java
package com.shen.my_j_u;
import org.omg.CORBA.Any;
import java.nio.BufferUnderflowException;
public class MyAVLTree<AnyType extends Comparable<? super AnyType>> {
/**
* 平衡节点内部类
* 注:与普通二叉树的区别在于,他需要记录每个节点的高度
* @param <AnyType>
*/
private static class AvlNode<AnyType>{
AvlNode( AnyType theElement){
this(theElement,null,null);
}
AvlNode( AnyType theElement,AvlNode<AnyType> lt, AvlNode<AnyType> rt){
element = theElement;
left = lt;
right = rt;
height = 0;
}
AnyType element;
AvlNode<AnyType> left;
AvlNode<AnyType> right;
int height;
}
private AvlNode<AnyType> root;
private static final int ALLOWED_IMBALANCE = 1;
/**
* 获取节点高度私有方法
* @param t
* @return
*/
private int height(AvlNode<AnyType> t){
return t ==null ?-1:t.height;
}
//既然大家都是了解二叉树的查找等结构的,那么我着重讲解一下平衡二叉树的---- 插入与删除的旋转
private AvlNode<AnyType> insert(AnyType x,AvlNode<AnyType> t){
if(t == null)
return new AvlNode<>(x,null,null);
int compareResult = x.compareTo(t.element);
if(compareResult<0)
t.left = insert(x,t.left);
else if(compareResult > 0)
t.right = insert(x,t.right);
else
;
return balance(t);
}
private AvlNode<AnyType> balance(AvlNode<AnyType> t){
if(t==null)
return t;
//如果左侧比右侧高一
if(height(t.left) - height(t.right)>ALLOWED_IMBALANCE)
//如果时左左情况,单侧旋转
if(height(t.left.left)>=height(t.left.right))
//单侧-右旋转
t = rotateWithLeftChild(t);
else
t = doubleWithLeftChild(t);
else
//如果右侧比左侧高一
if(height(t.right)-height(t.left)>ALLOWED_IMBALANCE)
//如果是右右情况,单侧旋转
if(height(t.right.right)>=height(t.right.left))
//单侧-左旋转
t = rotateWithRightChild(t);
else
t = doubleWithRightChild(t);
t.height = Math.max(height(t.left),height(t.right))+1;
return t;
}
/**
* 单侧-右旋转
* @param k2
* @return
*/
private AvlNode<AnyType> rotateWithLeftChild(AvlNode<AnyType> k2){
//先取出左侧的参数,这是想办法,把k1转到右侧
AvlNode<AnyType> k1 = k2.left;
//把当前节点的左侧子节点的右节点,赋值给当前节点的左节点,实现旋转节点的左侧右侧节点,右向左做旋转
k2.left = k1.right;
//然后把把旋转节点右移动,把之前的根节点k2变成其右侧节点,实现k2左侧移动
k1.right = k2;
//重新赋值k2高度
k2.height = Math.max(height(k2.left),height(k2.right))+1;
//重新赋值k1高度
k1.height = Math.max(height(k1.left),k2.height) + 1;
//旋转后,返回新的节点
return k1;
}
private AvlNode<AnyType> rotateWithRightChild(AvlNode<AnyType> k2){
AvlNode<AnyType> k1 = k2.right;
k2.right = k1.left;
k1.left = k2;
k2.height = Math.max(height(k2.right),height(k2.left))+1;
k1.height = Math.max(height(k1.right),k2.height) + 1;
return k1;
}
private AvlNode<AnyType> doubleWithLeftChild(AvlNode<AnyType> k3){
k3.left = rotateWithRightChild(k3.left);
return rotateWithLeftChild(k3);
}
private AvlNode<AnyType> doubleWithRightChild(AvlNode<AnyType> k3){
k3.right = rotateWithRightChild(k3.right);
return rotateWithRightChild(k3);
}
/**
* 传入参数是删除数据和 节点t
* 默认节点t为root跟节点
* @param x
* @param t
* @return
*/
private AvlNode<AnyType> remove(AnyType x,AvlNode<AnyType> t){
if(t==null)
return t;
//compareTo方法,相当返回0,小于返回-1,大于返回1
//先查找当前删除参数是否属于当前节点
int compareResult = x.compareTo(t.element);
//未查询到即继续递归查询
//即未折半查找法
if(compareResult<0)
t.left = remove(x,t.left);
if(compareResult<0)
t.left = remove(x,t.left);
else if(compareResult>0)
t.right = remove(x,t.right);
else if(t.left != null && t.right != null){
t.element = findMin(t.right).element;
t.right = remove(t.element,t.right);
}
else
t = (t.left != null) ? t.left :t.right;
return balance(t);
}
public AvlNode<AnyType> findMin(){
if(isEmty())
throw new BufferUnderflowException();
return findMin(root);
}
private AvlNode<AnyType> findMin(AvlNode<AnyType> t){
if(t ==null)
return null;
else if(t.left == null)
return t;
return findMin(t.left);
}
public boolean isEmty(){
return root == null;
}
}