高度平衡二叉搜索树(AVLTree)

目录

  • AVL树的概念
  • AVL树节点的定义
  • AVL树的插入
  • AVL树的旋转
    • 右单旋
    • 左单旋
    • 左右双旋
    • 右左双旋
  • AVL树的验证
  • 代码实现

AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

它的左右子树都是AVL树

左右子树高度之差(简称平衡因子)的绝对值不超过1,平衡因子的求法=右子树高度-左子树的高度

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 O(log2N),搜索时间复杂度O( log2N)

AVL树节点的定义

AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入过程可以分为两步:

1. 按照二叉搜索树的方式插入新节点

2. 调整节点的平衡因子

pair<Node*, bool> Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return make_pair(_root, true);}// 找到存储位置,把数据插入进去Node* parent = _root, *cur = _root;while (cur){if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{return make_pair(cur, true);}}cur = new Node(kv);Node* newnode = cur;if (parent->_kv.first < kv.first){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}// 控制平衡// 1、更新平衡因子// 2、如果出现不平衡,则需要旋转//while (parent)while (cur != _root){if (parent->_left == cur){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0){break;}else if (parent->_bf == 1 || parent->_bf == -1){// parent所在的子树高度变了,会影响parent->parent// 继续往上更新cur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//parent所在子树已经不平衡,需要旋转处理一下if (parent->_bf  == -2){if (cur->_bf == -1){// 右单旋RotateR(parent);}else // cur->_bf == 1{RotateLR(parent);}}else // parent->_bf  == 2{if (cur->_bf == 1){// 左单旋RotateL(parent);}else // cur->_bf == -1{RotateRL(parent);}}break;}else{// 插入节点之前,树已经不平衡了,或者bf出错。需要检查其他逻辑assert(false);}}return make_pair(newnode, true);}

AVL树的旋转

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:

1. 新节点插入较高左子树的左侧—左左:右单旋

2. 新节点插入较高右子树的右侧—右右:左单旋

3. 新节点插入较高左子树的右侧—左右:先左单旋再右单旋

4. 新节点插入较高右子树的左侧—右左:先右单旋再左单旋

右单旋

void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* parentParent = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (parentParent->_left == parent)parentParent->_left = subL;elseparentParent->_right = subL;subL->_parent = parentParent;}subL->_bf = parent->_bf = 0;}

左单旋

void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* parentParent = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (parentParent->_left == parent)parentParent->_left = subL;elseparentParent->_right = subL;subL->_parent = parentParent;}subL->_bf = parent->_bf = 0;}

左右双旋

void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);// ...平衡因子调节还需要具体分析if (bf == -1){subL->_bf = 0;parent->_bf = 1;subLR->_bf = 0;}else if (bf == 1){parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else if (bf == 0){parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else{assert(false);}}

右左双旋

void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);// 平衡因子更新if (bf == 1){subR->_bf = 0;parent->_bf = -1;subRL->_bf = 0;}else if (bf == -1){parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else if (bf == 0){parent->_bf = 0;subR->_bf = 0;subRL->_bf = 0;}else{assert(false);}}

AVL树的验证

AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:

  1. 验证其为二叉搜索树
    如果中序遍历可得到一个有序的序列,就说明为二叉搜索树

  2. 验证其为平衡树

每个节点子树高度差的绝对值不超过1

void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":"<<root->_kv.second<<endl;_InOrder(root->_right);}void InOrder(){_InOrder(_root);}int _Height(Node* root){if (root == nullptr){return 0;}int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return rightHeight > leftHeight ? rightHeight + 1 : leftHeight + 1;}bool _IsBalance(Node* root){if (root == nullptr){return true;}int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);// 检查一下平衡因子是否正确if (rightHeight - leftHeight != root->_bf){cout << "平衡因子异常:"<<root->_kv.first<<endl;return false;}return abs(rightHeight - leftHeight) < 2&& _IsBalance(root->_left)&& _IsBalance(root->_right);}bool IsAVLTree(){return _IsBalance(_root);}

