二叉排序树(BST, Binary Sort Tree)也称二叉查找树(Binary Search Tree), 或二叉搜索树。
定义:一颗二叉树,满足以下属性:
- 左子树的所有的值小于根节点的值
- 右子树的所有值大于根节点的值
- 左、右子树满足以上两点
那么我们如何去构建一个自己的二叉排序树呢?算法实现思路如下:
二叉排序树只是对值进行了排序,但是如果我们对二叉排序树进行树的平衡操作,即对树进行旋转,那么就引出了本文的重要知识点,平衡二叉树(AVL Tree)。《大话数据结构》是这样解释的:
下面通过几张图片来对AVL树有一个基本的认识:
为什么图2,图3不是平衡二叉树呢?
平衡二叉树首先是一颗二叉排序树,因为图2节点值58,它的左节点值为59,不满足二叉排序树的条件,因此更不可能是平衡二叉树了。
图3中,节点58的右子树为空,而左子树的高度为2,不满足左右节点高度差至多为1的条件,因此也不是平衡二叉树
下面通过过右旋,左右旋来做一个简单的介绍。(左旋和右旋相反,左右旋和右左旋相反)
右旋:即对树的根节点重新判定,即左子树最下层的左节点新增节点值,那么可以通过将新生成的根节点往右拉,即可实现平衡。即将新的根节点40拉到旧的根节点50的位置。
左右旋:如下图中所展示,先对左子树进行左旋,将原来左子树根节点20变为30,即将30往左拉; 然后再进行右旋,即将整颗树的新的根节点30往右拉
下面通过一张图片,总结一下分别在什么情况下需要左旋,左右旋,右旋,右左旋。
左旋,右旋只需要一次旋转即可完成树的平衡
左右旋,右左旋需要2次旋转才能完成树的平衡
下面直接接上代码,实现AVL Tree的构造
package unit2.class35;
/**
* 按照key进行存储的AVL树,
* key是排序的依据,key不可变
*/
public class AVLTree<K extends Comparable, V>
{
AVLNode<K, V> root; //根节点
int size = 0; //节点数量
public AVLTree(){
root = null;
int size = 0;
}
//Node节点数据结构
public static class AVLNode<K extends Comparable,V>
{
public AVLNode<K, V> left;
public AVLNode<K, V> right;
public K k;
public V v;
int height;
AVLNode(K key, V value){
k = key;
v = value;
height = 1;
}
}
private void setHeight(AVLNode<K, V> cur){
cur.height = Math.max(cur.left != null ? cur.left.height : 0,
cur.right != null ? cur.right.height : 0) + 1;
}
//右旋
private AVLNode<K, V> rightRotate (AVLNode<K, V> cur)
{
AVLNode<K, V> left = cur.left;
cur.left = left.right;
left.right = cur;
//先子后父,顺序不能颠倒
setHeight(cur);
setHeight(left);
return left;
}
//左旋
private AVLNode<K, V> leftRotate (AVLNode<K, V> cur)
{
AVLNode<K, V> right = cur.right;
cur.right = right.left;
right.left = cur;
//先子后父,顺序不能颠倒
setHeight(cur);
setHeight(right);
return right;
}
//旋转
private AVLNode<K, V> rotate (AVLNode<K, V> cur)
{
if (cur == null) {
return null;
}
int leftHeight = cur.left != null ? cur.left.height : 0; //左子树高度
int rightHeight = cur.right != null ? cur.right.height : 0; //右子树高度
//左、右子树高度差超过1,说明失衡,需要旋转
if (Math.abs(leftHeight - rightHeight) > 1)
{
if (leftHeight > rightHeight) {
int leftLeftHeight = cur.left != null && cur.left.left != null ? cur.left.left.height : 0;
int leftRightHeight = cur.left != null && cur.left.right != null ? cur.left.right.height : 0;
if (leftLeftHeight > leftRightHeight) { //右旋
cur = rightRotate(cur);
}
else { //左右旋
cur.left = leftRotate(cur.left);
cur = rightRotate(cur);
}
}
else {
int rightLeftHeight = cur.right != null && cur.right.left != null ? cur.right.left.height : 0;
int rightRightHeight = cur.right != null && cur.right.right != null ? cur.right.right.height : 0;
if (rightRightHeight > rightLeftHeight) { //左旋
cur = leftRotate(cur);
}
else { //右左旋
cur.right = rightRotate(cur.right);
cur = leftRotate(cur);
}
}
}
//每个节点已经在旋转的时候重置了height
//所以此处无需再次设置height
return cur;
}
private AVLNode<K, V> findExistOrPreNode (K key)
{
AVLNode<K, V> cur = root;
AVLNode<K, V> pre = root;
while (cur != null) {
pre = cur;
if (key.compareTo(cur.k) == 0) { //找到当前节点
break;
}
else if (key.compareTo(cur.k) > 0) {
cur = cur.right;
}
else {
cur = cur.left;
}
}
// pre的key,要么等于当前key, 要么是null或者最近接key的一个节点
return pre;
}
private AVLNode<K, V> add(AVLNode<K, V> cur, K key, V value)
{
if (cur == null) {
return new AVLNode(key, value);
}
if (key.compareTo(cur.k) > 0) {
cur.right = add(cur.right, key, value);
}
else { //不存在相等的情况,findExistOrPreNode之前已经检查过了
cur.left = add(cur.left, key, value);
}
//新增了节点,当前高度需要调整。
//旋转的时候,需要节点的高度判断
setHeight(cur);
//旋转
return rotate(cur);
}
//无需对cur做非空判断,因为remove已经检查key是否存在
private AVLNode<K, V> delete(AVLNode<K, V> cur, K key)
{
if (key.compareTo(cur.k) > 0) {
cur.right = delete(cur.right, key);
}
else if (key.compareTo(cur.k) < 0) { //不存在相等的情况,findExistOrPreNode之前已经检查过了
cur.left = delete(cur.left, key);
}
else {
if (cur.left == null && cur.right == null) {
cur = null;
}
else if (cur.left != null && cur.right == null){
cur = cur.left;
}
else if (cur.left == null && cur.right != null){
cur = cur.right;
}
else {
AVLNode<K, V> des = cur.right;
while (des.left != null) {
des = des.left;
}
//必须走递归,才能确保删除后,由下往上每一层节点旋转正确
//如果是SB树,删除不需要旋转保持平衡,那么此处直接删除即可,无需递归
cur.right = delete(cur.right, des.k);
des.left = cur.left;
des.right = cur.right;
cur = des;
}
}
if (cur != null) {
setHeight(cur);
}
return rotate(cur);
}
//查询是否存在
public boolean containsKey (K key)
{
AVLNode<K, V> findNode = findExistOrPreNode(key);
return findNode != null && key.compareTo(findNode.k) == 0;
}
//查询树的节点数
public int size(){
return size;
}
//查询树的高度
public int height() {
return root != null ? root.height : 0;
}
public V get(K key)
{
if (key == null) {
//return;
throw new IllegalArgumentException("key can not be null");
}
AVLNode<K, V> findNode = findExistOrPreNode(key);
return findNode != null ? findNode.v : null;
}
public K floorKey(K key) {
if (key == null) {
return null;
}
AVLNode<K, V> lastNoBigNode = findExistOrPreNode(key);
return lastNoBigNode == null ? null : lastNoBigNode.k;
}
public K ceilingKey(K key) {
if (key == null) {
return null;
}
AVLNode<K, V> lastNoSmallNode = findLastNoSmallIndex(key);
return lastNoSmallNode == null ? null : lastNoSmallNode.k;
}
public K firstKey() {
if (root == null) {
return null;
}
AVLNode<K, V> cur = root;
while (cur.left != null) {
cur = cur.left;
}
return cur.k;
}
public K lastKey() {
if (root == null) {
return null;
}
AVLNode<K, V> cur = root;
while (cur.right != null) {
cur = cur.right;
}
return cur.k;
}
private AVLNode<K, V> findLastNoBigIndex(K key) {
AVLNode<K, V> ans = null;
AVLNode<K, V> cur = root;
while (cur != null) {
if (key.compareTo(cur.k) == 0) {
ans = cur;
break;
} else if (key.compareTo(cur.k) < 0) {
cur = cur.left;
} else {
ans = cur;
cur = cur.right;
}
}
return ans;
}
private AVLNode<K, V> findLastNoSmallIndex(K key) {
AVLNode<K, V> ans = null;
AVLNode<K, V> cur = root;
while (cur != null) {
if (key.compareTo(cur.k) == 0) {
ans = cur;
break;
} else if (key.compareTo(cur.k) < 0) {
ans = cur;
cur = cur.left;
} else {
cur = cur.right;
}
}
return ans;
}
//新增、修改
public void put (K key, V value)
{
if (key == null) {
//return;
throw new IllegalArgumentException("key can not be null");
}
//寻找当前key
AVLNode<K, V> findNode = findExistOrPreNode(key);
/**
* 如果当前key存在,说明是修改可以的value
* 如果当前key不存在,那么是null或者最近接key的一个节点
*
* key是排序的依据,key值不可变。
* 如果是简单类型的数据,比如int类型的值构造的AVL树,那么不应当提供修改功能
*/
if (findNode != null && key.compareTo(findNode.k) == 0) {
findNode.v = value;
}
else {
size++;
root = add(root, key, value);
}
}
//删除
public void remove(K key)
{
if (key == null) {
throw new IllegalArgumentException("key can not be null");
}
if (containsKey(key)) {
size--;
root = delete(root, key);
}
}
}