高级数据结构和算法4:BST(二叉搜索树)、AVL树(平衡二叉树)

visualgo.net BST AVL

1. 二叉查找树

二叉查找树(Binary Search Tree),又被称为二叉搜索树


1. 特点

  1. 任意节点的左子树不空, 则左子树上所有节点的key均小于它的根节点的key
  2. 任意节点的右子树不空, 则右子树上所有节点的key均大于它的根节点的key
  3. 任意节点的左,右子树也分别为二叉查找树
  4. 没有key相等的节点

二叉查找树进行中序遍历,可以得到一个递增的有序序列


BST的中序遍历

2. 结构

二叉搜索树通常使用链式存储孩子表示法。

struct Node {
    int key;
    struct Node* left;
    struct Node* right;
}

3. 操作

3.1 插入

  1. 如果二叉查找树为空,则直接插入。
  2. 如果插入节点key小于根结点key,则插入到左子树中;大于根结点key,则插入到右子树中。
  3. 依次类推,直到找到插入位置。

BST默认插入新的节点是叶子节点。

Node* Insert(Node*& node, int key){
    if(NULL == node) node = new Node(key);
    if (key < node->key) node->left = Insert(node->left, key);
    else if (key > node->key) node->right = Insert(node->right,key);    
    return node;
}

3.2 查找

  1. 查找指定key的节点
    从根结点开始,若二叉树非空,将给定值与根结点的关键字比较,若相等,则查找成功;若不等,则当给定值小于根结点关键字时,在根结点的左子树中查找,否则在根结点的右子树中查找。
    Node* Search(Node* node, int key){
        if(NULL == node) return NULL;
        if (key < node->key) return Search(node->left, key);
        else if (key > node->key) return Search(node->right, key);
        return node;
    }
    
  2. 查找最大/最小key的节点
    // 最大键
    Node* Maximum(Node* node){
        if (NULL == node) return NULL;
        while (NULL != node->right) node = node->right;
        return node;
    }
    
    // 最小键
    Node* Minimum(Node* node){
        if (NULL == node) return NULL;
        while (NULL != node->left) node = node->left;
        return node;
    }
    

3.3 删除

二叉查找树的删除操作是相对复杂一点,它要按 3 种情况来处理:

  1. 被删除结点是叶子结点,直接删除。


  2. 结点只有左子树或只有右子树,则让子树替代;


  3. 结点既有左子树,又有右子树,有两种处理方式
    替代删除,后继代替删除节点,然后删除后继;或者前驱代替删除节点,然后删除前驱。
    合并删除,右子树合并到左子树的最大值的右子树上;或者左子树合并到右子树最小值的左子树上。


    合并删除-增加树高.png
Node* Remove(Node* node, int key){
    if(NULL == node) return NULL;
    if (key < node->key) node->left = Remove(node->left, key);
    else if (key > node->key) node->right = Remove(node->right, key);
    else {
        if(NULL != node->right && NULL != node->left){// 有两个子树
            // 最小值删除
            Node* p = Minimum(node->right);
            node->key = p->key;
            node->right = Remove(node->right,p->key);
        } else {// 叶子节点或者只有一个子树
            Node* res = NULL;
            if(NULL != node->right) res = node->right;
            if(NULL != node->left) res = node->left;
            delete node;
            node = NULL;
            return res;
        }
    }
    return node;
}

优缺点

  1. 优点
No. 操作 时间复杂度
1 插入 O(log n)
2 查找 O(log n)
3 插入 O(log n)
  1. 缺点
    二叉搜索树构造顺序影响操作的时间复杂度。

参考代码

#include <iostream>
#include <queue>
using namespace std;

// 节点
struct Node{
    int key;        ///< 键
    Node *left;     ///< 左子节点
    Node *right;    ///< 右子节点
    Node(int key) 
    :key(key),left(NULL),right(NULL){}
};

// 辅助操作
Node* Maximum(Node* node){
    if (NULL == node) return NULL;
    while (NULL != node->right) node = node->right;
    return node;
}
Node* Minimum(Node* node){
    if (NULL == node) return NULL;
    while (NULL != node->left) node = node->left;
    return node;
}

