参考资料

http://www.cnblogs.com/Cmpl/archive/2011/06/05/2073217.html

http://www.cnblogs.com/yc_sunniwell/archive/2010/06/27/1766236.html

http://www.cnblogs.com/suimeng/p/4560056.html

http://blog.csdn.net/gabriel1026/article/details/6311339

http://www.cnblogs.com/zhangbaochong/p/5164994.html

一、基本概念

平衡二叉查找树

简称平衡二叉树。由前苏联的数学家Adelse-Velskil和Landis在1962年提出的高度平衡的二叉树,根据科学家的英文名也称为AVL树。它具有如下几个性质:

  1. 可以是空树。
  2. 假如不是空树,任何一个结点的左子树与右子树都是平衡二叉树,并且高度之差的绝对值不超过1

平衡二叉树一定是二叉查找树。

平衡因子

左子树的高度减去右子树的高度。由平衡二叉树的定义可知,平衡因子的取值只可能为0,1,-1.分别对应着左右子树等高,左子树比较高,右子树比较高。

最小失衡子树:在新插入的结点向上查找,以第一个平衡因子的绝对值超过1的结点为根的子树称为最小不平衡子树。也就是说,一棵失衡的树,是有可能有多棵子树同时失衡的,如下。而这个时候,我们只要调整最小的不平衡子树,就能够将不平衡的树调整为平衡的树。

二、算法

平衡二叉树算法思想

若 向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性。首先要找出插入新结点后失去平衡的最小子树根结点的指针。然后再调整这个子树中有关结点之间的链接关系,使之成为新的平衡子树。当失去平衡的最小子树被调整为平衡子树后,原有其他所有不平衡子树无需调整,整个二叉排序树就又成为一棵平衡二叉树。失去平衡的最小子树是指以离插入结点最近,且平衡因子绝对值大于1的结点作为根的子树。假设用A表示失去平衡的最小子树的根结点,则调整该子树的操作可归纳为下列四种情况。

1. LL型

个人理解

左旋:旋转中心的父节点绕旋转中心向左下旋转

右旋:旋转中心的父节点绕旋转中心向右下旋转

平衡二叉树某一节点的左孩子的左子树上插入一个新的节点,使得该节点不再平衡。这时只需要把树向右旋转一次即可,如图所示,原A的左孩子B变为父结点,A变为其右孩子,而原B的右子树变为A的左子树,注意旋转之后Brh是A的左子树(图上忘在A于Brh之间标实线)

 

2. RR型

平衡二叉树某一节点的右孩子的右子树上插入一个新的节点,使得该节点不再平衡。这时只需要把树向左旋转一次即可,如图所示,原A右孩子B变为父结点,A变为其左孩子,而原B的左子树Blh将变为A的右子树。

3. LR型(最小失衡子树的根节点(A)的左孩子(B)的右孩子(C)为旋转点左旋,然后再以这个点(C)右旋)

平衡二叉树某一节点的左孩子的右子树上插入一个新的节点,使得该节点不再平衡。这时需要旋转两次,仅一次的旋转是不能够使二叉树再次平衡。如图所示,在B节点按照RR型向左旋转一次之后,二叉树在A节点仍然不能保持平衡,这时还需要再向右旋转一次。

4. RL型(最小失衡子树的根节点(A)的右孩子(B)的左孩子(C)为旋转点右旋,然后再以这个点(C)左旋)

平衡二叉树某一节点的右孩子的左子树上插入一个新的节点,使得该节点不再平衡。同样,这时需要旋转两次,旋转方向刚好同LR型相反。

删除节点

删除则稍微麻烦点,因为我们删的不一定是叶子,如果只是叶子,那就好办,如果不是呢?我们最通常的做法就是把这个节点往下挪,直到它变为叶子为止,看图。


也许你要问,如果和左子树最大节点交换后,要删除的节点依然不是叶子,那怎么办呢?那继续呗,看图:


那左子树不存在的情况下呢?你可以查找右子树的最小节点,和上面是类似的,图我就不画了。

三、示例代码

 #include <iostream>