代码实现

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;template<class K, class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;// 右子树的高度-左子树的高度int _bf; // 平衡因子  balance factorpair<K, V> _kv;AVLTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _bf(0), _kv(kv){}
};template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:AVLTree():_root(nullptr){}// 拷贝构造和赋值需要实现深拷贝void _Destory(Node* root){if (root == nullptr){return;}_Destory(root->_left);_Destory(root->_right);delete root;}~AVLTree(){_Destory(_root);_root = nullptr;}V& operator[](const K& key){pair<Node*, bool> ret = Insert(make_pair(key, V()));return ret.first->_kv.second;}pair<Node*, bool> Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return make_pair(_root, true);}// 找到存储位置,把数据插入进去Node* parent = _root, *cur = _root;while (cur){if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{return make_pair(cur, true);}}cur = new Node(kv);Node* newnode = cur;if (parent->_kv.first < kv.first){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}// 控制平衡// 1、更新平衡因子// 2、如果出现不平衡,则需要旋转//while (parent)while (cur != _root){if (parent->_left == cur){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0){break;}else if (parent->_bf == 1 || parent->_bf == -1){// parent所在的子树高度变了,会影响parent->parent// 继续往上更新cur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//parent所在子树已经不平衡,需要旋转处理一下if (parent->_bf  == -2){if (cur->_bf == -1){// 右单旋RotateR(parent);}else // cur->_bf == 1{RotateLR(parent);}}else // parent->_bf  == 2{if (cur->_bf == 1){// 左单旋RotateL(parent);}else // cur->_bf == -1{RotateRL(parent);}}break;}else{// 插入节点之前,树已经不平衡了,或者bf出错。需要检查其他逻辑assert(false);}}return make_pair(newnode, true);}void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);// ...平衡因子调节还需要具体分析if (bf == -1){subL->_bf = 0;parent->_bf = 1;subLR->_bf = 0;}else if (bf == 1){parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else if (bf == 0){parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else{assert(false);}}void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);// 平衡因子更新if (bf == 1){subR->_bf = 0;parent->_bf = -1;subRL->_bf = 0;}else if (bf == -1){parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else if (bf == 0){parent->_bf = 0;subR->_bf = 0;subRL->_bf = 0;}else{assert(false);}}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL){subRL->_parent = parent;}subR->_left = parent;Node* parentParent = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}parent->_bf = subR->_bf = 0;}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* parentParent = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (parentParent->_left == parent)parentParent->_left = subL;elseparentParent->_right = subL;subL->_parent = parentParent;}subL->_bf = parent->_bf = 0;}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_kv.first <  key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}return nullptr;}// 1、工作中会用的(AVL树不会自己写,这里通过插入深入理解一下他的性质就够了)// 2、校招会考的(基本不会问删除的细节)// 有兴趣的可以下去实现一下。bool Erase(const K& key){return false;}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":"<<root->_kv.second<<endl;_InOrder(root->_right);}void InOrder(){_InOrder(_root);}int _Height(Node* root){if (root == nullptr){return 0;}int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return rightHeight > leftHeight ? rightHeight + 1 : leftHeight + 1;}bool _IsBalance(Node* root){if (root == nullptr){return true;}int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);// 检查一下平衡因子是否正确if (rightHeight - leftHeight != root->_bf){cout << "平衡因子异常:"<<root->_kv.first<<endl;return false;}return abs(rightHeight - leftHeight) < 2&& _IsBalance(root->_left)&& _IsBalance(root->_right);}bool IsAVLTree(){return _IsBalance(_root);}
private:Node* _root;
};

