二叉平衡树
定义
- 是一个特殊的 二叉查找树
- 任何结点的两个子树的高度差小于等于1
- 前5个函数为后面的功能做铺垫,一般的树都有这些函数
1. 结点
public class Node{
int height;
int value;
Node left;
Node right;
public Node(int value, Node left, Node right) {
this.value = value;
this.left = left;
this.right = right;
this.height = 0;
}
}
2. 树高
//树高
public int height(){
return height(root);
}
private int height(Node node){
if(node != null){
return node.height;
}
return 0;
}
3. 比大小
//比大小
private int max(int a,int b){
return a > b ? a : b;
}
4. 找最值及其结点
public int min(){
Node node = min(root);
return node == null ? -1 : node.value ;
}
private Node min(Node node){
if(node == null) return null;
while(node.left != null){
node = node.left;
}
return node;
}
public int max(){
Node node = max(root);
return node == null ? -1 : node.value;
}
private Node max(Node node){
if(node == null) return null;
while(node.right != null){
node = node.right;
}
return node;
}
5. 查找
public Node search(int value){
return search(root,value);
}
private Node search(Node node,int value){
if(node == null){
return node;
}
if(value < node.value){
return search(node.left,value);
}else if(value > node.value){
return search(node.right,value);
}else{
return node;
}
}
6. 旋转
为了实现任何结点的左右子树高度差小于等于1,就要用旋转
使树达到平衡,而旋转分为,左左旋转,右右旋转,左右旋转和右左旋转
- 左左旋转
// 1 2
// 2 ---> 3 1
// 3
//左左旋转,斜树
private Node leftLeftRotation(Node node){
// 正常左旋
Node temp = node.left;
node.left = temp.right;
temp.right = node;
// 更新树高,node的原节点1,temp为2
node.height = max( height(node.left), height(node.right)) + 1;
temp.height = max( height(temp.left), node.height) + 1;
// 返回左旋后的节点,让上层节点更新
return temp;
}
- 右右旋转
//右右旋转,斜树
public Node rightRightRotation(Node node){
Node temp = node.right;
node.right = temp.left;
temp.left = node;
node.height = max( height(node.left), height(node.right)) + 1;
temp.height = max( height(temp.right), node.height) + 1;
return temp;
}
- 左右旋转
// 3 3 2
// 1 ---> 2 ---> 1 3
// 2 1
//左右旋转,斜树的最后一个往内侧放了
private Node leftRightRotation(Node node){
// 先进行右右旋转,然后该返回的跟新节点进行左左旋转
// node为3,node.left为1,这里要更新node.left
node.left = rightRightRotation(node.left);
// 做作旋转传入node,别弄混了
return leftLeftRotation(node);
}
- 右左旋转
//右左旋转
private Node rightLeftRotation(Node node){
node.right = leftLeftRotation(node.right);
return rightRightRotation(node);
}
7. 插入
//插入
public void put(int value){
root = put(root,value);
}
private Node put(Node node,int value){
if(node == null){ // 创建节点
node = new Node(value,null,null);
return node;
}
// 左移
if(value < node.value){
node.left = put(node.left,value);
// 树高差2,则平衡调整
if( (height(node.left) - height(node.right)) == 2){
if (value < node.left.value){ // 左斜树
node = leftLeftRotation(node);
}else{
node = leftRightRotation(node);
}
}
// 右移
}else if(value > node.value){
node.right = put(node.right,value);
if ( (height(node.right) - height(node.left)) == 2){
if(value > node.right.value){
node = rightRightRotation(node);
}else{
node = rightLeftRotation(node);
}
}
// 相等
}else{
System.out.println("不能插入相同的值");
}
// 插入节点后,树高+1
node.height = max( height(node.left),height(node.right) ) + 1;
return node;
}
8. 删除
// 删除
public void remove(int value){
// 找到删除节点的位置
Node node = search(root,value);
if(node != null){
root = remove(root,node);
}
}
private Node remove(Node tree, Node node){
if( tree == null || node == null){
return null;
}
// 左移,删除后更新上层节点的左节点
if(node.value < tree.value){
tree.left = remove(tree.left,node);
// 删除后不平衡,删左右高
if(height(tree.right ) - height(tree.left) == 2){
Node temp = tree.right; // 看删除节点的兄弟,左右谁高,即判断是不是斜树
if(height(temp.left) > height(temp.right)){
tree = rightLeftRotation(tree);
}else{
tree = rightRightRotation(tree);
}
}
// 右移
}else if(node.value > tree.value){
tree.right = remove(tree.right,node);
if(height(tree.left) - height(tree.right) == 2){
Node temp = tree.left;
if(height(temp.right) > height(temp.left)){
tree = leftRightRotation(tree);
}else{
tree = leftLeftRotation(tree);
}
}
//是该结点了
}else{
// 删除分三情况
// 1. 左右孩子不为空
if(tree.left != null && tree.right != null){
if(height(tree.left) > height(tree.right)){
Node max = max(tree.left);
tree.value = max.value;
tree.left = remove(tree.left, max);
}else{
Node min = min(tree.right);
tree.value = min.value;
tree.right = remove(tree.right, min);
}
// 2.3. 左右孩子不全存在,那么直接孩子代替父亲
}else{
Node temp = tree;
tree = (tree.left != null) ? tree.left : tree.right;
temp = null;
}
}
return tree;
}
9. 测试
// 测试
public static void main(String[] args) {
AVLTree tree = new AVLTree();
int[] arrs = {3,2,1,4,5,6,7,16,15,14,13,12,11,10,8,9};
for (int value : arrs){
tree.put(value);
}
tree.preOrder();
System.out.println("\n-------------");
System.out.println("最大值:" + tree.max());
System.out.println("最小值:" + tree.min());
System.out.println("树高:" + tree.height());
}
7 5 3 2 1 4 6 14 12 10 8 9 11 13 15 16
-------------
最大值:16
最小值:1
树高:5
10. 整体代码
// 查询、插入、删除都是O(logN)
public class AVLTree {
private Node root;
public class Node{
int height;
int value;
Node left;
Node right;
public Node(int value, Node left, Node right) {
this.value = value;
this.left = left;
this.right = right;
this.height = 0;
}
}
// 树高
public int height(){
return height(root);
}
private int height(Node node){
if(node != null){
return node.height;
}
return 0;
}
// 比大小
private int max(int a,int b){
return a > b ? a : b;
}
// 遍历
public void preOrder(){
preOrder(root);
}
public void preOrder(Node node){
if(node != null){
System.out.print(node.value);
System.out.print(" ");
preOrder(node.left);
preOrder(node.right);
}
}
// 查找,建议非递归实现
public Node search(int value){
return search(root,value);
}
private Node search(Node node,int value){
if(node == null){
return node;
}
if(value < node.value){
return search(node.left,value);
}else if(value > node.value){
return search(node.right,value);
}else{
return node;
}
}
//最值
public int min(){
Node node = min(root);
return node == null ? -1 : node.value ;
}
private Node min(Node node){
if(node == null) return null;
while(node.left != null){
node = node.left;
}
return node;
}
public int max(){
Node node = max(root);
return node == null ? -1 : node.value;
}
private Node max(Node node){
if(node == null) return null;
while(node.right != null){
node = node.right;
}
return node;
}
// 1 2
// 2 ---> 3 1
// 3
//左左旋转,斜树
private Node leftLeftRotation(Node node){
// 正常左旋
Node temp = node.left;
node.left = temp.right;
temp.right = node;
// 更新树高,node的原节点1,temp为2
node.height = max( height(node.left), height(node.right)) + 1;
temp.height = max( height(temp.left), node.height) + 1;
// 返回左旋后的节点,让上层节点更新
return temp;
}
//右右旋转,斜树
public Node rightRightRotation(Node node){
Node temp = node.right;
node.right = temp.left;
temp.left = node;
node.height = max( height(node.left), height(node.right)) + 1;
temp.height = max( height(temp.right), node.height) + 1;
return temp;
}
// 3 3 2
// 1 ---> 2 ---> 1 3
// 2 1
//左右旋转,斜树的最后一个往内侧放了
private Node leftRightRotation(Node node){
// 先进行右右旋转,然后该返回的跟新节点进行左左旋转
// node为3,node.left为1,这里要更新node.left
node.left = rightRightRotation(node.left);
// 做作旋转传入node,别弄混了
return leftLeftRotation(node);
}
//右左旋转
private Node rightLeftRotation(Node node){
node.right = leftLeftRotation(node.right);
return rightRightRotation(node);
}
//插入
public void put(int value){
root = put(root,value);
}
private Node put(Node node,int value){
if(node == null){ // 创建节点
node = new Node(value,null,null);
return node;
}
// 左移
if(value < node.value){
node.left = put(node.left,value);
// 树高差2,则平衡调整
if( (height(node.left) - height(node.right)) == 2){
if (value < node.left.value){ // 左斜树
node = leftLeftRotation(node);
}else{
node = leftRightRotation(node);
}
}
// 右移
}else if(value > node.value){
node.right = put(node.right,value);
if ( (height(node.right) - height(node.left)) == 2){
if(value > node.right.value){
node = rightRightRotation(node);
}else{
node = rightLeftRotation(node);
}
}
// 相等
}else{
System.out.println("不能插入相同的值");
}
// 插入节点后,树高+1
node.height = max( height(node.left),height(node.right) ) + 1;
return node;
}
// 删除
public void remove(int value){
// 找到删除节点的位置
Node node = search(root,value);
if(node != null){
root = remove(root,node);
}
}
private Node remove(Node tree, Node node){
if( tree == null || node == null){
return null;
}
// 左移,删除后更新上层节点的左节点
if(node.value < tree.value){
tree.left = remove(tree.left,node);
// 删除后不平衡,删左右高
if(height(tree.right ) - height(tree.left) == 2){
Node temp = tree.right; // 看删除节点的兄弟,左右谁高,即判断是不是斜树
if(height(temp.left) > height(temp.right)){
tree = rightLeftRotation(tree);
}else{
tree = rightRightRotation(tree);
}
}
// 右移
}else if(node.value > tree.value){
tree.right = remove(tree.right,node);
if(height(tree.left) - height(tree.right) == 2){
Node temp = tree.left;
if(height(temp.right) > height(temp.left)){
tree = leftRightRotation(tree);
}else{
tree = leftLeftRotation(tree);
}
}
//是该结点了
}else{
// 删除分三情况
// 1. 左右孩子不为空
if(tree.left != null && tree.right != null){
if(height(tree.left) > height(tree.right)){
Node max = max(tree.left);
tree.value = max.value;
tree.left = remove(tree.left, max);
}else{
Node min = min(tree.right);
tree.value = min.value;
tree.right = remove(tree.right, min);
}
// 2.3. 左右孩子不全存在,那么直接孩子代替父亲
}else{
Node temp = tree;
tree = (tree.left != null) ? tree.left : tree.right;
temp = null;
}
}
return tree;
}
// 测试
public static void main(String[] args) {
AVLTree tree = new AVLTree();
int[] arrs = {3,2,1,4,5,6,7,16,15,14,13,12,11,10,8,9};
for (int value : arrs){
tree.put(value);
}
tree.preOrder();
System.out.println("\n-------------");
System.out.println("最大值:" + tree.max());
System.out.println("最小值:" + tree.min());
System.out.println("树高:" + tree.height());
}
}