// 核心操作
Node* Insert(Node*& node, int key){
    if(NULL == node) node = new Node(key);
    if (key < node->key) node->left = Insert(node->left, key);
    else if (key > node->key) node->right = Insert(node->right,key);    
    return node;
}

Node* Search(Node* node, int key){
    if(NULL == node) return NULL;
    if (key < node->key) return Search(node->left, key);
    else if (key > node->key) return Search(node->right, key);
    return node;
}
Node* Remove(Node* node, int key){
    if(NULL == node) return NULL;
    if (key < node->key) node->left = Remove(node->left, key);
    else if (key > node->key) node->right = Remove(node->right, key);
    else {
        if(NULL != node->right && NULL != node->left){// 有两个子树
            // 最小值删除
            Node* p = Minimum(node->right);
            node->key = p->key;
            node->right = Remove(node->right,p->key);
        } else {// 叶子节点或者只有一个子树
            Node* res = NULL;
            if(NULL != node->right) res = node->right;
            if(NULL != node->left) res = node->left;
            delete node;
            node = NULL;
            return res;
        }
    }
    return node;
}

/////////////////////////////////////////////////////////////
ostream& operator<<(ostream& os,const Node root) {
    queue<const Node*> q;
    q.push(&root);
    while(!q.empty()){
        const Node* curr = q.front();
        q.pop();
        if (NULL != curr){
            os << curr->key;
            q.push(curr->left);
            q.push(curr->right);
        } else {
            os << '#';
        }
    }
    return os;        
}
void TestInsert(Node*& root){
    Insert(root,1);
    Insert(root,2);
    Insert(root,3);
    Insert(root,4);
    Insert(root,5);
    cout << *root << endl;
}
void TestSearch(Node* root,int key){
    cout <<"Search " << key << ":" << Search(root,key) << endl;
}
void TestRemove(Node*& root,int key){
    root = Remove(root, key);
    cout <<"Remove " << key << ":" ;
    if(NULL != root) cout << *root << endl;
    else cout << "#" << endl;
}
void TestMax(Node* root){
    Node* maxNode = Maximum(root);
    cout << "Max:"<< maxNode->key <<":"<< maxNode <<endl;
}
void TestMin(Node* root){
    Node* minNode = Minimum(root);
    cout << "Min:"<< minNode->key <<":"<< minNode <<endl;
}

int main(){
    Node* root = NULL;
    TestInsert(root);
    TestSearch(root,1);
    TestSearch(root,2);
    TestSearch(root,3);
    TestSearch(root,4);
    TestSearch(root,5);
    TestSearch(root,6);
    TestSearch(root,7);
    TestSearch(root,8);
    TestMax(root);
    TestMin(root);

    // TestRemove(root, 8);
    // TestRemove(root, 7);
    // TestRemove(root, 6);
    // TestRemove(root, 2);
    // TestRemove(root, 5);
    // TestRemove(root, 4);
    // TestRemove(root, 3);
    // TestRemove(root, 1);
    TestRemove(root,1);
    TestRemove(root,2);
    TestRemove(root,3);
    TestRemove(root,4);
    TestRemove(root,5);
}

98. 验证二叉搜索树

700. 二叉搜索树中的搜索

701. 二叉搜索树中的插入操作

450. 删除二叉搜索树中的节点


2. AVL树(平衡二叉树)

BST树的缺点不平衡


二叉树的平衡.png

平衡二叉树/自平衡二叉查找树(Balanced Binary Tree): 也称为AVL树(名称来自发明人G.M. Adelson-Velsky 和 E.M. Landis的首字母),它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

特点

  1. 左右子树深度之差的绝对值不超过1(任意节点的两子树的高度最大差别为1).
  2. 左右子树仍然为平衡二叉树.
  • 平衡因子BF(Balance Factor):左树深度减去右树深度的值。平衡因子BF = 左子树深度-右子树深度。
  • 平衡二叉树每个结点的平衡因子只能是1,0,-1。若其绝对值超过1,则该二叉排序树就是不平衡的。当BF为-1、0、1时,二叉树平衡;反之,不平衡。

树快速查找的关键是高度要低,高度低的关键是平衡。

