数据结构之高度平衡搜索树AVL树(含经典面试题----判断一棵树是否是AVL树)

什么是AVL树

AVL树简介

AVL树又称为高度平衡的二叉搜索树,目的在于尽量降低二叉树的高度,减少平均搜索长度。

满足二叉搜索树的性质

类比二叉搜索树,先将树的结构确定下来,下面处理满足AVL树独有的性质即可。

满足AVL树的性质

  1. 左子树和右子树高度差的绝对值不超过1。
  2. 树中的每一个左子树和每一个右子树都是AVL树。
  3. 每一个节点都有一个平衡因子,AVL树中任意节点的平衡因子可取值为-1,1,0 。

AVL树插入、删除、查找效率问题

一颗AVL树有N个节点,高度可以保持在log2N,插入、删除、查找的时间复杂度也是O(log2N).

AVL树的插入

插入一个元素,只需要找到该元素合适的位置,然后插入进去(不能插入相同元素),此时还只是满足二叉搜索树的性质,但是AVL树是二叉搜索树的升级版,所以还要保证树是平衡的。那就要求每次插入元素后检查是否会影响树的高度,即是否影响树的平衡性,此时插入完毕,如果影响树的高度就要做出调整。

平衡因子的更新规则
1、左子树插入,那么该节点对应的父亲节点的bf-=1;
2、右子树插入节点,该节点对应父亲节点的bf+=1;

更新后的情况作如下分析:

情况1:当插入一个节点后当前节点的父节点的平衡因子变为0,说明当前树是平衡的,不会对上层节点产生影响,所以结束更新直接返回。

情况2:假如插入节点之后该节点的父节点平衡因子变为1或者-1,此时说明之前父节点的平衡因子是0,那就破坏了平衡,那就会对上层节点的平衡性产生影响。此时就需要不断循环向上层更新,只要上层是1或者-1,循环就无法停下,直到父节点为根节点为止,才算插入完毕。

情况3:(循环向上层更新父节点出现的)如果父节点的平衡因子变成-2 或者2,那么就要进行旋转,更新节点的bf值,调整树的高度。

情况4:节点的平衡因子为-3 或者3,这种情况是不可能出现的,如果有说明原来的树就不是AVL树。我们要保证每个树再插入之前都是一颗AVL树。

bool Insert(const K& key){
        if (_root == NULL){
            _root = new Node(key);
            return true;
        }
        Node* cur = _root;
        Node* father = NULL;
        while (cur){
            if (cur->_key == key){
                return false;
            }
            else if (cur->_key < key){
                father = cur;
                cur = cur->_right;
            }
            else{
                father = cur;
                cur = cur->_left;
            }
        }
        //此时说明已经确定要插入的位置
        cur = new Node(key);
        if (father->_key < cur->_key){
            father->_right = cur;
            cur->_parent = father;
        }
        else(father->_key > cur->_key){
            father->_left = cur;
            cur->_parent = father;
        }
        //插入完成后要记得更新平衡因子
        while (father){
            if (father->_left == cur){
                father->_bf -= 1;
            }
            else (father->_right == cur){
                father->_bf += 1;
            }
            //根据更新后的平衡因子判断树是否平衡,如果不平衡进行旋转调整
            if (father->_bf == 0){
                return true;
            }
            else if (father->_bf == -1 || father->_bf == 1){
                cur = father;
                father = father->_parent;
                //此时还不确定是否是不平衡的,要进一步向上追溯看上面父节点的bf
                //值是否为-2或者2,如果是就要旋转,如果不是就不需要旋转
            }
            else if (father->_bf == -2 || father->_bf == 2){
                if (father->_bf == 2 && cur->_bf == 1){
                    RotateL(father);
                    return true;
                }
                if (father->_bf == -2 && cur->_bf == -1){
                    RotateR(father);
                    return true;
                }
                if (father->_bf == 2 && cur->_bf == -1){
                    RotateRL(father);
                    return true;
                }
                if (father->_bf == -2 && cur->_bf == 1){
                    RotateLR(father);
                    return true;
                }
            }
            else{
                cout << "平衡因子异常" << endl;
            }
        }
    }

解决情况3的旋转方法实现(旋转的分类)

1、左单旋:parent->bf=2;subR->bf=1

《数据结构之高度平衡搜索树AVL树(含经典面试题----判断一棵树是否是AVL树)》