#include <algorithm>
using namespace std;
#pragma once //平衡二叉树结点
template <typename T>
struct AvlNode
{
T data;
int height; //结点所在高度
AvlNode<T> *left;
AvlNode<T> *right;
AvlNode<T>(const T theData) : data(theData), left(NULL), right(NULL), height(){}
}; //AvlTree
template <typename T>
class AvlTree
{
public:
AvlTree<T>(){}
~AvlTree<T>(){}
AvlNode<T> *root;
//插入结点
void Insert(AvlNode<T> *&t, T x);
//删除结点
bool Delete(AvlNode<T> *&t, T x);
//查找是否存在给定值的结点
bool Contains(AvlNode<T> *t, const T x) const;
//中序遍历
void InorderTraversal(AvlNode<T> *t);
//前序遍历
void PreorderTraversal(AvlNode<T> *t);
//最小值结点
AvlNode<T> *FindMin(AvlNode<T> *t) const;
//最大值结点
AvlNode<T> *FindMax(AvlNode<T> *t) const;
private:
//求树的高度
int GetHeight(AvlNode<T> *t);
//单旋转 左
AvlNode<T> *LL(AvlNode<T> *t);
//单旋转 右
AvlNode<T> *RR(AvlNode<T> *t);
//双旋转 右左
AvlNode<T> *LR(AvlNode<T> *t);
//双旋转 左右
AvlNode<T> *RL(AvlNode<T> *t);
}; template <typename T>
AvlNode<T> * AvlTree<T>::FindMax(AvlNode<T> *t) const
{
if (t == NULL)
return NULL;
if (t->right == NULL)
return t;
return FindMax(t->right);
} template <typename T>
AvlNode<T> * AvlTree<T>::FindMin(AvlNode<T> *t) const
{
if (t == NULL)
return NULL;
if (t->left == NULL)
return t;
return FindMin(t->left);
} template <typename T>
int AvlTree<T>::GetHeight(AvlNode<T> *t)
{
if (t == NULL)
return -;
else
return t->height;
} //单旋转
//左左插入导致的不平衡
template <typename T>
AvlNode<T> * AvlTree<T>::LL(AvlNode<T> *t)
{
AvlNode<T> *q = t->left;
t->left = q->right;
q->right = t;
t = q;
t->height = max(GetHeight(t->left), GetHeight(t->right)) + ;
q->height = max(GetHeight(q->left), GetHeight(q->right)) + ;
return q;
} //单旋转
//右右插入导致的不平衡
template <typename T>
AvlNode<T> * AvlTree<T>::RR(AvlNode<T> *t)
{
AvlNode<T> *q = t->right;
t->right = q->left;
q->left = t;
t = q;
t->height = max(GetHeight(t->left), GetHeight(t->right)) + ;
q->height = max(GetHeight(q->left), GetHeight(q->right)) + ;
return q;
} //双旋转
//插入点位于t的左儿子的右子树
template <typename T>
AvlNode<T> * AvlTree<T>::LR(AvlNode<T> *t)
{
//双旋转可以通过两次单旋转实现
//对t的左结点进行RR旋转,再对根节点进行LL旋转
RR(t->left);
return LL(t);
} //双旋转
//插入点位于t的右儿子的左子树
template <typename T>
AvlNode<T> * AvlTree<T>::RL(AvlNode<T> *t)
{
LL(t->right);
return RR(t);
} template <typename T>
void AvlTree<T>::Insert(AvlNode<T> *&t, T x)
{
if (t == NULL)
t = new AvlNode<T>(x);
else if (x < t->data)
{
Insert(t->left, x);
//判断平衡情况
if (GetHeight(t->left) - GetHeight(t->right) > )
{
//分两种情况 左左或左右 if (x < t->left->data)//左左
t = LL(t);
else //左右
t = LR(t);
}
}
else if (x > t->data)
{
Insert(t->right, x);
if (GetHeight(t->right) - GetHeight(t->left) > )
{
if (x > t->right->data)
t = RR(t);
else
t = RL(t);
}
}
else
;//数据重复
t->height = max(GetHeight(t->left), GetHeight(t->right)) + ;
} template <typename T>
bool AvlTree<T>::Delete(AvlNode<T> *&t, T x)
{
//t为空 未找到要删除的结点
if (t == NULL)
return false;
//找到了要删除的结点
else if (t->data == x)
{
//左右子树都非空
if (t->left != NULL && t->right != NULL)
{//在高度更大的那个子树上进行删除操作 //左子树高度大,删除左子树中值最大的结点,将其赋给根结点
if (GetHeight(t->left) > GetHeight(t->right))
{
t->data = FindMax(t->left)->data;
Delete(t->left, t->data);
}
else//右子树高度更大,删除右子树中值最小的结点,将其赋给根结点
{
t->data = FindMin(t->right)->data;
Delete(t->right, t->data);
}
}
else
{//左右子树有一个不为空,直接用需要删除的结点的子结点替换即可
AvlNode<T> *old = t;
t = t->left ? t->left: t->right;//t赋值为不空的子结点
delete old;
}
}
else if (x < t->data)//要删除的结点在左子树上
{
//递归删除左子树上的结点
Delete(t->left, x);
//判断是否仍然满足平衡条件
if (GetHeight(t->right) - GetHeight(t->left) > )
{
if (GetHeight(t->right->left) > GetHeight(t->right->right))
{
//RL双旋转
t = RL(t);
}
else
{//RR单旋转
t = RR(t);
}
}
else//满足平衡条件 调整高度信息
{
t->height = max(GetHeight(t->left), GetHeight(t->right)) + ;
}
}
else//要删除的结点在右子树上
{
//递归删除右子树结点
Delete(t->right, x);
//判断平衡情况
if (GetHeight(t->left) - GetHeight(t->right) > )
{
if (GetHeight(t->left->right) > GetHeight(t->left->left))
{
//LR双旋转
t = LR(t);
}
else
{
//LL单旋转
t = LL(t);
}
}
else//满足平衡性 调整高度
{
t->height = max(GetHeight(t->left), GetHeight(t->right)) + ;
}
} return true;
} //查找结点
template <typename T>
bool AvlTree<T>::Contains(AvlNode<T> *t, const T x) const
{
if (t == NULL)
return false;
if (x < t->data)
return Contains(t->left, x);
else if (x > t->data)
return Contains(t->right, x);
else
return true;
} //中序遍历
template <typename T>
void AvlTree<T>::InorderTraversal(AvlNode<T> *t)
{
if (t)
{
InorderTraversal(t->left);
cout << t->data << ' ';
InorderTraversal(t->right);
}
} //前序遍历
template <typename T>
void AvlTree<T>::PreorderTraversal(AvlNode<T> *t)
{
if (t)
{
cout << t->data << ' ';
PreorderTraversal(t->left);
PreorderTraversal(t->right);
}
}