判断平衡:BF


判断平衡二叉树
/* 节点 */
struct Node{
    char key;       // 键值
    Node* right;    // 右节点
    Node* left;     // 左节点
    int height;     // 节点高度
    Node(char key):key(key),right(NULL),left(NULL),height(0){}
    Node(char key,int height):key(key),right(NULL),left(NULL),height(height){}
};

/*
* 平衡因子:左子树树高-右子树树高
*/
int BalanceFactor(const Node* root){
    if(NULL == root) return 0;
    if(NULL == root->left && NULL == root->right) return 0;
    if(NULL == root->left) return -root->right->height;
    if(NULL == root->right) return root->left->height;
    return root->left->height - root->right->height;
}

ostream& operator<<(ostream& os,const Node root) {
    queue<const Node*> q;
    q.push(&root);
    while(!q.empty()){
        const Node* curr = q.front();
        q.pop();
        if (NULL != curr){
            os << curr->key << "[" << BalanceFactor(curr)<<"]";
            q.push(curr->left);
            q.push(curr->right);
        } else {
            os << '#';
        }
    }
    return os;        
}
int main(){
    {
        //     1
        //    /
        //   2
        //  /
        // 3
        Node a('1',3);
        Node b('2',2);
        Node c('3',1);
        a.left = &b;
        b.left = &c;
        cout << a << endl;
    }
    {
        // 1
        //  \
        //   2
        //    \
        //     3
        Node a('1',3);
        Node b('2',2);
        Node c('3',1);
        a.right = &b;
        b.right = &c;
        cout << a << endl;
    }
    {
        //     1
        //    /
        //   2
        //    \
        //     3
        Node a('1', 3);
        Node b('2', 2);
        Node c('3', 1);
        a.left = &b;
        b.right = &c;
        cout << a << endl;
    }
    {
        // 1
        //  \
        //   2
        //  /
        // 3
        Node a('1', 3);
        Node b('2', 2);
        Node c('3', 1);
        a.right = &b;
        b.left = &c;
        cout << a << endl;
    }
}

LeetCode 110. 平衡二叉树

两种旋转(左旋/右旋)、三个节点(新插入节点/不平衡节点/旋转节点)、四种不平衡(LL/RR/RL/LR)。

操作

AVL树的操作基本和二叉查找树一样,这里我们关注的是两个变化很大的操作:插入和删除!

旋转

旋转条件:当最小不平衡子树根节点BF>1,右旋;BF<-1,左旋。


当P的A、B节点插入新结点,导致Q点不平衡,左重右轻,右旋。
当Q的B、C节点插入新结点,导致P点不平衡,右重左轻,左旋。
旋转点上升,不平衡点向轻的一侧旋转。

旋转步骤:

  1. 获取旋转节点
  2. 旋转节点的子节点替换父节点的旋转节点
  3. 旋转节点父节点做旋转节点子节点
  4. 返回旋转节点
依次插入1,2,3

依次插入3,2,1

依次插入2,1,3或2,3,1

依次插入1,3,2

依次插入3,1,2

依次插入4,5

依次插入6,7

旋转1→2→3和3→2→1的情况

#include <iostream>
#include <queue>
using namespace std;

/* 节点 */
struct Node{
    char key;       // 键值
    Node* right;    // 右节点
    Node* left;     // 左节点
    int height;     // 节点高度
    Node(char key):key(key),right(NULL),left(NULL),height(0){}
};

ostream& operator<<(ostream& os,const Node root) {
    queue<const Node*> q;
    q.push(&root);
    while(!q.empty()){
        const Node* curr = q.front();
        q.pop();
        if (NULL != curr){
            os << curr->key;
            q.push(curr->left);
            q.push(curr->right);
        } else {
            os << '#';
        }
    }
    return os;        
}

/*
 * 右旋
 *       Q                  P 
 *      / \                / \ 
 *     P   C  ------->    A   Q 
 *    / \                /   / \ 
 *   A   B              X'  B   C  
 *
 */
Node* RightRotate(Node* root){
    Node* pivot = root->left;  // 获取旋转节点
    root->left = pivot->right; // 旋转节点的子节点替换父节点的旋转节点
    pivot->right = root;       // 旋转节点父节点做旋转节点子节点
    return pivot;              // 返回旋转节点
}

