Java數據結構(十四)—— 平衡二叉樹(AVL樹)

平衡二叉樹(AVL樹)

二叉排序樹問題分析java

  1. 左子樹所有爲空,從形式上看更像一個單鏈表node

  2. 插入速度沒有影響git

  3. 查詢速度明顯下降ide

  4. 解決方案:平衡二叉樹this

基本介紹atom

  1. 平衡二叉樹也叫二叉搜索樹,保證查詢效率較高spa

  2. 它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,而且左右兩棵子樹都是一棵平衡二叉樹對象

  3. 經常使用的實現方法有紅黑樹、AVL、替罪羊樹、Treap、伸展樹等blog

平衡二叉樹左旋轉

使用條件排序

右子樹高度與左子樹高度插值大於1的時候,使用左旋轉

要求

給定數列{4,3,6,5,7,8},建立對應的平衡二叉樹

建立二叉排序樹

image-20201206153816613

此時若轉換爲平衡二叉樹,下降右子樹的高度

思路分析

  1. 建立一個新節點newNode ,值等於當前根節點的值

  2. 把新節點的左子樹設置爲當前節點的左子樹,newnode.left = left

  3. 把新結點的右子樹設置爲當前節點右子樹的左子樹newNode.right = right.left

  4. 把當前節點的值換位右子節點的值 value = right.value

  5. 把當前節點的右子樹設置成右子樹的右子樹right = right.right

  6. 把當前節點的左子樹設置爲新節點 left = newLeft

轉換後

image-20201206154518606

平衡二叉樹右旋轉

要求

使用數列{10,12,8,9,7,6},建立平衡二叉樹

建立二叉排序樹

image-20201206163842168

基本思路

  1. 建立新的節點newNode,使得newNode.value = this.value

  2. 將newNode的右子樹設置爲this的右子樹,newNode.right = this.right

  3. 將newNode的左子樹設置爲this左子樹的右子樹,newNode.left = this.left.right

  4. 把this節點的值換爲左子節點的值,this.value = this.left.value

  5. 將this節點的左子樹設置爲左子樹的左子樹,this.left = this.left.left

  6. 將this節點的右子樹 設置爲newNode,this.right = newNode

轉換後

image-20201206164416964

平衡二叉樹雙旋轉

要求

使用數列{10,11,7,6,8,9},建立平衡二叉樹

建立二叉排序樹

image-20201206165644636

思路分析

  • 當孩子節點知足左旋轉或右旋轉條件時,先平衡孩子 節點,後平衡父節點

建立平衡二叉樹代碼實現

package com.why.tree;

/**
* @Description TODO 平衡二叉樹
* @Author why
* @Date 2020/12/6 15:56
* Version 1.0
**/
public class AVLTreeDemo {
   public static void main(String[] args) {
       int[] arr = {10,11,7,6,8,9};
       //建立AVLTree對象
       AVLTree avlTree = new AVLTree();

       //添加節點
       for (int i = 0; i < arr.length; i++) {
           avlTree.add(new AVLNode(arr[i]));
      }

       //遍歷
       System.out.println("中序遍歷:");
       avlTree.midOrder();

       //根節點樹的高度
       System.out.println("根節點樹的高度: " + avlTree.height());
       System.out.println("左子樹高度:" + avlTree.leftHeight());
       System.out.println("右子樹高度:" + avlTree.rightHeight());
       System.out.println("根節點:" + avlTree.getRoot());

  }
}

/**
* AVL,平衡二叉樹
*/
class AVLTree{
   private AVLNode root;

   public AVLNode getRoot() {
       return root;
  }

   public void setRoot(AVLNode root) {
       this.root = root;
  }

   /**
    * 添加節點
    * @param node
    */
   public void add(AVLNode node){
       if (root == null){//直接放上
           root = node;
      }else {
           root.add(node);
      }
  }

   /**
    * 中序遍歷
    */
   public void midOrder(){
       if (root != null){
           root.midOrder();
      }else {
           System.out.println("二叉排序樹爲空");
      }
  }

   /**
    * 查找需刪除的節點
    * @param value
    * @return
    */
   public AVLNode search(int value){
       if (root == null){
           return null;
      }else {
           return root.search(value);
      }
  }

   /**
    * 查找父節點
    * @param value
    * @return
    */
   public AVLNode searchParent(int value){
       if (root == null){
           return null;
      }else {
           return root.searchParent(value);
      }
  }

   public void deleteNode(int value){
       if (root == null){
           return;
      }else {
           //找到需刪除的節點
           AVLNode targetNode = search(value);
           if (targetNode == null){//未找到
               return;
          }
           //若是二叉排序樹只有一個節點
           if (root.left == null && root.right == null){
               return;
          }

           //查找需刪除的節點的父節點
           AVLNode parent = searchParent(value);
           if (targetNode.left == null && targetNode.right == null){//刪除的節點是葉子節點
               //判斷targetNode是父節點的左子節點仍是右子節點
               if (parent.left != null && parent.left.value == value){//是左子節點
                   parent.left = null;
              }else if (parent.right != null && parent.right.value == value){//是右子節點
                   parent.right = null;
              }
          }else if ((targetNode.left != null && targetNode.right == null) ||
                  (targetNode.right != null && targetNode.left == null)) {//只有一棵子樹
               //肯定targetNode的節點是左節點仍是右節點
               if (targetNode.left != null) {//左子節點
                   if (parent != null){//非根節點
                       //肯定targetNode是parent的左子節點仍是右子節點
                       if (parent.left.value == value) {//左子節點
                           parent.left = targetNode.left;
                      } else {//右子節點
                           parent.right = targetNode.left;
                      }
                  }else {
                       root = targetNode.left;
                  }
              } else {//右子節點
                   if (parent != null){
                       //肯定targetNode是parent的左子節點仍是右子節點
                       if (parent.left.value == value) {//左子節點
                           parent.left = targetNode.right;
                      } else {//右子節點
                           parent.right = targetNode.right;
                      }
                  }else {
                       root = targetNode.right;
                  }
              }
          }else {//刪除的節點有兩顆子樹
               //找到最小值並刪除
               int minValue = deleteRightMin(targetNode.right);
               //將最小值賦值給targetNode.value
               targetNode.value = minValue;
          }
      }
  }