cpp

 #include "AvlTree.h"

 int main()
{
AvlTree<int> tree;
int value;
int tmp;
cout << "请输入整数建立二叉树(-1结束):" << endl;
while (cin >> value)
{
if (value == -)
break;
tree.Insert(tree.root,value);
}
cout << "中序遍历";
tree.InorderTraversal(tree.root);
cout << "\n前序遍历:";
tree.PreorderTraversal(tree.root);
cout << "\n请输入要查找的结点:";
cin >> tmp;
if (tree.Contains(tree.root, tmp))
cout << "已查找到" << endl;
else
cout << "值为" << tmp << "的结点不存在" << endl;
cout << "请输入要删除的结点:";
cin >> tmp;
tree.Delete(tree.root, tmp);
cout << "删除后的中序遍历:";
tree.InorderTraversal(tree.root);
cout << "\n删除后的前序遍历:";
tree.PreorderTraversal(tree.root);
}

平衡二叉树(AVL树)的更多相关文章

  1. 二叉查找树(BST)、平衡二叉树(AVL树)(只有插入说明)

    二叉查找树(BST).平衡二叉树(AVL树)(只有插入说明) 二叉查找树(BST) 特殊的二叉树,又称为排序二叉树.二叉搜索树.二叉排序树. 二叉查找树实际上是数据域有序的二叉树,即对树上的每个结点, ...

  2. Java 树结构实际应用 四(平衡二叉树/AVL树)

    平衡二叉树(AVL 树) 1 看一个案例(说明二叉排序树可能的问题) 给你一个数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST), 并分析问题所在.  左边 BST 存在的问题分析: ...

  3. 【数据结构】平衡二叉树—AVL树

    (百度百科)在计算机科学中,AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增 ...

  4. 平衡二叉树,AVL树之图解篇

    学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建 ...

  5. 二叉查找树(BST)、平衡二叉树(AVL树)

    二叉查找树(BST) 特殊的二叉树,又称为排序二叉树.二叉搜索树.二叉排序树. 二叉查找树实际上是数据域有序的二叉树,即对树上的每个结点,都满足其左子树上所有结点的数据域均小于或等于根结点的数据域,右 ...

  6. 图解:平衡二叉树,AVL树

    学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建 ...

  7. 平衡二叉树,AVL树之代码篇

    看完了第一篇博客,相信大家对于平衡二叉树的插入调整以及删除调整已经有了一定的了解,下面,我们开始介绍代码部分. 首先,再次提一下使用的结构定义 typedef char KeyType; //关键字 ...

  8. 大话数据结构—平衡二叉树(AVL树)

    平衡二叉树(Self-Balancing Binary Search Tree/Height-Balanced Binary Search Tree),是一种二叉排序树,当中每个节点的左子树和右子树的 ...

  9. 平衡二叉树-AVL树(LL、RR、LR、RL旋转)

    平衡二叉树的定义: 任意的左右子树高度差的绝对值不超过1,将这样的二叉树称为平衡二叉树,二叉平衡树前提是一个二叉排序树. 平衡二叉树的插入: 二叉平衡树在插入或删除一个结点时,先检查该操作是否导致了树 ...

  10. 数据结构之平衡二叉树(AVL树)

    平衡二叉树(AVL树)定义如下:平衡二叉树或者是一棵空树,或者是具有以下性质的二叉排序树: (1)它的左子树和右子树的高度之差绝对值不超过1: (2)它的左子树和右子树都是平衡二叉树. AVL树避免了 ...