/*
 * 左旋
 *      P                    Q
 *     / \                  / \ 
 *    A   Q     ----->     P   C 
 *       / \              / \   \ 
 *      B   C            A   B   X'  
 */
Node* LeftRotate(Node* root){
    Node* pivot = root->right; // 获取旋转节点
    root->right = pivot->left; // 旋转节点的子节点替换父节点的旋转节点
    pivot->left = root;        // 旋转节点父节点做旋转节点子节点
    return pivot;              // 返回旋转节点 
}

int main(){
    {
        Node a('1');
        Node b('2');
        Node c('3');
        a.left = &b;
        b.left = &c;
        cout << a << endl;
        cout << *RightRotate(&a) << endl;
    }
    {
        Node a('1');
        Node b('2');
        Node c('3');
        a.right = &b;
        b.right = &c;
        cout << a << endl;
        cout << *LeftRotate(&a) << endl;
    }
    {
        Node q('Q');
        Node p('P');
        Node c('C');
        Node a('A');
        Node b('B');
        q.left = &p;
        q.right = &c;
        p.left = &a;
        p.right = &b;
        cout << q << endl;
        Node* r = RightRotate(&q);
        cout << *r << endl;
        Node *l = LeftRotate(r);
        cout << *l << endl;
    }
}

四种不平衡

不平衡的四种情况:
LL:插入一个新节点到不平衡节点的左子树(Left)的左子树(Left),导致不平衡节点的平衡因子由1变为2

RR:插入一个新节点到不平衡节点的右子树(Right)的右子树(Right),导致不平衡节点的平衡因子由-1变为-2

LR:插入一个新节点到不平衡节点的左子树(Left)的右子树(Right),导致不平衡节点的平衡因子由1变为2

RL:插入一个新节点到不平衡节点的右子树(Right)的左子树(Left),导致不平衡节点的平衡因子由-1变为-2

判断四种不平衡
1→2→3
3→2→1
1→3→2
3→1→2
依次插入:3,4,5,1,2
依次插入:3,4,5,2,1
依次插入:3,4,5,6,7
依次插入:3,4,5,7,6
依次插入:7,4,1,3,2,8,9,5,6