思路:
1、将parent节点和subRL节点链接起来(parent->_right=subRL);
2、如果ppNode为空,那么_root=subR。
3、ppNode不为空时,判断parent是ppNode的左孩子还是右孩子,如果是左孩子,那么ppNode->_left=subR;反之ppNode->_right=subR。
4、最后一步将subR和parent连接起来(subR->_left=parent,parent->_parent=subR)。

void RotateL(Node* parent){
        Node* SubR = parent->_right;
        Node* SubRL = SubR->_left;
        Node*PpNode = parent->_parent;
        parent->_right = SubRL;
        if (SubRL != NULL){
            SubRL->_parent = parent;
        }
        if (PpNode == NULL){
            _root = SubR;
            SubR->_parent = PpNode;
        }
        else{
            if (PpNode->_left == parent){
                PpNode->_left = SubR;
                SubR->_parent = PpNode;
            }
            else{
                PpNode->_right = SubR;
                SubR->_parent = PpNode;
            }
        }
        SubR->_left = parent;
        parent->_parent = SubR;
        parent->_bf = 0;
        SubR->_bf = 0;
    }

右单旋:parent->bf=-2;subL->bf=-1

《数据结构之高度平衡搜索树AVL树(含经典面试题----判断一棵树是否是AVL树)》

思路:
1、将subLR连到parent的左边(sub小于subLR 小于parent)。
2、记录父亲的上层节点ppNode。然后判断ppNode是否为空,是则说明父节点是根节点,只需要将根节点更新成subL即可。
3、ppNode不为空 ,此时只要直到原来的parent节点是ppNode节点的左边还是右边就可以确定subL是ppNode节点的左边还是右边。
4、最后一步就是将subL和parent连接起来,subL->_right=parent。

void RotateR(Node* parent){
        Node* SubL = parent->_left;
        Node* SubLR = SubL->_right;
        Node* PpNode = parent->_parent;
        parent->_left = SubLR;
        if (SubLR){
            SubLR->_parent = parent;
        }
        if (PpNode == NULL){
            _root = SubL;
            SubL->_parent = NULL;
        }
        else{
            if (PpNode->_left == parent){
                PpNode->_left = SubL;
            }
            else{
                PpNode->_right = SubL;
            }
            SubL->_parent = PpNode;
        }
        SubL->_right = parent;
        parent->_parent = SubL;
        SubL->_bf = 0;
        parent->_bf = 0;
    }

左右双旋:parent->bf=-2;subL->bf=1

《数据结构之高度平衡搜索树AVL树(含经典面试题----判断一棵树是否是AVL树)》
《数据结构之高度平衡搜索树AVL树(含经典面试题----判断一棵树是否是AVL树)》

思路:
由于在subLR的右边插入了新节点,那么首先对以subLR为父节点进行左单旋(RotateL(subL)),完成左旋之后在以parent为父节点进行右单旋(RotateR(parent))。

在更新平衡因子时符合以下规律:

原来的subLR1(新插入节点在subLR的右边)0-1(新插入节点在subLR的左边)
更新后的parent001
更新后的subL100
更新后的subLR000
void RotateLR(Node* parent){
        Node* SubL = parent->_left;
        Node* SubLR = SubL->_right;
        int bf = SubLR->_bf;
        RotateL(SubL);
        RotateR(parent);
        //更新平衡因子
        if (bf == 0){
            parent->_bf = 0;
            SubL->_bf = 0;
        }
        else if (bf == 1){
            parent->_bf = 0;
            SubL->_bf = -1;
        }
        else{
            parent->_bf = 1;
            SubL->_bf =0;
        }
        SubLR->_bf = 0;
    }

右左双旋:parent->bf=2;subR->bf=-1;

《数据结构之高度平衡搜索树AVL树(含经典面试题----判断一棵树是否是AVL树)》
《数据结构之高度平衡搜索树AVL树(含经典面试题----判断一棵树是否是AVL树)》

思路:
由于在subRL的左边插入了新节点,那么首先对以subR为父节点进行右单旋(RotateR(subR)),完成左旋之后在以parent为父节点进行左单旋(RotateL(parent))。

在更新平衡因子时符合以下规律:

原来的subRL1(新插入节点在subRL的右边)0-1(新插入节点在subRL的左边)
更新后的parent-100
更新后的subR001
更新后的subRL000
void RotateRL(Node* parent){
        Node* SubR = parent->_right;
        Node* SubRL = SubR->_left;
        int bf = SubRL->_bf;
        RotateR(subR);
        RotateL(parent);
        //更新平衡因子
        if (bf == 0){
            parent->_bf = 0;
            SubR->_bf = 0;
            SubRL->_bf = 0;
        }
        else if (bf == 1){
            parent->_bf = -1;
            SubR->_bf = 0;
            SubRL->_bf = 0;
        }
        else if(bf==-1){
            parent->_bf =0;
            SubR->_bf = 1;
            SubRL->_bf = 0;
        }
        else{
            cout << "参数有误" << endl;
        }
    }

经典面试题——判断一棵树是否是AVL树

思路1:递归的判断左右子树是不是AVL树,同时判断某一个子树是否是AVL树的时候又得递归求一遍其左右子树的高度,通过高度差来判断对应子树是否是AVL树,这样一来时间复杂度就是O(N^2)

在你答出上面的思路时候,面试官常常会让优化时间复杂度!!!
优化思路1的时间复杂度为O(N):判断一棵树是否是AVL树,同时借用一个输出型参数height带出左右子树的高度,利用高度差的绝对值小于2,得出结论。
(不采用递归查看平衡因子的方式是由于有的时候平衡因子更新出错,导致误判)

bool IsAVLtree(){
        int height = 0;
        return _IsAVLtree(_root, height);
    }
    bool _IsAVLtree(Node* root, int& height){
        if (root == NULL){
            height = 0;
            return true;
        }
        int lheight = 0;
        if (_IsAVLtree(root->_left, lheight) == false){
            return false;
        }
        int rheight = 0;
        if (_IsAVLtree(root->_right, rheight) == false){
            return false;
        }
        if (abs(lheight - rheight) !=root->_bf){
            cout << "平衡因子有误\n";
        }
        height = lheight > rheight ? lheight + 1 : rheight + 1;
        return abs(lheight - rheight) < 2;
    }

完整代码:

#include<iostream>
using namespace std;

template<class K>
struct TreeNode{
public:
    TreeNode(const K& key)
        :_parent(NULL)
        , _left(NULL)
        , _right(NULL)
        , _key(key)
        , _bf(0)
    {}
    TreeNode<K>* _parent;
    TreeNode<K>* _left;
    TreeNode<K>* _right;
     K _key;
    int _bf;
};
template< class K >

class AVLTree{
public:
    typedef TreeNode<K> Node;
    AVLTree()
        :_root(NULL)
    {}
    bool Insert(const K& key){
        if (_root == NULL){
            _root = new Node(key);
            return true;
        }
        Node* cur = _root;
        Node* father = NULL;
        while (cur){
            if (cur->_key == key){
                return false;
            }
            else if (cur->_key < key){
                father = cur;
                cur = cur->_right;
            }
            else{
                father = cur;
                cur = cur->_left;
            }
        }
        //此时说明已经确定要插入的位置
        cur = new Node(key);
        if (father->_key < cur->_key){
            father->_right = cur;
            cur->_parent = father;
        }
        else(father->_key > cur->_key){
            father->_left = cur;
            cur->_parent = father;
        }
        //插入完成后要记得更新平衡因子
        while (father){
            if (father->_left == cur){
                father->_bf -= 1;
            }
            else (father->_right == cur){
                father->_bf += 1;
            }
            //根据更新后的平衡因子判断树是否平衡,如果不平衡进行旋转调整
            if (father->_bf == 0){
                return true;
            }
            else if (father->_bf == -1 || father->_bf == 1){
                cur = father;
                father = father->_parent;
                //此时还不确定是否是不平衡的,要进一步向上追溯看上面父节点的bf
                //值是否为-2或者2,如果是就要旋转,如果不是就不需要旋转
            }
            else if (father->_bf == -2 || father->_bf == 2){
                if (father->_bf == 2 && cur->_bf == 1){
                    RotateL(father);
                    return true;
                }
                if (father->_bf == -2 && cur->_bf == -1){
                    RotateR(father);
                    return true;
                }
                if (father->_bf == 2 && cur->_bf == -1){
                    RotateRL(father);
                    return true;
                }
                if (father->_bf == -2 && cur->_bf == 1){
                    RotateLR(father);
                    return true;
                }
            }
            else{
                cout << "平衡因子异常" << endl;
            }
        }
    }

