AVL树理解
AVL树理解
简介
我们知道,AVL树也是平衡树中的一种,是自带平衡条件的二叉树,始终都在维护树的高度,保持着树的高度为logN,同时把插入、查找、删除一个结点的时间复杂度的最好和最坏情况都维持在O(logN);增加和删除需要通过一次或者多次的树旋转来重新平衡这棵树。
其中规定每个结点的左子树和右子树的高度最多差1,也就是说任何结点的两个子树的最大高度为1。
性质
平衡树最重要的地方在于旋转,其中分为单旋转和双旋转,其中单旋转和双旋转又各分为两种子形式,分别为左单旋转,右单旋转,左右双旋转,右左双旋转;在左左和右右这样的单旋转的情况下,只需要进行一次旋转操作,但是在左右或者右左这样的双旋转的情况下,则需要进行二次旋转操作。
如图:
- 1,4为单旋转,1为左左,4为右右
- 2,3为双旋转,2为左右,3为右左
再看下面一张图:
- 左右(父亲结点在左边,子结点在右边):子结点先要经过一次左旋,变成左左的情况,再对父亲结点进行一次右旋,即可达到平衡;
- 右左(父亲结点在右边,子结点在左边):同理,先对子结点进行一次右旋,变成右右的情况,再对父亲结点进行一次左旋,即可达到平衡。
单旋转
这是一个左左的情况,因为是对称的,所以两者可以通过左右旋转相互到达;
可以发现的是,k2为失去平衡的树的根结点,k1为旋转后重新平衡的树的根结点,k1结点的右结点经过旋转连接到了k2的左结点上,此时,k2的左结点连接到k1的右结点,而k2则连到k1的右结点上面;
双旋转
上面是左右的情况,但是右左的情况虽然不是对称的, 但是情况是类似的;
我们先看子结点的部分,也就是以k1为父结点的部分,此时k1为失去平衡的树的根结点,k2为进行旋转以后平衡树的根结点,经过左旋转以后变成了以k2为父结点,k1为左子结点的部分,此时树变成了k3为失去平衡的树根结点,k2变成经过旋转以后平衡树的根结点,所以在旋转时,可以把k1看作是一个整体,把k2的右子结点连接到k3的左子结点上面,再将k3当作一个整体连接到k2的右子结点上面。
实现
TreeNode.hpp
//
// TreeNode.hpp
// AVL
//
// Created by George on 16/10/7.
// Copyright © 2016年 George. All rights reserved.
//
#ifndef TreeNode_hpp
#define TreeNode_hpp
namespace AVL {
template<class T>
class TreeNode {
public:
TreeNode(T value, int height = 0) : value(value), height(height), left(nullptr), right(nullptr){};
T value;
int height;
TreeNode* left;
TreeNode* right;
};
template<class T>
class AVLTree {
public:
AVLTree() = default;
AVLTree(TreeNode<T>* ptr) : _root(ptr){};
~AVLTree();
void Insert(T value);
TreeNode<T>* find(T value);
void Delete(T value);
void Traversal();
private:
TreeNode<T> * _root;
void insertNode(TreeNode<T>* &root, T value);
TreeNode<T>* find(TreeNode<T>* root, T value);
void deleteNode(TreeNode<T>* &root, T value);
void traversal(TreeNode<T>* root);
int height(TreeNode<T>* node) const;
void singleRotateWithLeftChild(TreeNode<T>* &node);
void singleRotateWithRightChild(TreeNode<T>* &node);
void doubleRotateWithLeftRightChild(TreeNode<T>* &node);
void doubleRotateWithRightLeftChild(TreeNode<T>* &node);
};
void insertTest();
void deleteTest();
void searchTest();
void traversalTest();
}
#endif /* TreeNode_hpp */
TreeNode.cpp
//
// TreeNode.cpp
// AVL
//
// Created by George on 16/10/7.
// Copyright © 2016年 George. All rights reserved.
//
#include "TreeNode.hpp"
#include <string>
#include <iostream>
namespace AVL {
template<class T>
void LogNode(TreeNode<T> *node) {
std::cout << "value :" << node->value << ", height :" << node->height << std::endl;
}
std::string GetInputString(const std::string content) {
std::cout << content << " :";
std::string input;
std::cin >> input;
return input;
}
/*------------------------------AVLTree-------------------------------*/
template<class T>
int AVLTree<T>::height(TreeNode<T> *node) const {
return node == nullptr ? -1 : node->height;
}
template<class T>
void AVLTree<T>::singleRotateWithLeftChild(TreeNode<T>* &node) {
TreeNode<T>* lNode = node->left;
node->left = lNode->right;
lNode->right = node;
node->height = std::max(height(node->left), height(node->right)) + 1;
lNode->height = std::max(height(lNode->left), height(lNode->right)) + 1;
node = lNode;
}
template<class T>
void AVLTree<T>::singleRotateWithRightChild(TreeNode<T>* &node) {
TreeNode<T>* rNode = node->right;
node->right = rNode->left;
rNode->left = node;
node->height = std::max(height(node->left), height(node->right)) + 1;
rNode->height = std::max(height(rNode->left), height(rNode->right)) + 1;
node = rNode;
}
template<class T>
void AVLTree<T>::doubleRotateWithLeftRightChild(TreeNode<T>* &node) {
singleRotateWithRightChild(node->left);
singleRotateWithLeftChild(node);
}
template<class T>
void AVLTree<T>::doubleRotateWithRightLeftChild(TreeNode<T>* &node) {
singleRotateWithLeftChild(node->right);
singleRotateWithRightChild(node);
}
template<class T>
void AVLTree<T>::insertNode(TreeNode<T>* &root, T value) {
if (!root)
root = new TreeNode<T>(value);
if (value < root->value) {
insertNode(root->left, value);
if (height(root->left) - height(root->right) == 2) {
if (value < root->left->value)
singleRotateWithLeftChild(root);
else
doubleRotateWithLeftRightChild(root);
}
}
else if (value > root->value) {
insertNode(root->right, value);
if (height(root->right) - height(root->left) == 2) {
if (value > root->right->value)
singleRotateWithRightChild(root);
else
doubleRotateWithRightLeftChild(root);
}
}
root->height = std::max(height(root->left), height(root->right)) + 1;
}
template<class T>
TreeNode<T>* AVLTree<T>::find(TreeNode<T>* root, T value) {
if (root == nullptr)
return nullptr;
if (value < root->value)
return find(root->left, value);
else if (value > root->value)
return find(root->right, value);
else
return root;
}
template<class T>
void AVLTree<T>::deleteNode(TreeNode<T> *&root, T value) {
if (root == nullptr) return ;
if (value < root->value) {
deleteNode(root->left, value);
if (height(root->right) - height(root->left) == 2) {
// 判断是否存在右边子结点的左子结点
if (root->right->left && height(root->right->left) > height(root->right->right))
doubleRotateWithRightLeftChild(root);
else
singleRotateWithRightChild(root);
}
}
else if (value > root->value) {
deleteNode(root->right, value);
if (height(root->left) - height(root->right) == 2) {
// 判断是否存在左边子结点的右子结点
if (root->left->right && height(root->left->right) > height(root->left->left))
doubleRotateWithLeftRightChild(root);
else
singleRotateWithLeftChild(root);
}
}
else {
// 存在两个子结点
if (root->left && root->right) {
TreeNode<T>* ptr = root->right;
// 找到右子树中最小的结点
while (ptr->left != nullptr) {
ptr = ptr->left;
}
// 替换值
root->value = ptr->value;
// 删除右子树中最小值的结点
deleteNode(root->right, ptr->value);
if (height(root->left) - height(root->right) == 2) {
if (root->left->right && height(root->left->right) > height(root->left->left))
doubleRotateWithLeftRightChild(root);
else
singleRotateWithLeftChild(root);
}
}
else { // 存在一个结点或没有结点
TreeNode<T>* ptr = root;
if (root->left == nullptr)
root = root->right;
else if (root->right == nullptr)
root = root->left;
delete ptr;
ptr = nullptr;
}
}
if (root) {
root->height = std::max(height(root->left), height(root->right)) + 1;
}
}
template<class T>
void AVLTree<T>::traversal(TreeNode<T>* root) {
if (!root) return;
traversal(root->left);
LogNode(root);
traversal(root->right);
}
template<class T>
void AVLTree<T>::Insert(T value) {
insertNode(_root, value);
}
template<class T>
TreeNode<T>* AVLTree<T>::find(T value) {
return find(_root, value);
}
template<class T>
void AVLTree<T>::Delete(T value) {
deleteNode(_root, value);
}
template<class T>
void AVLTree<T>::Traversal() {
traversal(_root);
}
/*------------------------------Test-------------------------------*/
static AVLTree<int>* tree = new AVLTree<int>();
void insertTest() {
for (int i = 1; i <= 10 ; i++) {
tree->Insert(i);
}
}
void deleteTest() {
std::string input = GetInputString("Please input the delete value");
int value = atoi(input.c_str());
tree->Delete(value);
}
void searchTest() {
std::string input = GetInputString("Please input the search value");
int value = atoi(input.c_str());
TreeNode<int>* node = tree->find(value);
LogNode(node);
}
void traversalTest() {
tree->Traversal();
}
}
main.cpp
//
// main.cpp
// AVL
//
// Created by George on 16/10/7.
// Copyright © 2016年 George. All rights reserved.
//
#include <iostream>
#include "TreeNode.hpp"
int main(int argc, const char * argv[]) {
// insert code here...
AVL::insertTest();
AVL::traversalTest();
AVL::deleteTest();
AVL::traversalTest();
AVL::searchTest();
return 0;
}
运行结果
应用
因为旋转消耗的时间较多,所以适用于插入删除次数较少的,比如windows对进程地址空间的管理就用到了AVL。
AVL树理解的更多相关文章
- AVL树的左旋右旋理解 (转)
AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增加和删除可能需要通过一次或多 ...
- AVL树的理解及自写AVL树
AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增加和删除可能需要通过一次或多 ...
- 以AVL树为例理解二叉树的旋转(Rotate)操作
树旋转是在二叉树中的一种子树调整操作, 每一次旋转并不影响对该二叉树进行中序遍历的结果. 树旋转通常应用于需要调整树的局部平衡性的场合. 树旋转包括两个不同的方式, 分别是左旋转和右旋转. 两种旋转呈 ...
- 06-看图理解数据结构与算法系列(AVL树)
AVL树 AVL树,也称平衡二叉搜索树,AVL是其发明者姓名简写.AVL树属于树的一种,而且它也是一棵二叉搜索树,不同的是他通过一定机制能保证二叉搜索树的平衡,平衡的二叉搜索树的查询效率更高. AVL ...
- 对于AVL树和红黑树的理解
AVL又称(严格)高度平衡的二叉搜索树,也叫二叉查找树.平衡二叉树.window对进程地址空间的管理用到了AVL树. 红黑树是非严格平衡二叉树,统计性能要好于平衡二叉树.广泛的在C++的STL中,ma ...
- 深入理解索引和AVL树、B-树、B+树的关系
目录 什么是索引 索引的分类 索引和AVL树.B-树.B+树的关系 AVL树.红黑树 B-树 B+树 SQL和NoSQL索引 什么是索引 索引时数据库的一种数据结构,数据库与索引的关系可以看作书籍和目 ...
- 算法与数据结构(十一) 平衡二叉树(AVL树)
今天的博客是在上一篇博客的基础上进行的延伸.上一篇博客我们主要聊了二叉排序树,详情请戳<二叉排序树的查找.插入与删除>.本篇博客我们就在二叉排序树的基础上来聊聊平衡二叉树,也叫AVL树,A ...
- 数据结构图文解析之:AVL树详解及C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- PAT树_层序遍历叶节点、中序建树后序输出、AVL树的根、二叉树路径存在性判定、奇妙的完全二叉搜索树、最小堆路径、文件路由
03-树1. List Leaves (25) Given a tree, you are supposed to list all the leaves in the order of top do ...
随机推荐
- Python之 Lambda表达式
标签(空格分隔): Python进阶 Lambda是一种匿名函数,当我们需要重复调用某一函数,又不想写那么多代码时可以使用lambda表达式来代替. lambda的通用格式: lambda argum ...
- Flask:静态文件&模板(0.1)
Windows 10家庭中文版,Python 3.6.4,Flask 1.0.2 前面看了Flask的Quickstart文档,可是,一直没有练习里面的内容,这不,刚刚练习完毕,来写篇博文记录一下! ...
- 使用mongoose操作mongodb数据库
1.如何启动mongodb数据库 参考地址:http://www.runoob.com/mongodb/mongodb-window-install.html 在数据库安装的地方,bin文件夹,输入 ...
- hihoCoder #1190 : 连通性·四(点的双连通分量模板)
时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho从约翰家回到学校时,网络所的老师又找到了小Hi和小Ho. 老师告诉小Hi和小Ho:之前的分组出了点问题,当服 ...
- JS中精选this关键字的指向规律你记住了吗
1.首先要明确: 谁最终调用函数,this指向谁 this指向的永远只可能是对象!!!!! this指向谁永远不取决于this写在哪,而取 ...
- (三)Jsoup 使用选择器语法查找 DOM 元素
第一节: Jsoup 使用选择器语法查找 DOM 元素 Jsoup使用选择器语法查找DOM元素 我们前面通过标签名,Id,Class样式等来搜索DOM,这些是不能满足实际开发需求的, 很多时候我们需要 ...
- celery在Django中的集成使用
继上回安装和使用Redis之后,看看如何在Django中使用Celery.Celery是Python开发分布式任务列队的处理库.可以异步分布式地异步处理任务,也可定时执行任务等等.通常我们可以在Dja ...
- 20155225 2016-2017-2 《Java程序设计》第十周学习总结
20155225 2016-2017-2 <Java程序设计>第十周学习总结 教材学习内容总结 22章网络 22.1网络概览 22.2超文本传输协议(HTTP) 22.2.1 HTTP请求 ...
- Windows开机自动启动pageant,方便使用ssh链接到GitHub
按win +r,输入 shell:startup "C:\Program Files\TortoiseGit\bin\pageant.exe" "d:\GitHubPri ...
- swift中Double转String
swift上手有好几天了.发现swift除了本身的几个基本类型转换,一些比较特殊的数值类型转换需要“桥接”到Objective-C来进行- 代码当然也很简单- var numString = &quo ...