/*
* 检查是否平衡
*/
void Check(Node* root){
    int nf = BalanceFactor(root);
    if(nf>1){
        int lf = BalanceFactor(root->left);
        if(lf > 0) { // LL
            cout << "LL" << endl;
        } else { // LR
            cout << "LR" << endl;
        }
    }else if(nf < -1){
        int rf = BalanceFactor(root->right);
        if (rf < 0) { // RR
            cout << "RR" << endl;
        } else { // RL
            cout << "RL" << endl;
        }
    }else{// 保持平衡的情况
        cout << "Balance" << endl;
    }
}
int main(){
    {
        //     1
        //    /
        //   2
        //  /
        // 3
        Node a('1',3);
        Node b('2',2);
        Node c('3',1);
        a.left = &b;
        b.left = &c;
        cout << a << endl;
        Check(&a);
    }

    {
        // 1
        //  \
        //   2
        //    \
        //     3
        Node a('1',3);
        Node b('2',2);
        Node c('3',1);
        a.right = &b;
        b.right = &c;
        cout << a << endl;
        Check(&a);
    }

    {
        //     1
        //    /
        //   2
        //    \
        //     3
        Node a('1', 3);
        Node b('2', 2);
        Node c('3', 1);
        a.left = &b;
        b.right = &c;
        cout << a << endl;
        Check(&a);
    }

    {
        // 1
        //  \
        //   2
        //  /
        // 3
        Node a('1', 3);
        Node b('2', 2);
        Node c('3', 1);
        a.right = &b;
        b.left = &c;
        cout << a << endl;
        Check(&a);
    }

    {
        //     1
        //    / \
        //   2   4
        //  / \
        // 3   5
        Node a('1', 3);
        Node b('2', 2);
        Node c('3', 1);
        Node d('4', 1);
        Node e('5', 1);
        a.left = &b;
        a.right = &d;
        b.left = &c;
        b.right = &e;
        cout << a << endl;
        Check(&a);
    }

    {
        //   1
        //  / \
        // 4   2
        //    / \
        //   5   3
        Node a('1', 3);
        Node b('2', 2);
        Node c('3', 1);
        Node d('4', 1);
        Node e('5', 1);
        a.right = &b;
        a.left = &d;
        b.right = &c;
        b.left = &e;
        cout << a << endl;
        Check(&a);
    }

    {
        //       1
        //      / \
        //     2   4
        //    / \
        //   3   5
        //  /
        // 6
        Node a('1', 4);
        Node b('2', 3);
        Node c('3', 2);
        Node d('4', 1);
        Node e('5', 1);
        Node f('6', 1);
        a.left = &b;
        a.right = &d;
        b.left = &c;
        b.right = &e;
        c.left = &f;
        cout << a << endl;
        Check(&a);
    }

    {
        //       1
        //      / \
        //     2   4
        //    / \
        //   3   5
        //    \
        //     6
        Node a('1', 4);
        Node b('2', 3);
        Node c('3', 2);
        Node d('4', 1);
        Node e('5', 1);
        Node f('6', 1);
        a.left = &b;
        a.right = &d;
        b.left = &c;
        b.right = &e;
        c.right = &f;
        cout << a << endl;
        Check(&a);
    }

    {
        //       1
        //      / \
        //     2   4
        //    / \
        //   3   5
        //      /
        //     6
        Node a('1', 4);
        Node b('2', 3);
        Node c('3', 1);
        Node d('4', 1);
        Node e('5', 2);
        Node f('6', 1);
        a.left = &b;
        a.right = &d;
        b.left = &c;
        b.right = &e;
        e.left = &f;
        cout << a << endl;
        Check(&a);
    }

    {
        //       1
        //      / \
        //     2   4
        //    / \
        //   3   5
        //        \
        //         6
        Node a('1', 4);
        Node b('2', 3);
        Node c('3', 1);
        Node d('4', 1);
        Node e('5', 2);
        Node f('6', 1);
        a.left = &b;
        a.right = &d;
        b.left = &c;
        b.right = &e;
        e.right = &f;
        cout << a << endl;
        Check(&a);
    }

    {
        //   1
        //  / \
        // 4   2
        //    / \
        //   5   3
        //  /
        // 6
        Node a('1', 3);
        Node b('2', 2);
        Node c('3', 1);
        Node d('4', 1);
        Node e('5', 1);
        Node f('6', 1);
        a.right = &b;
        a.left = &d;
        b.right = &c;
        b.left = &e;
        e.left = &f;
        cout << a << endl;
        Check(&a);
    }
}

不平衡处理方法

  • 单向右旋平衡处理LL
  • 单向左旋平衡处理RR
  • 双向旋转(先左后右)平衡处理LR
  • 双向旋转(先右后左)平衡处理RL
