STL源码笔记(18)—平衡二叉树AVL(C++封装+模板)

AVLTree平衡二叉树

在几年前刚学数据结构时,AVL-Tree只是一个仅仅需要掌握其概念的东西,今非昔比,借看STL源码剖析的契机希望从代码层面将其拿下。

1.简介

二叉查找树给我们带来了很多方便,但是由于其在有序序列插入时就会退化成单链表(时间复杂度退化成 O(n)),AVL-tree就克服了上述困难。AVL-tree是一个“加上了平衡条件的”二叉搜索树,平衡条件确保整棵树的深度为O(log n)。

AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是 O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。

节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或-1的节点被认为是平衡的。带有平衡因子-2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。

AVL树的所有操作都与二叉查找树相同,不同的是,这里AVL树需要做“AVL旋转”。


2.AVL旋转

AVL树最重要的核心部分就是AVL旋转了,这部分我的感触是,单做旋转还是挺好理解的,只不过写起代码来有点复杂,书中以插入节点为例,删除节点的部分折腾了好久。

在理解AVL旋转之前,首先得知道以下几个概念:
1. AVL 树节点的插入总是在叶子节点。
2. AVL 树在插入节点之前总是满足平衡条件的。
3. 插入新节点后有可能满足平衡条件也有可能不满足。
4. 当不满足平衡条件后,我们就需要对新的树进行旋转。

旋转之前,我们首先要找到一个X节点,这个X节点做如下定义:

假如我们在某一个叶子节点处插入一个新的节点后,此时这棵树的某些节点的平衡性会发生变化,那么我们从叶子节点向上到根节点的路径上第一个平衡性发生变化的节点。

基于这个X节点,考虑一件事情:
这个X节点分为左右子树,左右子树又有左右子树,1分2,2分4,所以以这个X节点为根节点的话,新插入的节点可能出现的位置有:

X的左孩子节点的左子树上(left-left)
X的右孩子节点的右子树上(right-right)
X的左孩子节点的右子树上(left-right)
X的右孩子节点的左子树上(right-left)

根据上述情况就延生出了4种旋转:
1.left-left Rotation
2.right-right Rotation
3.left-right Rotation
4.right-left Rotation

前两种属于单旋转,后两种属于双旋转,双旋转的操作可以由两次单旋转组成。

PS:AVL树的旋转还是得画图来理解,这里直接贴出书中的图了。

插入新节点

这里写图片描述

平衡破坏条件

这里写图片描述

单旋转(以left-left Rotation为例)

这里写图片描述

双旋转(以left-right Rotation为例)

这里写图片描述

双旋转:两次单旋转

这里写图片描述


3.AVL-Tree实现

AVL-Tree是一个二叉排序树,其基本操作也跟它类似,唯一需要注意的就是在插入,删除节点后,需要对树进行调整,让树的每个节点保持平衡。

节点的平衡因子是通过计算其左子树和右子树的差得来的,这里有两种考虑方式:
1. 每次都计算一次(递归求深度)。
2. 将平衡因子作为一个成员变量保存在节点中,平衡性发生变化的时候更新。

我采取了第1种方式,网上也有用第2种方式的,我说不上利弊,但是感觉起码掌握一种方式就可以“一招鲜”了吧。

另外,这里我用了C++类封装,为了学习还顺便使用了模板,所以类的声明和实现都放在了一个文件中,感觉内容太多,还是分开来比较好。

(1)AVL-Tree节点结构定义

//AVLNode.h

#ifndef __AVLNODE_H__
#define __AVLNODE_H__
#include <iostream>
#include <vector>
#include <algorithm>
template <typename KeyType>
class AVLNode{
public:
    KeyType key;
    AVLNode * left;
    AVLNode * right;
    AVLNode() :key(0),left(NULL), right(NULL){}
    AVLNode(KeyType k) :key(k), left(NULL), right(NULL){}
};
#endif

(2)AVL-Tree 类声明

//AVLTree.h

#ifndef __AVLTREE_H__
#define __AVLTREE_H__
#include "AVLNode.h"
//AVL树的模板实现
template <typename KeyType>
class AVLTree
{
    typedef AVLNode<KeyType> AVLNode;//类型定义
private:
    AVLNode * avlroot;//私有数据结构
    int __height(const AVLNode *root);//求树的高度
    int __diff(const AVLNode*root);//高度差(平衡因子)

    //AVL4种旋转:左左,左右,右右,右左
    //X定义为插入位置节点到根节点的路径上平衡条件被改变的节点中最深的那个节点
    //X通过递归返回的方式找到
    //左左:插入点位于X的左孩子节点的左子树
    //左右:插入点位于X的左孩子节点的右子树
    //右右:插入点位于X的右孩子节点的右子树
    //右左:插入点位于X的右孩子节点的左子树

    //单旋转
    AVLNode * __ll_Rotation(AVLNode *root);//left-left rotation
    AVLNode * 
  • 13
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值