   /**
    * 尋找最小值
    * @param node
    * @return
    */
   public int deleteRightMin(AVLNode node){
       AVLNode target = node;
       while (target.left != null){
           target = target.left;
      }
       //這時target指向最小節點
       //刪除最小節點
       deleteNode(target.value);
       //返回最小節點的value
       return target.value;
  }

   /**
    * 返回根節點樹的高度
    * @return
    */
   public int height(){
       return root.height();
  }

   /**
    * 左子樹的高度
    * @return
    */
   public int leftHeight(){
       return root.leftHeight();
  }

   /**
    * 右子樹的高度
    * @return
    */
   public int rightHeight(){
       return root.rightHeight();
  }

}

/**
* 節點類
*/
class AVLNode{
   int value;
   AVLNode left;
   AVLNode right;

   public AVLNode(int value) {
       this.value = value;
  }

   /**
    * 添加節點,遞歸形式,需知足二叉排序樹的要求
    * @param node
    */
   public void add(AVLNode node){
       if (node == null){
           return;
      }
       //判斷傳入的節點的值和當前子樹的根節點的值的關係
       if (node.value < this.value){
           if (this.left == null){//當前節點左子節點爲空
               this.left = node;
          }else {//不爲空,遞歸向左子樹添加
               this.left.add(node);
          }
      }else {
           if (this.right == null){
               this.right = node;
          }else {
               this.right.add(node);
          }
      }

       //當添加完節點後,若右子樹的高度比左子樹的高度的數值大於1
       if (rightHeight() - leftHeight() > 1){
           if (right != null && right.leftHeight() > right.rightHeight()){
               //對右子樹 右旋轉
               right.rightRotate();
          }
           //左旋轉
           this.leftRotate();
           return;
      }
       //當添加完節點後leftHeight - rightHeight > 1
       if (leftHeight() - rightHeight() > 1){
           if (left != null && left.rightHeight() > left.leftHeight()){
               //對左子樹左旋轉
               left.leftRotate();
          }
           //右旋轉
           this.rightRotate();
           return;
      }
  }

   /**
    * 中序遍歷
    */
   public void midOrder(){
       if (left != null){
           this.left.midOrder();
      }
       System.out.println(this);
       if (this.right != null){
           this.right.midOrder();
      }
  }

   @Override
   public String toString() {
       return "Node{" +
               "value=" + value +
               '}';
  }

   /**
    * 尋找須要刪除的節點
    * @param value
    * @return
    */
   public AVLNode search(int value){
       if (value == this.value){//找到
           return this;
      }else if (value < this.value){//向左子樹查找
           if (this.left == null){
               return null;
          }
           return this.left.search(value);
      }else {//向右子樹查找
           if (this.right == null){
               return null;
          }
           return this.right.search(value);
      }
  }

   /**
    * 查找須要刪除節點的父節點
    * @param value
    * @return
    */
   public AVLNode searchParent(int value){
       if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)){
           //找到父節點返回當前節點
           return this;
      }else {
           //若是查找的值小於當前節點的值
           if (value < this.value && this.left != null){//左子樹查找
               return this.left.searchParent(value);
          }else if (value >= this.value && this.right != null){//右子樹查找
               return this.right.searchParent(value);
          }else {
               return null;//沒有找到父節點
          }
      }
  }

   /**
    * 返回以當前節點爲根節點的樹的高度
    * @return
    */
   public int height(){
       return Math.max(this.left == null ? 0 : this.left.height(),this.right == null ? 0 : this.right.height()) + 1;
  }

   /**
    * 返回左子樹的高度
    * @return
    */
   public int leftHeight(){
       if (left == null){
           return 0;
      }else {
           return left.height();
      }
  }

   /**
    * 返回右子樹的高度
    * @return
    */
   public int rightHeight(){
       if (right == null){
           return 0;
      }else {
           return right.height();
      }
  }

   /**
    * 左旋轉方法
    */
   private void leftRotate(){
       //建立新的節點,以當前根節點的值建立
       AVLNode newNode = new AVLNode(this.value);
       //把新的節點的左子樹設置爲當前節點的左子樹
       newNode.left = this.left;
       //把新節點的右子樹設置爲當前節點右子樹的左子樹
       newNode.right = this.right.left;
       //將當前節點的值修改成右子樹的值
       this.value = this.right.value;
       //將當前節點的右子樹設置爲右子樹的右子樹
       this.right = this.right.right;
       //將當前節點的左子節點設置爲新的節點
       this.left = newNode;
  }

   /**
    * 右旋轉
    */
   private void rightRotate(){
       //以當前節點的值建立新的節點
       AVLNode newNode = new AVLNode(this.value);
       //將新節點的右子樹設置爲當前節點的右子樹
       newNode.right = this.right;
       //將當前節點的左子樹設置爲當前節點左子節點的右子樹
       newNode.left = this.left.right;
       //將當前節點的值用左子節點的值替換
       this.value = this.left.value;
       //將當前節點的左子節點設置爲當節點左子節點的左子樹
       this.left = this.left.left;
       //將當前節點的右子節點設置爲新節點
       this.right = newNode;
  }
}

 

 全部源碼均可在gitee倉庫中下載:https://gitee.com/vvwhyyy/java_algorithm

相關文章
相關標籤/搜索