image.png
/*
* 平衡
*/
Node* Balance(Node* root){
    Node *res = root;
    int nf = BalanceFactor(root);
    if(nf>1){
        int lf = BalanceFactor(root->left);
        if(lf > 0) { // LL
            cout << "LL" << endl;
            res = RightRotate(root);
        } else { // LR
            cout << "LR" << endl;
            root->left = LeftRotate(root->left);
            res = RightRotate(root);
        }
    }else if(nf <-1){
        int rf = BalanceFactor(root->right);
        if (rf < 0) { // RR
            cout << "RR" << endl;
            res = LeftRotate(root);
        } else { // RL
            cout << "RL" << endl;
            root->right = RightRotate(root->right);
            res = LeftRotate(root);
        }
    }else{
        cout << "Balance" << endl;
        // 保持平衡的情况
    }
    return res;
}
int main(){
    {
        //     1
        //    /
        //   2
        //  /
        // 3
        Node a('1',3);
        Node b('2',2);
        Node c('3',1);
        a.left = &b;
        b.left = &c;
        cout << a << endl;
        Check(&a);
        cout << *Balance(&a) << endl;
        cout << "----------------" << endl;
    }

    {
        // 1
        //  \
        //   2
        //    \
        //     3
        Node a('1',3);
        Node b('2',2);
        Node c('3',1);
        a.right = &b;
        b.right = &c;
        cout << a << endl;
        Check(&a);
        cout << *Balance(&a) << endl;
        cout << "----------------" << endl;
    }

    {
        //     1
        //    /
        //   2
        //    \
        //     3
        Node a('1', 3);
        Node b('2', 2);
        Node c('3', 1);
        a.left = &b;
        b.right = &c;
        cout << a << endl;
        Check(&a);
        cout << *Balance(&a) << endl;
        cout << "----------------" << endl;
    }

    {
        // 1
        //  \
        //   2
        //  /
        // 3
        Node a('1', 3);
        Node b('2', 2);
        Node c('3', 1);
        a.right = &b;
        b.left = &c;
        cout << a << endl;
        Check(&a);
        cout << *Balance(&a) << endl;
        cout << "----------------" << endl;
    }

    {
        //     1
        //    / \
        //   2   4
        //  / \
        // 3   5
        Node a('1', 3);
        Node b('2', 2);
        Node c('3', 1);
        Node d('4', 1);
        Node e('5', 1);
        a.left = &b;
        a.right = &d;
        b.left = &c;
        b.right = &e;
        cout << a << endl;
        Check(&a);
        cout << *Balance(&a) << endl;
        cout << "----------------" << endl;
    }

    {
        //   1
        //  / \
        // 4   2
        //    / \
        //   5   3
        Node a('1', 3);
        Node b('2', 2);
        Node c('3', 1);
        Node d('4', 1);
        Node e('5', 1);
        a.right = &b;
        a.left = &d;
        b.right = &c;
        b.left = &e;
        cout << a << endl;
        Check(&a);
        cout << *Balance(&a) << endl;
        cout << "----------------" << endl;
    }

    {
        //       1
        //      / \
        //     2   4
        //    / \
        //   3   5
        //  /
        // 6
        Node a('1', 4);
        Node b('2', 3);
        Node c('3', 2);
        Node d('4', 1);
        Node e('5', 1);
        Node f('6', 1);
        a.left = &b;
        a.right = &d;
        b.left = &c;
        b.right = &e;
        c.left = &f;
        cout << a << endl;
        Check(&a);
        cout << *Balance(&a) << endl;
        cout << "----------------" << endl;
    }

    {
        //       1
        //      / \
        //     2   4
        //    / \
        //   3   5
        //    \
        //     6
        Node a('1', 4);
        Node b('2', 3);
        Node c('3', 2);
        Node d('4', 1);
        Node e('5', 1);
        Node f('6', 1);
        a.left = &b;
        a.right = &d;
        b.left = &c;
        b.right = &e;
        c.right = &f;
        cout << a << endl;
        Check(&a);
        cout << *Balance(&a) << endl;
        cout << "----------------" << endl;
    }

    {
        //       1
        //      / \
        //     2   4
        //    / \
        //   3   5
        //      /
        //     6
        Node a('1', 4);
        Node b('2', 3);
        Node c('3', 1);
        Node d('4', 1);
        Node e('5', 2);
        Node f('6', 1);
        a.left = &b;
        a.right = &d;
        b.left = &c;
        b.right = &e;
        e.left = &f;
        cout << a << endl;
        Check(&a);
        cout << *Balance(&a) << endl;
        cout << "----------------" << endl;
    }

    {
        //       1
        //      / \
        //     2   4
        //    / \
        //   3   5
        //        \
        //         6
        Node a('1', 4);
        Node b('2', 3);
        Node c('3', 1);
        Node d('4', 1);
        Node e('5', 2);
        Node f('6', 1);
        a.left = &b;
        a.right = &d;
        b.left = &c;
        b.right = &e;
        e.right = &f;
        cout << a << endl;
        Check(&a);
        cout << *Balance(&a) << endl;
        cout << "----------------" << endl;
    }
}

更新树高

