平衡二叉树(AVL树)

目录

1 平衡二叉树

2 AVL树与其他树结构的比较

2.1 红黑树,AVL树

2.2 B树(平衡多路查找树)

2.2.1 B树的特性

2.2.2 在B树中查找给定关键字的方法

 2.2.3 B树的使用场景

 2.3 B+树

2.3.1 在B树上做了哪些改动?

2.3.2 B+树的两个搜索方法

2.3.3 B+树的使用场景

2.4 B树与B+树的比较

2.4.1 同

2.4.2 异

2.4.3 各自的优点


1 平衡二叉树

AVL树仍然是一棵二叉查找树,只是在其基础上增加了“平衡”的要求。所谓平衡是指,对AVL树的任意结点来说,其左子树与右子树的高度之差的绝对值不超过1,其中左子树与右子树的高度之差称为该结点的平衡因子。

假设现在已有一棵平衡二叉树,那么可以预见,在往其中插入一个结点时,一定会有结点的平衡因子发生变化,此时可能会有结点的平衡因子的绝对值大于1(这些平衡因子只可能是2或者-2),这样以该结点为根结点的子树就是失衡的,需要进行调整。可以证明,只要把最靠近插入结点的失衡结点调整到正常,路径上的所有结点就都会平衡

假设最靠近插入结点的失衡结点是A,显然它的平衡因子只可能是2或者-2.很容易发现这两种情况完全对称,因此主要讨论结点A的平衡因子是2的情形

由于结点A的平衡因子是2,因此左子树的高度比右子树大2,于是以结点A为根节点的子树一定是下图的两种形态LL型与LR型之一(注意:LL和LR只是表示树型,不是左右旋的意思)其中结点A、B、C的权值满足A>B>C。可以发现,当结点A的左孩子的平衡因子是1时为LL型,是-1时为LR型。

  

为什么结点A的左孩子的平衡因子只可能是1或者-1,而不可能是0?这是因为这种情况无法由平衡二叉树插入一个结点得到。

LL树型(A>B>C):把C为根结点的子树看做一个整体,然后以结点A作为root进行右旋,便可以达到平衡

LR树型(A>B>C):先忽略结点A,以结点C为root进行左旋,就可以把情况转化为LL型,然后按LL型进行一次右旋即可。

RR树型(A<B<C):可以把以C为根结点的子树看做一个整体,然后以结点A作为root进行左旋,便可以达到平衡。

RL树型(A<B<C):可以先忽略结点A,以结点C为root进行右旋,就可以把情况转化为RR型,然后进行一次左旋即可。

