构造AVL树基础 + 力扣1382. 将二叉搜索树变平衡

构造AVL树基础

定义

对于任意一个节点,左子树和右子树的高度差不能超过1。

怎么计算

  • 标注节点的高度
  • 计算平衡因子

如何维持平衡

如果平衡被打破需要根据不同的情况来旋转修改树,如插入2后,原有的平衡被打破,节点8变得不平衡:

左旋操作

针对情况 RR

插入的元素在不平衡的节点的右侧的右侧,即 RR

对节点y进行向左旋转操作,返回旋转后的新的根节点x
             y                                 x
            / \                               / \
           T4  x      向左旋转 (y)            y    z
              / \     -------------->      / \   / \
             T3  z                        T4 T3 T1  T2
                / \
               T1 T2
 具体步骤
  • x.left = y
  • y.right = T3
  • 更新节点高度
初始条件T4 < y < T3 < x < T1 < z < T2
             y                                 x
            / \                               / \
           T4  x      向左旋转 (y)            y    z
              / \     -------------->      / \   / \
             T3  z                        T4 T3 T1  T2
                / \
               T1 T2

step 1: x.left = y
             y                                 x
            / \                               / \
           T4  x                             y    z         T3(单独)   
              / \     -------------->      /     / \
             T3  z                        T4    T1  T2
                / \                     
               T1 T2                            

step 2: y.right = T3
              x
             / \
           y    z
          / \   / \
         T4 T3 T1  T2

        

右旋操作

针对情况 LL

插入的元素在不平衡的节点的左侧的左侧,即 LL

 对节点y进行向右旋转操作,返回旋转后的新的根节点x
                y                                 x
               / \                               / \
              x   T4      向右旋转 (y)           z    y
             / \        -------------->       / \   / \
            z   T3                           T1 T2 T3  T4
           / \
          T1  T2
 具体步骤
  • x.left = y
  • y.right = T3
  • 更新节点高度
初始条件T1 < z < T2 < x < T3 < y < T4
                y                                 x
               / \                               / \
              x   T4      向右旋转 (y)          z    y
             / \        -------------->       / \   / \
            z   T3                           T1 T2 T3  T4
           / \
          T1  T2

step 1: x.right = y

                y                                 x
               / \                               / \
              x   T4                            z    y     T3(单独)
             / \        -------------->       / \     \
            z   T3                           T1 T2     T4
           / \
          T1  T2

step 2: y.left = T3
              
         x
        / \
       z    y
      / \   / \
    T1 T2 T3  T4

LR和RL

LR

首先对x进行左旋转,转化为了了LL的情况,接下来按LL处理

RL:
                y                                 y
               / \                               / \
              x   T4   首先对x进行左旋转          z    T4
             / \        -------------->       / \   
            T1   z                           x  T3 
                / \                         / \
              T2  T3                       T1 T2
RL
首先对x进行右旋转,转化为了了RR的情况,接下来按RR处理
LR:
                y                                 y
               / \                               / \
              T1  x   首先对x进行右旋转           T1   z
                 / \        -------------->        / \   
                z   T4                           T2   x 
               / \                                   / \
              T2  T3                                T3 T4

给你一棵二叉搜索树,请你返回一棵 平衡后 的二叉搜索树,新生成的树应该与原来的树有着相同的节点值。如果有多种构造方法,请你返回任意一种。

如果一棵二叉搜索树中,每个节点的两棵子树高度差不超过 1 ,我们就称这棵二叉搜索树是 平衡的 。

示例 1:

输入:root = [1,null,2,null,3,null,4,null,null]
输出:[2,1,3,null,null,null,4]
解释:这不是唯一的正确答案,[3,1,4,null,2,null,null] 也是一个可行的构造方案。

示例 2:

输入: root = [2,1,3]
输出: [2,1,3]

提示:

  • 树节点的数目在 [1, 104] 范围内。
  • 1 <= Node.val <= 105

 

两个题解都来自burning-summer

都是java写的,本文用的c++

题解一:最优并且不需要完整构建AVL树

手撕AVL并不是最优解,只是通解,时间复杂度是nlog(n)。

利用二叉搜索树的性质,中序遍历输出,然后以中间为root,递归构造树,效率更高,算是本题的最优解。

