平衡二叉樹定義(AVL):
(1)它的左子樹和右子樹的深度之差(平衡因子)的絕對值不超過1。
(2)它的左子樹和右子樹都是一顆平衡二叉樹。
平衡因子:
將二叉樹上節點的左子樹深度減去右子樹深度的值稱爲平衡因子BF。則平衡二叉樹上所有節點的平衡因子只可能是1,-1,0。只要二叉樹上有一個節點的平衡因子的絕對值大於1,那麼該二叉樹就是不平衡的。
最小不平衡子樹:
距離插入節點最近的,且平衡因子的絕對值大於1的節點爲根的子樹,我們稱之爲最小不平衡子樹。
我們之間通過代碼來看實現:
1、二叉樹的定義
class AVLNode {
/**
* 保存節點的數據
*/
private int data;
/**
* 當前節點的深度
* 深度是常用參數,保存深度數據,
* 不用多次遞歸去計算節點的深度
*/
private int depth;
/**
* 平衡指數(左邊深度-右邊深度)
* 左子樹高則爲正,左邊子樹第則爲負數
*/
private int balance;
/**
* 指向父節點(簡化後面關於循環父節點獲取)
*/
public AVLNode parent;
/**
* 左子樹
*/
public AVLNode left;
/**
* 右子樹
*/
public AVLNode right;
public AVLNode(int data) {
this.data = data;
depth = 1;
balance = 0;
left = null;
right = null;
}
}
2、關於樹的深度的計算(因爲這裏的二叉樹定義了高度,直接用左右子樹的最高深度就可以了)
/**
* 計算當前節點深度
*
* @param node
* @return
*/
private int calculateDepth(AVLNode node) {
int depth = 0;
if (node.left != null) {
depth = node.left.depth;
}
if (node.right != null && node.right.depth > depth) {
depth = node.right.depth;
}
depth++;
return depth;
}
3、計算平衡因子()
/**
* 計算平衡因子
*
* @param node
* @return
*/
private int calculateBalance(AVLNode node) {
int leftDepth;
int rightDepth;
//獲取節點左邊深度
if (node.left != null) {
leftDepth = node.left.depth;
} else {
leftDepth = 0;
}
if (node.right != null) {
rightDepth = node.right.depth;
} else {
rightDepth = 0;
}
return leftDepth - rightDepth;
}
4、右旋
/**
* 右旋
*
* @param node
*/
private void rightRotate(AVLNode node) {
AVLNode nParent = node.parent;
AVLNode nLeftSon = node.left;
AVLNode nLeftRightGrandSon = nLeftSon.right;
if (nParent == null) {
//更改更改根節點
this.root = nLeftSon;
}
//1、更改父節點指向
//在操作的時候只改變指針指向,故可以用==
else if (node == nParent.left) {
nParent.left = nLeftSon;
} else if (node == nParent.right) {
nParent.right = nLeftSon;
}
//2、更改左子節點指針指向(父節點和右子節點改變)
nLeftSon.parent = nParent;
nLeftSon.right = node;
//3、更改當前節點
node.parent = nLeftSon;
node.left = nLeftRightGrandSon;
//4、更改右子節點
if (nLeftRightGrandSon != null) {
nLeftRightGrandSon.parent = node;
}
//此時改變平衡度/深度其實就左子節點和當前節點
//需要先計算當前節點的深度和平衡度,
// 因爲原來的左子節點變成當前節點的父節點
node.depth = calculateDepth(node);
node.balance = calculateBalance(node);
nLeftSon.depth = calculateDepth(nLeftSon);
nLeftSon.balance = calculateBalance(nLeftSon);
}
5、左旋
/**
* 左旋
*
* @param node
*/
private void leftRotate(AVLNode node) {
AVLNode nParent = node.parent;
AVLNode nRightSon = node.right;
AVLNode nRightLeftGrandSon = nRightSon.left;
if (nParent == null) {
//更改更改根節點
this.root = nRightSon;
//1、更改父節點指向
} else if (node == nParent.right) {
nParent.right = nRightSon;
} else if (node == nParent.left) {
nParent.left = nRightSon;
}
//2、更改右子節點指向
nRightSon.parent = node;
nRightSon.left = node;
//3、更改當前節點指向
node.parent = nRightSon;
node.right = nRightLeftGrandSon;
//4、更改右左節點指向
if (nRightLeftGrandSon != null) {
nRightLeftGrandSon.parent = node;
}
//此時改變平衡度/深度其實就右子節點和當前節點
//需要先計算當前節點的深度和平衡度,
// 因爲原來的右子節點變成當前節點的父節點
node.depth = calculateDepth(node);
node.balance = calculateBalance(node);
nRightSon.depth = calculateDepth(nRightSon);
nRightSon.balance = calculateBalance(nRightSon);
}
6、插入數據方法;
public boolean insert(int data) {
return insert(root, data);
}
/**
* 插入平衡樹數據
*
* @param node 當前數值比較的節點
* @param data 數值
*/
public boolean insert(AVLNode node, int data) {
if (node == null) {
node = new AVLNode(data);
root = node;
return true;
}
//插入相同數值的二叉樹
if (data == node.data) {
return false;
}
//二叉樹左邊數值小,右邊數值大【根據這個特性對數據進行遞歸插入】
//當插入數據小於當前比較的節點時候,從左邊遞歸插入
if (data < node.data) {
//由於左邊 還有數值,需要和下一個左邊值比較
if (node.left != null) {
insert(node.left, data);
} else {
//左邊數值爲空,可以直接插入
//將比較節點左邊指向 新節點
node.left = new AVLNode(data);
//給新節點parent的父節點參數指向 比較節點
node.left.parent = node;
}
} else {
//當插入數據大於當前比較的節點時候,從右邊遞歸插入
if (node.right != null) {
//由於右邊 還有數值,需要和下一個右邊邊值比較
insert(node.right, data);
} else {
//左邊數值爲空,可以直接插入
//將比較節點左邊指向 新節點
node.right = new AVLNode(data);
node.right.parent = node;
}
}
//更新當前節點上面的balance和depth,並作出旋轉
rebuild(node);
return true;
}
/**
* 更新當前節點上面的balance和depth,並作出旋轉
*
* @param node
*/
private void rebuild(AVLNode node) {
//獲取當前節點的平衡度
node.balance = calculateBalance(node);
//此時左樹更改,且大於最大平衡度,應該右旋
if (node.balance >= MAX_LEFT) {
//此時左子節點的右子樹更高需要,先左旋
if (node.left.balance == RIGHT) {
leftRotate(node.left);
}
rightRotate(node);
}
//此時右樹更改,且大於最大平衡度,應該左旋
if (node.balance <= MAX_RIGHT) {
//此時右子節點的左子樹更高需要,先右旋
if (node.right.balance == LEFT) {
rightRotate(node.right);
}
leftRotate(node);
}
node.balance = calculateBalance(node);
node.depth = calculateDepth(node);
}
查看完整代碼:
public class AVLTree {
private AVLNode root;
private final int LEFT = 1;
private final int RIGHT = -1;
private final int MAX_LEFT = 2;
private final int MAX_RIGHT = -2;
public boolean insert(int data) {
return insert(root, data);
}
/**
* 插入平衡樹數據
*
* @param node 當前數值比較的節點
* @param data 數值
*/
public boolean insert(AVLNode node, int data) {
if (node == null) {
node = new AVLNode(data);
root = node;
return true;
}
//插入相同數值的二叉樹
if (data == node.data) {
return false;
}
//二叉樹左邊數值小,右邊數值大【根據這個特性對數據進行遞歸插入】
//當插入數據小於當前比較的節點時候,從左邊遞歸插入
if (data < node.data) {
//由於左邊 還有數值,需要和下一個左邊值比較
if (node.left != null) {
insert(node.left, data);
} else {
//左邊數值爲空,可以直接插入
//將比較節點左邊指向 新節點
node.left = new AVLNode(data);
//給新節點parent的父節點參數指向 比較節點
node.left.parent = node;
}
} else {
//當插入數據大於當前比較的節點時候,從右邊遞歸插入
if (node.right != null) {
//由於右邊 還有數值,需要和下一個右邊邊值比較
insert(node.right, data);
} else {
//左邊數值爲空,可以直接插入
//將比較節點左邊指向 新節點
node.right = new AVLNode(data);
node.right.parent = node;
}
}
//更新當前節點上面的balance和depth,並作出旋轉
rebuild(node);
return true;
}
/**
* 更新當前節點上面的balance和depth,並作出旋轉
*
* @param node
*/
private void rebuild(AVLNode node) {
//獲取當前節點的平衡度
node.balance = calculateBalance(node);
//此時左樹更改,且大於最大平衡度,應該右旋
if (node.balance >= MAX_LEFT) {
//此時左子節點的右子樹更高需要,先左旋
if (node.left.balance == RIGHT) {
leftRotate(node.left);
}
rightRotate(node);
}
//此時右樹更改,且大於最大平衡度,應該左旋
if (node.balance <= MAX_RIGHT) {
//此時右子節點的左子樹更高需要,先右旋
if (node.right.balance == LEFT) {
rightRotate(node.right);
}
leftRotate(node);
}
node.balance = calculateBalance(node);
node.depth = calculateDepth(node);
}
/**
* 計算當前節點深度
*
* @param node
* @return
*/
private int calculateDepth(AVLNode node) {
int depth = 0;
if (node.left != null) {
depth = node.left.depth;
}
if (node.right != null && node.right.depth > depth) {
depth = node.right.depth;
}
depth++;
return depth;
}
/**
* 右旋
*
* @param node
*/
private void rightRotate(AVLNode node) {
AVLNode nParent = node.parent;
AVLNode nLeftSon = node.left;
AVLNode nLeftRightGrandSon = nLeftSon.right;
if (nParent == null) {
//更改更改根節點
this.root = nLeftSon;
}
//1、更改父節點指向
//在操作的時候只改變指針指向,故可以用==
else if (node == nParent.left) {
nParent.left = nLeftSon;
} else if (node == nParent.right) {
nParent.right = nLeftSon;
}
//2、更改左子節點指針指向(父節點和右子節點改變)
nLeftSon.parent = nParent;
nLeftSon.right = node;
//3、更改當前節點
node.parent = nLeftSon;
node.left = nLeftRightGrandSon;
//4、更改右子節點
if (nLeftRightGrandSon != null) {
nLeftRightGrandSon.parent = node;
}
//此時改變平衡度/深度其實就左子節點和當前節點
//需要先計算當前節點的深度和平衡度,
// 因爲原來的左子節點變成當前節點的父節點
node.depth = calculateDepth(node);
node.balance = calculateBalance(node);
nLeftSon.depth = calculateDepth(nLeftSon);
nLeftSon.balance = calculateBalance(nLeftSon);
}
/**
* 左旋
*
* @param node
*/
private void leftRotate(AVLNode node) {
AVLNode nParent = node.parent;
AVLNode nRightSon = node.right;
AVLNode nRightLeftGrandSon = nRightSon.left;
if (nParent == null) {
//更改更改根節點
this.root = nRightSon;
//1、更改父節點指向
} else if (node == nParent.right) {
nParent.right = nRightSon;
} else if (node == nParent.left) {
nParent.left = nRightSon;
}
//2、更改右子節點指向
nRightSon.parent = node;
nRightSon.left = node;
//3、更改當前節點指向
node.parent = nRightSon;
node.right = nRightLeftGrandSon;
//4、更改右左節點指向
if (nRightLeftGrandSon != null) {
nRightLeftGrandSon.parent = node;
}
//此時改變平衡度/深度其實就右子節點和當前節點
//需要先計算當前節點的深度和平衡度,
// 因爲原來的右子節點變成當前節點的父節點
node.depth = calculateDepth(node);
node.balance = calculateBalance(node);
nRightSon.depth = calculateDepth(nRightSon);
nRightSon.balance = calculateBalance(nRightSon);
}
/**
* 計算平衡因子
*
* @param node
* @return
*/
private int calculateBalance(AVLNode node) {
int leftDepth;
int rightDepth;
//獲取節點左邊深度
if (node.left != null) {
leftDepth = node.left.depth;
} else {
leftDepth = 0;
}
if (node.right != null) {
rightDepth = node.right.depth;
} else {
rightDepth = 0;
}
return leftDepth - rightDepth;
}
class AVLNode {
/**
* 保存節點的數據
*/
private int data;
/**
* 當前節點的深度
* 深度是常用參數,保存深度數據,
* 不用多次遞歸去計算節點的深度
*/
private int depth;
/**
* 平衡指數(左邊深度-右邊深度)
* 左子樹高則爲正,左邊子樹第則爲負數
*/
private int balance;
/**
* 指向父節點(簡化後面關於循環父節點獲取)
*/
public AVLNode parent;
/**
* 左子樹
*/
public AVLNode left;
/**
* 右子樹
*/
public AVLNode right;
public AVLNode(int data) {
this.data = data;
depth = 1;
balance = 0;
left = null;
right = null;
}
}
public static void main(String[] args) {
AVLTree node = new AVLTree();
// com.yin.dynamic_elasticjob.util.AVLNode node = new com.yin.dynamic_elasticjob.util.AVLNode(3);
node.insert(3);
node.insert(2);
node.insert(1);
node.insert(4);
node.insert(5);
node.insert(6);
node.insert(7);
node.insert(10);
node.insert(9);
System.out.println("-----驗證是否平衡-----");
}
}