例题——PAT A1066

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN=21;
struct TreeNode {
    int data;  // 结点权值
    int height;  // 当前子树高度
    TreeNode* left;  // 左孩子结点地址
    TreeNode* right;  // 右孩子结点地址
    TreeNode():height(1),left(NULL),right(NULL) {}
    TreeNode(int d):data(d),height(1),left(NULL),right(NULL) {}
    int getHeight() {  // 获取结点所在子树的当前高度
        return height;
    }
    void updateHeight() {  // 更新结点的height
        int lenL = left==NULL? 0 : left->getHeight();
        int lenR = right==NULL? 0 : right->getHeight();
        height = max(lenL,lenR) + 1;
    }
    int getBalanceFactor() {  // 计算结点的平衡因子
        int lenL = left==NULL? 0 : left->getHeight();
        int lenR = right==NULL? 0 : right->getHeight();
        return lenL - lenR;
    }
};
void Search(TreeNode* root,int x) {  // 查找AVL树中数据域为x的结点
    if(root==NULL) { // 空树,查找失败
        //...... some behavior
        return;
    }
    if(x==root->data) {
        //查找成功,访问之
    } else if(x<root->data)
        Search(root->left, x);
    else
        Search(root->right, x);
}
void LeftTran(TreeNode* &root) {  // 左旋
    TreeNode* temp = root->right;
    root->right = temp->left;
    temp->left = root;
    root->updateHeight();
    temp->updateHeight();
    root = temp;
}
void RightTran(TreeNode* &root) {  // 右旋
    TreeNode* temp = root->left;
    root->left = temp->right;
    temp->right = root;
    root->updateHeight();
    temp->updateHeight();
    root = temp;
}
void Insert(TreeNode* &root,int val) {  // AVL树的插入操作
    if(root==NULL) {  // 到达空结点
        root = new TreeNode(val);
        return;
    }
    if(val < root->data) {  // val比根结点的权值小
        Insert(root->left, val);  // 往左子树插入
        root->updateHeight();  // 更新树高
        if(root->getBalanceFactor()==2) {
            if(root->left->getBalanceFactor()==1) { // LL型
                RightTran(root);
            } else if(root->left->getBalanceFactor()==-1) { // LR型
                LeftTran(root->left);
                RightTran(root);
            }
        }
    } else {
        Insert(root->right, val);
        root->updateHeight();
        if(root->getBalanceFactor()==-2) {
            if(root->right->getBalanceFactor()==-1) { // RR型
                LeftTran(root);
            } else if(root->right->getBalanceFactor()==1) { // RL型
                RightTran(root->right);
                LeftTran(root);
            }
        }
    }
}
TreeNode* Create(int arr[],int n) {  // AVL树的建立
    TreeNode* root = NULL;
    for(int i=0; i<n; i++)
        Insert(root, arr[i]);
    return root;   // 返回根结点
}
int main() {
    int N,arr[MAXN];
    scanf("%d",&N);
    for(int i=0; i<N; i++)
        scanf("%d",&arr[i]);
    TreeNode* root=Create(arr, N);
    printf("%d",root->data);
    return 0;
}

2 AVL树与其他树结构的比较

保证平衡性的最大的目的就是降低树的高度,因为树的查找性能取决于树的高度。所以树的高度越低搜索的效率越高!
这也是为什么存在二叉树、搜索二叉树等,各类树的目的。

2.1 红黑树,AVL树

简单来说都是用来搜索的

  • AVL树:平衡二叉树,一般是用平衡因子差值决定并通过旋转来实现,左右子树树高差不超过1,那么和红黑树比较它是严格的平衡二叉树,平衡条件非常严格(树高差只有1),只要插入或删除不满足上面的条件就要通过旋转来保持平衡。由于旋转是非常耗费时间的。我们可以推出AVL树适合用于插入删除次数比较少,但查找多的情况。
  • 红黑树:平衡二叉树,通过对任何一条从根到叶子的简单路径上各个节点的颜色进行约束,确保没有一条路径会比其他路径长2倍,因而是近似平衡的。所以相对于严格要求平衡的AVL树来说,它的旋转保持平衡次数较少。用于搜索时,插入删除次数多的情况下我们就用红黑树来取代AVL
红黑树

红黑树的特性:

  • 每个节点或者是黑色,或者是红色。
  • 根节点是黑色。
  • 每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
  • 如果一个节点是红色的,则它的子节点必须是黑色的。
  • 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。[这里指到叶子节点的路径]

包含n个内部节点的红黑树的高度是 O(log(n))

2.2 B树(平衡多路查找树)

2.2.1 B树的特性

  • 每个节点包含了键值和键值对于的数据对象存放地址指针,所以成功搜索一个对象可以不用到达树的叶节点
  • 每个节点至多有m棵子树
  • 根节点除外,其它每个分支节点至少有【m/2】棵子树
  • 根节点至少有两棵子树(除非B树只包含一个节点)
  • 所有叶子节点在同一层上,B树的叶子节点可以看成一种外部节点,不包含任何信息
  • 有j个孩子的非叶结点恰好有j-1个关键码,关键码按递增次序排列。
B树