/*
* 更新树高
*/
int UpdateHeight(Node* root){
    if(NULL == root) return 0;
    if(NULL == root->left && NULL == root->right) {
        root->height = 1;
    } else if(NULL == root->left) {
        root->height = 1 + UpdateHeight(root->right);
    } else if(NULL == root->right) {
        root->height = 1 + UpdateHeight(root->left);
    } else {
        root->height = 1 + max(UpdateHeight(root->left),UpdateHeight(root->right));
    }
    return root->height;
}

插入

与 BST(二叉搜索树)中类似,先进行一次失败的查找来确定插入的位置,插入节点后根据平衡因子来决定是否需要调整。

二叉树的平衡1-右旋.png

二叉树的平衡2-左旋.png

二叉树的平衡3-左旋.png

二叉树的平衡4-左旋.png

二叉树的平衡5-右旋左旋.png

二叉树的平衡6-右旋左旋.png
/*
* 插入AVL树
*/
bool Insert(Node*& root,char key){
    bool res;
    if (NULL == root){
        root = new Node(key);
        res = true;
    } else if (root->key == key){
        res = false;
    } else if (root->key < key){
        res = Insert(root->right, key);
    } else {
        res = Insert(root->left, key);
    }
    if(res){
        root = Balance(root);
        UpdateHeight(root); // 更新高度
    }
    return res;
}

删除

删除情况分析





删除节点有三种情况

  1. 删除叶子节点。
  2. 删除只有一棵子树的节点。
  3. 删除有两棵子树的节点。

AVL删除与BST删除方式一致,但是AVL删除会导致树高以及平衡因子变化,这时需要沿着被删除结点到根的路径来调整这种变化。

复制删除.png
合并删除.png
合并删除2.png

优点

查找、插入和删除在平均和最坏情况下都是O(logn)。

缺点

插入和删除旋转比较耗时,适用于插入和删除较少的情况。

参考代码

#include <iostream>
#include <queue>
using namespace std;

struct Node{
    int key;        ///< 键
    Node *left;     ///< 左子节点
    Node *right;    ///< 右子节点
    int height;     ///< 节点高度
    Node(int key, int height) 
    :key(key),left(NULL),right(NULL),height(height){}
};

// 辅助操作
Node* Maximum(Node* node){
    if (NULL == node) return NULL;
    while (NULL != node->right) node = node->right;
    return node;
}

Node* Minimum(Node* node){
    if (NULL == node) return NULL;
    while (NULL != node->left) node = node->left;
    return node;
}

// 平衡因子:左子树树高-右子树树高
int BalanceFactor(const Node* root){
    if(NULL == root) return 0;
    if(NULL == root->left && NULL == root->right) return 0;
    if(NULL == root->left) return -root->right->height;
    if(NULL == root->right) return root->left->height;
    return root->left->height - root->right->height;
}

/*
 * 右旋
 *       Q                  P 
 *      / \                / \ 
 *     P   C  ------->    A   Q 
 *    / \                /   / \ 
 *   A   B              X'  B   C  
 *
 */
Node* RightRotate(Node* root){
    Node* pivot = root->left;  // 获取旋转节点
    root->left = pivot->right; // 旋转节点的子节点替换父节点的旋转节点
    pivot->right = root;       // 旋转节点父节点做旋转节点子节点
    return pivot;              // 返回旋转节点
}

/*
 * 左旋
 *      P                    Q
 *     / \                  / \ 
 *    A   Q     ----->     P   C 
 *       / \              / \   \ 
 *      B   C            A   B   X'  
 */
Node* LeftRotate(Node* root){
    Node* pivot = root->right; // 获取旋转节点
    root->right = pivot->left; // 旋转节点的子节点替换父节点的旋转节点
    pivot->left = root;        // 旋转节点父节点做旋转节点子节点
    return pivot;              // 返回旋转节点 
}

// 更新树高
int UpdateHeight(Node* root){
    if(NULL == root) return 0;
    if(NULL == root->left && NULL == root->right) {
        root->height = 1;
    } else if(NULL == root->left) {
        root->height = 1 + UpdateHeight(root->right);
    } else if(NULL == root->right) {
        root->height = 1 + UpdateHeight(root->left);
    } else {
        root->height = 1 + max(UpdateHeight(root->left),UpdateHeight(root->right));
    }
    return root->height;
}