高度平衡二叉搜索树(AVLTree)相关推荐

  1. 每天一道LeetCode-----将有序序列转成高度平衡二叉搜索树

    Convert Sorted Array to Binary Search Tree 原题链接Convert Sorted Array to Binary Search Tree 给定一个地增序列,要 ...

  2. 《恋上数据结构第1季》平衡二叉搜索树、AVL树

    AVL树 二叉搜索树缺点分析 改进二叉搜索树 平衡(Balance) 理想平衡 如何改进二叉搜索树? 平衡二叉搜索树(Balanced Binary Search Tree) AVL树 BST 对比 ...

  3. 数据结构与算法-平衡二叉搜索树

    平衡二叉搜索树 1.自平衡的二叉搜索树 2.平衡 (1)空树平衡 (2)非空树平衡 左右子树平衡 左右子树高度差绝对值 <= 1 3.平衡因子 左右子树的高度差的衡量值 -1 0 1 (一)平衡 ...

  4. VC++2012编程演练数据结构《9》平衡二叉搜索树

    平衡二叉搜索树 任何结点的左子树和右子树高度最多相差1的二叉搜索树. (1)AVL树的插入算法 a. 插入结点之后仍然是AVL树,则不调整: b. 插入结点之后不再满足AVL树条件,则进行调整,根据导 ...

  5. 二叉搜索树、平衡二叉搜索树和红黑树

    文章目录 一. 二叉搜索树(Binary Sort Tree) 二. 二叉平衡搜索树(AVL) 三. 红黑树 一. 二叉搜索树(Binary Sort Tree) 二叉搜索树,又称为二叉排序树(二叉查 ...

  6. 五.树,二叉树,二叉搜索树(BST)和自平衡二叉搜索树(AVL)

    1.树 树是一种数据结构 比如:目录结构 树是一种可以递归定义的数据结构 树是由n个节点组成的集合: 如果 n=0, 那这是一颗空树 如果 n>0, 那存在1个节点作为树的根节点,其他节点可以分 ...

  7. 看动画学算法之:平衡二叉搜索树AVL Tree

    简介 平衡二叉搜索树是一种特殊的二叉搜索树.为什么会有平衡二叉搜索树呢? 考虑一下二叉搜索树的特殊情况,如果一个二叉搜索树所有的节点都是右节点,那么这个二叉搜索树将会退化成为链表.从而导致搜索的时间复 ...

  8. 牛客题霸 [将升序数组转化为平衡二叉搜索树]C++题解/答案

    牛客题霸 [将升序数组转化为平衡二叉搜索树]C++题解/答案 题目描述 给出一个升序排序的数组,将其转化为平衡二叉搜索树(BST). 题解: 二叉搜索树的定义: 二叉搜索树或者是一棵空树,或者是具有下 ...

  9. 红黑树 平衡二叉搜索树_红黑树:自我平衡的二叉搜索树,并举例说明

    红黑树 平衡二叉搜索树 什么是红黑树? (What is a Red-Black Tree?) Red-Black Tree is a type of self-balancing Binary Se ...

最新文章

  1. 编写一个程序,实现将c语言源程序中的注释全部删除
  2. 团队作业1(陈爽、夏江华、李瑞超、甘彩娈、吕乐乐)
  3. mysql 值为0 但却被认为null_MySQL介于普通读和锁定读的加锁方式
  4. 代码抽象_如何通过抽象使代码更具可读性
  5. 同步服务器客户端位置,服务器和客户端信息同步方式
  6. Android 教你打造炫酷的ViewPagerIndicator
  7. 2012年参加油田象棋比赛的几盘棋
  8. html网页设计语言基础教程,HTML 网页设计新手入门教程(共32课时)_IT教程网
  9. 期货交易常用术语英语词汇
  10. 锦天科技被盛大收购 23岁创始人成亿万富翁
  11. android 看电脑视频,超级看电脑在线观看方法教程详解_丝瓜视频安卓版
  12. Scrapy框架学习 - 爬取豆瓣电影排行榜TOP250所有电影信息并保存到MongoDB数据库中
  13. 解读6大常见肿瘤的消融选择
  14. 想象力的再突破!无人机后还有大招?!
  15. Oracle精髓(第4版)
  16. 为什么现在还有985高校给大一上C语言课?
  17. Spring Boot2 redis
  18. Android菜鸟的成长笔记(3)——给QQ登录界面说So Easy
  19. numpy.corrcoef()函数讲解
  20. Keil5可以打开Keil4

热门文章

  1. Redis的高可用性
  2. 成本控制在项目管理中的重要性
  3. 开源项目学习方法ABC
  4. Spring boot 保姆级教程,包学包会,授之以渔
  5. SAP低值易耗品做固定资产处理的折旧测试
  6. MoveWindow
  7. 怎么用Python写出随时间变化的字_Python爬虫实战,AcFun弹幕视频网 - 长情且温柔
  8. N1变砖有救N1 eMMC 镜像,理论用于TTL救砖 ,使用ddbr恢复官改系统救砖方法
  9. 高通退出,ARM进军服务器市场还看中国
  10. mysql数据类型介绍(含text,longtext,mediumtext说明)