2.2.2 在B树中查找给定关键字的方法

        首先把根结点取来,在根结点所包含的关键字K1,…,kj查找给定的关键字(可用顺序查找或二分查找法),若找到等于给定值的关键字,则查找成功;否则,一定可以确定要查的关键字在某个Ki或Ki+1之间(注意这是开区间,于是取Pi所指的下一层索引节点块继续查找,直到找到,或指针Pi为空时查找失败。

 2.2.3 B树的使用场景

 多用于做文件系统的索引

B树和二叉树、红黑树相比较,子树更多也就是路数越多,子树越多表示数的高度越低,搜索效率越高,当然如果路数太多就可能变成一个有序数组了。所以当然不可能使得路数无限大

正因为文件系统和数据库一般都是存在电脑硬盘上的,如果数据量太大的话不一定能一次性加载到内存中。(一棵树不能一次性加载完怎么查找对吧?)但是B树可以多路存储。也正因为B树的这一个优点,可以在文件查找的时候每次只加载一个节点的内容存入内存来查找。而红黑树在内存中查找非常块,但是如果在数据库和文件系统中,显然B树更优

 2.3 B+树

2.3.1 在B树上做了哪些改动?

  1. 非叶子结点不保存数据(只用来索引)
  2. 非叶子结点的子树指针与关键字个数相同;
  3. 非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);
  4. 所有叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字大小、自小而大顺序链接
B+树

2.3.2 B+树的两个搜索方法

  1. 按叶节点自己拉起的链表顺序搜索
  2. 从根节点开始搜索,和B树类似,不过如果非叶节点的关键码等于给定值,搜索并不停止,而是继续沿右指针,一直查到叶节点上的关键码。所以无论搜索是否成功,都将走完树的所有层

B+ 树中,数据对象的插入和删除仅在叶节点上进行。

2.3.3 B+树的使用场景

多用于数据库中的索引

因为在数据库中select常常不只是查询一条记录,常常要查询多条记录。比如:按照id的排序的后10条。如果是多条的话,B树需要做中序遍历,可能要跨层访问。而B+树由于所有数据都在叶子结点,不用跨层,同时由于有链表结构,只需要找到首尾,通过链表就能够把所有数据取出来了。

2.4 B树与B+树的比较

2.4.1 同

B树、B+树都是多路查找树,一般用于数据库系统中,为啥?

因为它们分支多层数少,都知道磁盘IO是非常耗时的,而像大量数据存储在磁盘中所以我们要有效的减少磁盘IO次数避免磁盘频繁的查找。

  • 磁盘读取数据是以盘块(block)为基本单位的。位于同一盘块中的所有数据都能被一次性全部读取出来。而磁盘IO代价主要花费在查找时间Ts上。因此我们应该尽量将相关信息存放在同一盘块,同一磁道中。或者至少放在同一柱面或相邻柱面上,以求在读/写信息时尽量减少磁头来回移动的次数,避免过多的查找时间Ts。
  • 所以,在大规模数据存储方面,大量数据存储在外存磁盘中,而在外存磁盘中读取/写入块(block)中某数据时,首先需要定位到磁盘中的某块,如何有效地查找磁盘中的数据,需要一种合理高效的外存数据结构。

2.4.2 异

  • B树中同一键值不会出现多次,并且它有可能出现在叶结点,也有可能出现在非叶结点中。而B+树的键一定会出现在叶结点中,并且有可能在非叶结点中也有可能重复出现,以维持B+树的平衡。
  • 因为B树键位置不定,且在整个树结构中只出现一次,虽然可以节省存储空间,但使得在插入、删除操作复杂度明显增加。B+树相比来说是一种较好的折中。
  • B树的查询效率与键在树中的位置有关,最大时间复杂度与B+树相同(在叶结点的时候),最小时间复杂度为1(在根结点的时候)。而B+树的时候复杂度对某建成的树是固定的。

2.4.3 各自的优点

1、B+树的层级更少:相较于B树B+每个非叶子节点存储的关键字数更多,树的层级更少所以查询数据更快;

2、B+树查询速度更稳定:B+所有关键字数据地址都存在叶子节点上,所以每次查找的次数都相同所以查询速度要比B树更稳定;

3、B+树天然具备排序功能:B+树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比B树高。

4、B+树全节点遍历更快:B+树遍历整棵树只需要遍历所有的叶子节点即可,,而不需要像B树一样需要对每一层进行遍历,这有利于数据库做全表扫描。

B树相对于B+树的优点是,如果经常访问的数据离根节点很近,而B树非叶子节点本身存有关键字其数据的地址,所以这种数据检索的时候会要比B+树快。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值