// 平衡
Node* Balance(Node* root){
    Node *res = root;
    int nf = BalanceFactor(root);
    if(nf>1){
        int lf = BalanceFactor(root->left);
        if(lf > 0) { // LL
            cout << "LL" << endl;
            res = RightRotate(root);
        } else { // LR
            cout << "LR" << endl;
            root->left = LeftRotate(root->left);
            res = RightRotate(root);
        }
    }else if(nf <-1){
        int rf = BalanceFactor(root->right);
        if (rf < 0) { // RR
            cout << "RR" << endl;
            res = LeftRotate(root);
        } else { // RL
            cout << "RL" << endl;
            root->right = RightRotate(root->right);
            res = LeftRotate(root);
        }
    }else{
        cout << "Balance" << endl;
        // 保持平衡的情况
        // return res;
    }
    UpdateHeight(res);
    return res;
}

// 三个核心操作
Node* Insert(Node* node, int key){
    if(NULL == node) node = new Node(key,1);
    if (key < node->key) node->left = Insert(node->left, key);
    else if (key > node->key) node->right = Insert(node->right,key);
    node = Balance(node); // 平衡
    return node;
}

Node* Search(Node* node, int key){
    if(NULL == node) return NULL;
    if (key < node->key) return Search(node->left, key);
    else if (key > node->key) return Search(node->right, key);
    return node;
}

Node* Remove(Node* node, int key){
    if(NULL == node) return NULL;
    if (key < node->key) node->left = Remove(node->left, key);
    else if (key > node->key) node->right = Remove(node->right, key);
    else {
        if(NULL != node->right && NULL != node->left){// 有两个子树
            // 最小值删除
            Node* p = Minimum(node->right);
            node->key = p->key;
            node->right = Remove(node->right,p->key);
        } else {// 叶子节点或者只有一个子树
            Node* res = NULL;
            if(NULL != node->right) res = node->right;
            if(NULL != node->left) res = node->left;
            delete node;
            node = NULL;
            return res;
        }
    }
    node = Balance(node); // 平衡
    return node;
}
/////////////////////////////////////////////////////////////
ostream& operator<<(ostream& os,const Node root) {
    queue<const Node*> q;
    q.push(&root);
    while(!q.empty()){
        const Node* curr = q.front();
        q.pop();
        if (NULL != curr){
            os << curr->key << '[' << curr->height <<']';
            q.push(curr->left);
            q.push(curr->right);
        } else {
            os << '#';
        }
    }
    return os;        
}
void TestInsert(Node*& root){
    root = Insert(root,1);
    root = Insert(root,2);
    root = Insert(root,3);
    root = Insert(root,4);
    root = Insert(root,5);
    cout << *root << endl;
}
void TestSearch(Node* root,int key){
    cout <<"Search " << key << ":" << Search(root,key) << endl;
}
void TestRemove(Node*& root,int key){
    root = Remove(root, key);
    cout <<"Remove " << key << ":" ;
    if(NULL != root) cout << *root << endl;
    else cout << "#" << endl;
}
void TestMax(Node* root){
    Node* maxNode = Maximum(root);
    cout << "Max:"<< maxNode->key <<":"<< maxNode <<endl;
}
void TestMin(Node* root){
    Node* minNode = Minimum(root);
    cout << "Min:"<< minNode->key <<":"<< minNode <<endl;
}

int main(){
    Node* root = NULL;
    TestInsert(root);
    TestSearch(root,1);
    TestSearch(root,2);
    TestSearch(root,3);
    TestSearch(root,4);
    TestSearch(root,5);
    TestSearch(root,6);
    TestSearch(root,7);
    TestSearch(root,8);
    TestMax(root);
    TestMin(root);

    // TestRemove(root, 8);
    // TestRemove(root, 7);
    // TestRemove(root, 6);
    TestRemove(root, 2);
    TestRemove(root, 5);
    TestRemove(root, 4);
    TestRemove(root, 3);
    TestRemove(root, 1);
    // TestRemove(root,1);
    // TestRemove(root,2);
    // TestRemove(root,3);
    // TestRemove(root,4);
    // TestRemove(root,5);
}

BST与AVL比较

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容