查看代码
 /**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> vec;
    //按中序遍历并依次存储节点
    void inOrder(TreeNode* cur){
        if(cur==nullptr)
            return;
        inOrder(cur->left);
        vec.emplace_back(cur->val);
        inOrder(cur->right);
    }
    TreeNode* buildTree(int l,int r){
        if(l>r)
            return nullptr;
        int mid=floor((l+r)/2);
        //中间节点作为根节点,把中间节点的左右节点作为根节点的叶子节点
        //   int mid = start + (end - start >> 1);
        TreeNode* root =new TreeNode(vec[mid]);
        // 递归构造左右子树
        root->left = buildTree(l,mid-1);
        root->right = buildTree(mid+1,r);
        // 返回根节点
        return root;

    }
    TreeNode* balanceBST(TreeNode* root) {
        inOrder(root);
        return buildTree(0,vec.size()-1);
    }
};

题解二:构造AVL树

先遍历得到原树的节点,然后依次添加到AVL树中。

查看代码
 /**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    //记录node节点的高度
    map<TreeNode*,int> height;
    TreeNode* balanceBST(TreeNode* root) {
        if(root==nullptr)
            return nullptr;
        
        //通过插入操作建树
        stack<TreeNode*> st;
        TreeNode* newroot=nullptr;
        TreeNode* node=root;
        // 先序遍历插入(其实用哪个遍历都行)
        while(node!=nullptr||!st.empty()){
            if(node!=nullptr){
                // 不停插入新节点,得到当前的根节点
                newroot=insert(newroot,node->val);
                st.push(node);
                node=node->left;//先序一直往左走
            }
            else{//左边为空开始取右边的
                node=st.top();
                st.pop();
                node=node->right;
            }
        }
        return newroot;
    }
    // 新节点插入
    TreeNode* insert(TreeNode* root,int val){
        if(root==nullptr){
            root=new TreeNode(val);
            height.insert({root,1});//新节点的高度为1
            return root;
        }
        TreeNode* node=root;
        int cmp=val-node->val;
        if(cmp<0){
            //左子树插入
            node->left=insert(root->left,val);
            //如果左右子树高度差超过1,进行旋转调整
            if(height[node->left]-height[node->right]>1){
                if(val>node->left->val){
                    //插入在右孩子左边,RL,右孩子先右旋,转换为RR
                    node->left = rotateLeft(node->left);
                }
                //节点左旋,处理RR情况
                node=rotateRight(node);
            }
        }else if(cmp>0){
            // 右子树插入
            node->right = insert(root->right,val);
            // 如果左右子树高度差超过1,进行旋转调整
            if (height[node->right] - height[node->left] > 1){
                if (val < node->right->val){
                    // 插入在右孩子左边,右孩子先右旋
                    node->right = rotateRight(node->right);
                }
                // 节点左旋
                node = rotateLeft(node);
            }
        }
        else{
            // 一样的节点,啥都没发生
            return node;
        }
        // 获取当前节点新高度
        int curheight =  getCurNodeNewHeight(node);
        // 更新当前节点高度
        height[node]=curheight;
        return node;
    }
    /**
     * node节点左旋
      对节点y进行向左旋转操作,返回旋转后的新的根节点x
             y                                 x
            / \                               / \
           T4  x      向左旋转 (y)            y    z
              / \     -------------->      / \   / \
             T3  z                        T4 T3 T1  T2
                / \
               T1 T2
     */
    TreeNode* rotateLeft(TreeNode* node){
        // ---指针调整
        TreeNode* right = node->right;
        node->right = right->left;
        right->left = node;
        // ---高度更新
        // 先更新node节点的高度,这个时候node是right节点的左孩子
        int newNodeHeight = getCurNodeNewHeight(node);
        // 更新node节点高度
        // nodeHeight.put(node,newNodeHeight);
        height[node]=newNodeHeight;
        // newNodeHeight是现在right节点左子树高度,原理一样,取现在right左右子树最大高度+1
        int newRightHeight = max(newNodeHeight,height[right->right]) + 1;
        // 更新原right节点高度
        // nodeHeight.put(right,newRightHeight);
        height[right]=newRightHeight;
        return right;
    }

    /**
     * node节点右旋
     对节点y进行向右旋转操作,返回旋转后的新的根节点x
                y                                 x
               / \                               / \
              x   T4      向右旋转 (y)           z    y
             / \        -------------->       / \   / \
            z   T3                           T1 T2 T3  T4
           / \
          T1  T2
     */
    TreeNode* rotateRight(TreeNode* node){
        // ---指针调整
        TreeNode* left = node->left;
        node->left = left->right;
        left->right = node;
        // ---高度更新
        // 先更新node节点的高度,这个时候node是right节点的左孩子
        int newNodeHeight = getCurNodeNewHeight(node);
        // 更新node节点高度
        // nodeHeight.put(node,newNodeHeight);
        height[node]=newNodeHeight;
        // newNodeHeight是现在left节点右子树高度,原理一样,取现在right左右子树最大高度+1
        int newLeftHeight = max(newNodeHeight,height[left->left]) + 1;
        // 更新原left节点高度
        // nodeHeight.put(left,newLeftHeight);
        height[left]=newLeftHeight;
        return left;
    }

    /**
     * 获取当前节点的新高度
     * @param node node
     * @return 当前node的新高度
     */
    int getCurNodeNewHeight(TreeNode* node){
        // node节点的高度,为现在node左右子树最大高度+1
        return max(height[node->left],height[node->right]) + 1;
    }
};

 

 

posted @ 2023-02-10 15:43  付玬熙  阅读(29)  评论(0编辑  收藏  举报