随机推荐

  1. 【Python学习笔记之二】浅谈Python的yield用法

    在上篇[Python学习笔记之一]Python关键字及其总结中我提到了yield,本篇文章我将会重点说明yield的用法 在介绍yield前有必要先说明下Python中的迭代器(iterator)和生 ...

  2. 常用Linux命令、包括vi 、svn

    /etc/init.d/network restart//===========================================更新脚本cd /www/scripts更新站点./sta ...

  3. mysql 存储引擎介绍1

    1.1  存储引擎的使用 数据库中的各表均被(在创建表时)指定的存储引擎来处理. 服务器可用的引擎依赖于以下因素: MySQL的版本 服务器在开发时如何被配置 启动选项 为了解当前服务器中有哪些存储引 ...

  4. [spring 入门第一天]

    关于Spring Framework 简介: Spring框架提供了一个全面的现代java企业应用程序编程和配置模型——可以部署在任何类型的平台.支持任何级别的应用程序:spring专注于程序架构,这 ...

  5. 一.把传统服务做成dubbo分布式服务架构的步骤

    1.把传统服务按照一定原则(根据项目的业务逻辑和场景)拆分成多个服务(主要服务是服务提供者和服务消费者,服务提供者或服务消费者的公共部分也可以拆分成其他服务,如公共DAO.公共工具类.公共实体,公共w ...

  6. java 连接数据库测试类

    import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import ...

  7. python之线程学习

    一.进程与线程简介 进程 进程是程序的一次执行,由进程段.数据段.进程控制块三部分组成.具体三个基本状态,就绪.执行.阻塞,是一个拥有资源的独立单位. 线程 属于进程的一个实体,拥有极少的资源.也具有 ...

  8. 【转】 文档与笔记利器 reStructuredText 和 Sphinx

    关于制作文档和笔记这种事,我已经纠结了很久,网上解决方案也一大推,我试过几样,ScrapBook 和 Zotero,编辑不太方便,同步麻烦.Google Note 过于格式简单,现在也不更新了,Goo ...

  9. webpack 的使用2

    实际项目中的配置 要加__dirname 不然会报错 注意path  /dist 前不要加点 结果 将两个文件打包在一起 结果 传入对象 并且单独打包 name为key 加上本次打包的hash has ...

  10. PDO浅谈之php连接mysql

    一.首先我们先说一下什么是pdo?  百科上说 PDO扩展为PHP访问数据库定义了一个轻量级的.一致性的接口,它提供了一个数据访问抽象层,这样,无论使用什么数据库,都可以通过一致的函数执行查询和获取数 ...