    //左单旋
    void RotateL(Node* parent){
        Node* SubR = parent->_right;
        Node* SubRL = SubR->_left;
        Node*PpNode = parent->_parent;
        parent->_right = SubRL;
        if (SubRL != NULL){
            SubRL->_parent = parent;
        }
        if (PpNode == NULL){
            _root = SubR;
            SubR->_parent = PpNode;
        }
        else{
            if (PpNode->_left == parent){
                PpNode->_left = SubR;
                SubR->_parent = PpNode;
            }
            else{
                PpNode->_right = SubR;
                SubR->_parent = PpNode;
            }
        }
        SubR->_left = parent;
        parent->_parent = SubR;
        parent->_bf = 0;
        SubR->_bf = 0;
    }
    //右单旋
    void RotateR(Node* parent){
        Node* SubL = parent->_left;
        Node* SubLR = SubL->_right;
        Node* PpNode = parent->_parent;
        parent->_left = SubLR;
        if (SubLR){
            SubLR->_parent = parent;
        }
        if (PpNode == NULL){
            _root = SubL;
            SubL->_parent = NULL;
        }
        else{
            if (PpNode->_left == parent){
                PpNode->_left = SubL;
            }
            else{
                PpNode->_right = SubL;
            }
            SubL->_parent = PpNode;
        }
        SubL->_right = parent;
        parent->_parent = SubL;
        SubL->_bf = 0;
        parent->_bf = 0;
    }
    //左右双旋
    void RotateLR(Node* parent){
        Node* SubL = parent->_left;
        Node* SubLR = SubL->_right;
        int bf = SubLR->_bf;
        RotateL(SubL);
        RotateR(parent);
        //更新平衡因子
        if (bf == 0){
            parent->_bf = 0;
            SubL->_bf = 0;
        }
        else if (bf == 1){
            parent->_bf = 0;
            SubL->_bf = -1;
        }
        else{
            parent->_bf = 1;
            SubL->_bf =0;
        }
        SubLR->_bf = 0;
    }
    //右左双旋
    void RotateRL(Node* parent){
        Node* SubR = parent->_right;
        Node* SubRL = SubR->_left;
        int bf = SubRL->_bf;
        RotateR(subR);
        RotateL(parent);
        //更新平衡因子
        if (bf == 0){
            parent->_bf = 0;
            SubR->_bf = 0;
            SubRL->_bf = 0;
        }
        else if (bf == 1){
            parent->_bf = -1;
            SubR->_bf = 0;
            SubRL->_bf = 0;
        }
        else if(bf==-1){
            parent->_bf =0;
            SubR->_bf = 1;
            SubRL->_bf = 0;
        }
        else{
            cout << "参数有误" << endl;
        }
    }
    void InOrder(){
        _InOrder(_root);
        cout <<endl;
    }
    void _InOrder(Node* root){
        if (root == NULL){
            return;
        }
        _InOrder(_root->_left);
        cout << _root->_key << " ";
        _InOrder(_root->_right);
    }
    void Destroy(){
        if (_root == NULL){
            return;
        }
        Destroy(_root->_left);
        Destroy(_root->_right);
        delete _root;
        _root = NULL;
    }
    //经典面试题-----判断一棵树是否为平衡树
    bool IsAVLtree(){
        int height = 0;
        return _IsAVLtree(_root, height);
    }
    bool _IsAVLtree(Node* root, int& height){
        if (root == NULL){
            height = 0;
            return true;
        }
        int lheight = 0;
        if (_IsAVLtree(root->_left, lheight) == false){
            return false;
        }
        int rheight = 0;
        if (_IsAVLtree(root->_right, rheight) == false){
            return false;
        }
        if (abs(lheight - rheight) !=root->_bf){
            cout << "平衡因子有误\n";
        }
        height = lheight > rheight ? lheight + 1 : rheight + 1;
        return abs(lheight - rheight) < 2;
    }
private:
    Node* _root;
};
int main(){
    AVLTree<int> tree;
    tree.Insert(1);
    tree.Insert(2);
    tree.Insert(3);
    tree.Insert(4);
    tree.InOrder();
    cout << tree.IsAVLtree() << endl;
    system("pause");
    return 0;
}
    原文作者:AVL树
    原文地址: https://blog.csdn.net/cx2479750196/article/details/80960674
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