在上一个专题中,我们在谈论二叉查找树的效率的时候。不同结构的二叉查找树,查找效率有很大的不同(单支树结构的查找效率退化成了顺序查找)。如何解决这个问题呢?关键在于如何最大限度的减小树的深度。正是基于这个想法,平衡二叉树出现了。

平衡二叉树的定义 (AVL—— 发明者为Adel'son-Vel'skii 和 Landis)

平衡二叉查找树,又称 AVL树。 它除了具备二叉查找树的基本特征之外,还具有一个非常重要的特点:它 的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值(平衡因子 ) 不超过1。 也就是说AVL树每个节点的平衡因子只可能是-1、0和1(左子树高度减去右子树高度)。

那么如何是二叉查找树在添加数据的同时保持平衡呢?基本思想就是:当在二叉排序树中插入一个节点时,首先检查是否因插入而破坏了平衡,若 破坏,则找出其中的最小不平衡二叉树,在保持二叉排序树特性的情况下,调整最小不平衡子树中节点之间的关系,以达 到新的平衡。所谓最小不平衡子树 指离插入节点最近且以平衡因子的绝对值大于1的节点作为根的子树。

平衡二叉树的操作

1. 查找操作

平衡二叉树的查找基本与二叉查找树相同。

2. 插入操作

在平衡二叉树中插入结点与二叉查找树最大的不同在于要随时保证插入后整棵二叉树是平衡的。那么调整不平衡树的基本方法就是: 旋转 。 下面我们归纳一下平衡旋转的4中情况

1) 绕某元素左旋转

80                                    90

/  \             左旋               /    \

60 90          ---- ->         80     120

/  \                               /  \       /

85 120                    60  85 100

/

100

a)  BST树                              b ) AVL树

分析一下:在插入数据100之前,a图的B ST树只有80节点的平衡因子是-1(左高-右高),但整棵树还是平衡的。加入100之后,80节点的平衡因子就成为了-2,此时平衡被破坏。需要左旋转成b 图。

当树中节点X的右孩子的右孩子上插入新元素,且平衡因子从-1变成-2后,就需要绕节点X进行左旋转。

2) 绕某元素右旋转

100                                   85

/  \               右旋              /    \

85  120         ------ ->     60    100

/  \                                      \      /   \

60 90                                 80  90 120

\

80

a) B ST树                                b) AVL树

当树中节点X的左孩子的左孩子上插入新元素,且平衡因子从1变成2后,就需要绕节点X进行右旋转。

3) 绕某元素的左子节点左旋转,接着再绕该元素自己右旋转。 此情况下就是左旋与右旋 的结合,具体操作时可以分 解成这两种操作,只是围绕点不一样而已。

100                             100                                90

/  \             左旋            /  \              右旋           /    \

80  120       ------>      90  120        ------>     80   100

/ \                                  /                                    /  \      \

60 90                            80                              60  85  120

/                               / \

85                            60 85

当树中节点X的左孩子的右孩子上插入新元素,且 平衡因子从1变成2后,就需要 先绕X的左子节点Y左旋转,接着再绕X右旋转

4) 绕某元素的右子节点右旋转,接着再绕该元素自己左旋转。 此情况下就是 右旋与左旋 的结合,具体操作时可以分解 成这两种操作,只是围绕点不一样而已 。

80                               80                                       85

/   \             右 旋          /  \                 左 旋             /  \

60  100      ------>      60 85            ------->          80 100

/  \                                 \                                   /     /   \

85  120                        100                           60    90 120

\                                   /  \

90                           90  120

当树中节点X的右孩子的左孩子上插入新元素,且 平衡因子从-1变成-2后,就需要 先绕X的右子节点Y右旋转,接着再绕X左旋转

平衡二叉树性能分析

平衡二叉树的性能优势:

很显然,平衡二叉树的优势在于不会出现普通二叉查找树的最差情况。其查找的时间复杂度为O(logN)。

平衡二叉树的缺陷:

(1) 很遗憾的是,为了保证高度平衡,动态插入和删除的代价也随之增加。因此,我们在下一专题中讲讲《红黑树》 这种更加高效的查找结构。

(2) 所有二叉查找树结构的查找代价都与树高是紧密相关的,能否通过减少树高来进一步降低查找代价呢。我们可以通过多路查找树的结构来做到这一点,在后面专题中我们将通过《多路查找树/B-树/B+树 》来介绍。

(3) 在大数据量查找环境下(比如说系统磁盘里的文件目录,数据库中的记录查询 等),所有的二叉查找树结构(BST、AVL、RBT)都不合适。如此大规模的数据量(几G数据),全部组织成平衡二叉树放在内存中是不可能做到的。那么 把这棵树放在磁盘中吧。问题就来了:假如构造的平衡二叉树深度有1W层。那么从根节点出发到叶子节点很可能就需要1W次的硬盘IO读写。大家都知道,硬盘的机械部件读写数据的速度远远赶不上纯电子媒体的内存。 查找效率在IO读写过程中将会付出巨大的代价。在大规模数据查询这样一个实际应用背景下,平衡二叉树的效率就很成问题了。对这一问题的解决:我们也会在《多路查找树/B-树/B+树 》 将详细分析。

上面提到的红黑树和多路查找树都是属于深度有界查找树(depth-bounded tree —DBT)

来自于:http://hxraid.iteye.com/blog/609949

----------------------------------------------------------------------------------------------------------------------------------------------------------

  1. typedef int ElementType;
  2. #ifndef AVLTREE_H
  3. #define AVLTREE_H
  4. struct TreeNode
  5. {
  6. ElementType Element;
  7. int Height;
  8. struct TreeNode *Left;
  9. struct TreeNode *Right;
  10. };
  11. typedef struct TreeNode *AvlTree;
  12. typedef struct TreeNode *Position;
  13. AvlTree MakeEmpty(AvlTree T);
  14. AvlTree Insert(ElementType X, AvlTree T);
  15. Position Find(ElementType X ,AvlTree T);
  16. Position FindMax(AvlTree T);
  17. Position FindMin(AvlTree T);
  18. #endif

avltree.c函数实现

[cpp] view plaincopyprint?
  1. #include "fatal.h"
  2. #include "avltree.h"
  3. AvlTree MakeEmpty(AvlTree T)
  4. {
  5. if(T != NULL)
  6. {
  7. MakeEmpty(T->Left);
  8. MakeEmpty(T->Right);
  9. free(T);
  10. }
  11. return NULL;
  12. }
  13. static int Height(Position P)
  14. {
  15. if(P == NULL)
  16. return -1;
  17. else
  18. return P->Height;
  19. }
  20. static int Max(int Lhs, int Rhs)
  21. {
  22. return Lhs > Rhs ? Lhs : Rhs;
  23. }
  24. static Position SingleRotateWithLeft(Position K2)
  25. {
  26. Position K1;
  27. K1 = K2->Left;
  28. K2->Left = K1->Right;
  29. K1->Right = K2;
  30. K1->Height = Max(Height(K1->Left), Height(K1->Right)) + 1;
  31. K2->Height = Max(Height(K2->Left), Height(K2->Right)) + 1;
  32. return K1;
  33. }
  34. static Position SingleRotateWithRight(Position K2)
  35. {
  36. Position K1;
  37. K1 = K2->Right;
  38. K2->Right = K1->Left;
  39. K1->Left = K2;
  40. K1->Height = Max(Height(K1->Left), Height(K1->Right)) + 1;
  41. K2->Height = Max(Height(K2->Left), Height(K2->Right)) + 1;
  42. return K1;
  43. }
  44. static Position DoubleRotateWithLeft(Position K3)
  45. {
  46. K3->Left = SingleRotateWithRight(K3->Left);
  47. return SingleRotateWithLeft(K3);
  48. }
  49. static Position DoubleRotateWithRight(Position K3)
  50. {
  51. K3->Right = SingleRotateWithLeft(K3->Right);
  52. return SingleRotateWithRight(K3);
  53. }
  54. AvlTree Insert(ElementType X, AvlTree T)
  55. {
  56. if(T == NULL)
  57. {
  58. T = (Position)malloc(sizeof(struct TreeNode));
  59. if(T == NULL)
  60. FatalError("Out of space");
  61. T->Element = X;
  62. T->Height = 0;
  63. T->Left = T->Right = NULL;
  64. }
  65. else if(X < T->Element)//左子树插入新节点
  66. {
  67. T->Left = Insert(X, T->Left);
  68. if(Height(T->Left) - Height(T->Right) == 2)//左子树插入节点所以高度是左子树高于右子树
  69. {
  70. if(X < T->Left->Element)//对α的左儿子的左子树进行一次插入,需要左旋
  71. T = SingleRotateWithLeft(T);
  72. else //对α的左儿子的右子树进行一次插入,需要双旋
  73. T = DoubleRotateWithLeft(T);
  74. }
  75. }
  76. else if(X > T->Element)//右子树插入新节点
  77. {
  78. T->Right = Insert(X, T->Right);
  79. if(Height(T->Right) - Height(T->Left) == 2)//因为是右子树插入新节点,所以高度是右子树高于左子树
  80. {
  81. if(X > T->Right->Element)//对α的右儿子的右子树进行一次插入,需要右旋
  82. T = SingleRotateWithRight(T);
  83. else//对α的右儿子的左子树进行一次插入,需要双旋
  84. T = DoubleRotateWithRight(T);
  85. }
  86. }
  87. T->Height = Max(Height(T->Left), Height(T->Right)) + 1;
  88. return T;
  89. }
  90. Position Find(ElementType X, AvlTree T)
  91. {
  92. if(T == NULL)
  93. return NULL;
  94. if(X < T->Element)
  95. return Find(X, T->Left);
  96. else if(X > T->Element)
  97. return Find(X, T->Right);
  98. else
  99. return T;
  100. }
  101. Position FindMin(AvlTree T)
  102. {
  103. if(T == NULL)
  104. return NULL;
  105. else if(T->Left == NULL)
  106. return T;
  107. else
  108. return FindMin(T->Left);
  109. }
  110. Position FindMax(AvlTree T)
  111. {
  112. if(T == NULL)
  113. return NULL;
  114. else if(T->Right == NULL)
  115. return T;
  116. else
  117. return FindMax(T->Right);
  118. }

testavl.c测试AVL树的实现

[cpp] view plaincopyprint?
  1. #include "avltree.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. void InOrder(AvlTree T)
  5. {
  6. if(T != NULL)
  7. {
  8. InOrder(T->Left);
  9. printf("%d ", T->Element);
  10. InOrder(T->Right);
  11. }
  12. }
  13. void PreOrder(AvlTree T)
  14. {
  15. if(T != NULL)
  16. {
  17. printf("%d ", T->Element);
  18. PreOrder(T->Left);
  19. PreOrder(T->Right);
  20. }
  21. }
  22. int main(void)
  23. {
  24. AvlTree T;
  25. Position P;
  26. int i;
  27. T = MakeEmpty(NULL);
  28. for(i = 1; i <= 7; i++)
  29. T = Insert(i, T);
  30. for(i = 16; i >= 10; i--)
  31. T = Insert(i, T);
  32. T = Insert(8, T);
  33. T = Insert(9, T);
  34. printf("Root: %d\n", T->Element);
  35. printf("InOrder:  ");
  36. InOrder(T);
  37. printf("\nPreOrder: ");
  38. PreOrder(T);
  39. putchar('\n');
  40. system("Pause");
  41. return 0;
  42. }

测试:首先插入1到7,然后插入16到10,最后插入8和9。AVL树的应该为下图所示

测试结果如下图所示

---------------------------------------------------------------------------------------------------------------------------------------------------------

来于:http://blog.csdn.net/zitong_ccnu/article/details/11097663

--------------------------------------------------------------------

//关于平衡二叉树的实现 BBST(Blance binary search tree)
#include <cstdio>
#include <cstdlib>
//#define _OJ_
typedef struct tree
{
    int data;
    int height;
    struct tree *lchild;
    struct tree *rchild;
} tree, *Bitree;

int
Height(Bitree T)
{
    if(T == NULL)    return -1;
    else  return T->height;
}

int
Max(int h1, int h2)
{
    return h1 > h2 ? h1 : h2;
}

Bitree
Left(Bitree T)
{
    Bitree T1;
    T1 = T->rchild;
    T->rchild = T1->lchild;
    T1->lchild = T;

T->height  = Max(Height(T->lchild), Height(T->rchild)) + 1;
    printf("T == %d\n", T->height);

T1->height = Max(Height(T1->lchild), Height(T1->rchild)) + 1;
    printf("T1 == %d\n", T1->height);
    return T1;
}

Bitree
Right(Bitree T)
{
    Bitree T1;
    T1 = T->lchild;
    T->lchild = T1->rchild;
    T1->rchild = T;
 
    T->height  = Max(Height(T->lchild), Height(T->rchild)) + 1;
    printf("T == %d\n", T->height);
    T1->height = Max(Height(T1->lchild), Height(T1->rchild)) + 1;
    printf("T1 == %d\n", T1->height);
    return T1;
}

Bitree
Left_Right(Bitree T)
//左孩子的右孩子插入元素
{
    T = Left(T);
    return Right(T);
}

Bitree
Right_Left(Bitree T)
//右孩子的左孩子插入元素
{
    T = Right(T);
    return Left(T);
}

Bitree
Insert(Bitree T, int x)
{
    if(T == NULL) {
    T = (Bitree) malloc (sizeof(tree));
    T->data = x;         T->height = 0;
    T->lchild = T->rchild = NULL;
    }

else if(x < T->data) {
    T->lchild = Insert(T->lchild, x);
    if(Height(T->lchild) - Height(T->rchild) == 2) {
    if(x < T->lchild->data)
        T = Right(T);                      //在左边左儿子插入元素ll此时右旋
    if(x > T->lchild->data)
        T = Left_Right(T);                //在左边右儿子的插入元素lr此时左右旋
    }
  }

else if(x > T->data) {
    T->rchild = Insert(T->rchild, x);
    if(Height(T->rchild) - Height(T->lchild) == 2) {
        printf("\n%d %d\n", Height(T->rchild), Height(T->lchild));
        printf("ok\n");
    if(x > T->rchild->data)
        T = Left(T);                       //在右边右儿子插入元素rr此时左旋
    if(x < T->lchild->data)
        T = Right_Left(T);                //在右边左儿子插入元素rl此时右左旋
     }
  }

T->height = Max(Height(T->lchild), Height(T->rchild)) + 1;
    return T;
}

void
preoder(Bitree T)
{
    if(T) {
        printf("data == %d\n", T->data);
        preoder(T->lchild);
        printf("\n");
        preoder(T->rchild);
    }
}

int main(int argc, char const *argv[]) {
#ifndef _OJ_ //ONLINE JUDGE
       freopen("input.txt", "r", stdin);
       //freopen("output.txt", "w", stdout);
#endif
    
    int a[100];
    int n;
    Bitree T = NULL;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);    printf("%d ", a[i]);
        T = Insert(T, a[i]);
    }
    preoder(T);
    // printf("%d\n", Height(T));
    // printf("%d\n", T->data);

return 0;
}

转载于:https://www.cnblogs.com/airfand/p/5082410.html

数据结构(复习)--------关于平衡二叉树(转载)相关推荐

  1. [转]C#与数据结构--树论--平衡二叉树(AVL Tree)

    C#与数据结构--树论--平衡二叉树(AVL Tree) http://www.cnblogs.com/abatei/archive/2008/11/17/1335031.html 介绍 我们知道在二 ...

  2. 数据结构分类概述【转载】

    文章目录 数据结构类别 1 数组 2 栈 3 队列 4 链表 5 树 6 散列表 7 堆 8 图 数据结构类别 数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成 ...

  3. 【数据结构复习】二叉树的遍历——从微软2014校园招聘说起

    [数据结构复习]是学习.复习常用数据结构系列文章.数据结构与算法密不可分,是程序实现功能的重要组成部分.优秀的数据结构可以提高算法的时间及空间效率.反之,将增加算法复杂度,甚至妨碍程序的正确执行. 一 ...

  4. 南京邮电大学计算机网络专硕,南京邮电大学计算机专硕数据结构复习经验分享...

    南京邮电大学计算机专硕数据结构复习经验分享 今年数学考砸了,考完了一直没想碰考研的任何东西.今天不知道怎么的突然觉得还是有必要给后人留下点 作者 magicls 次阅读 2014-01-28 今年数学 ...

  5. 数据结构课程设计--平衡二叉树

    大二的最后一个作业,等明天再过去答辩完后,我的大二也就基本告一段落.这次的课设没有怎么用心,所以也基本就是应付式的完成的,不过其中还是有挺多东西可以学的,因此就趁着刚写完,认真整理一下,方便以后学习. ...

  6. KPM算法——数据结构|复习局|串|复杂模式匹配算法|二维数组解决KPM

    数据结构复习局--KPM算法 何为KPM? 事先规则 状态 匹配 dp--状态转移图 状态X 获得dp数组值 看看图再理解下 写在前面: 本文仅为作者个人学习记录,详细具体内容参考自知乎大佬labul ...

  7. 数据结构复习-基础、线性表、栈、队列、串

    数据结构复习笔记 作者: 收集于网络 第一章:概论 数据:指所有能被输入到计算机中,且能够被计算机识别.存储和加工处理的信息的载体,是计算机操作的对象的总称. 数据元素:数据的基本单位,有时一个数据元 ...

  8. CAUC算法与数据结构复习基本代码(一)

    算法与数据结构复习基本代码(一) 线性表 线性表 顺序表 单链表 双链表 栈 栈 顺序栈 链式栈 表达式求值 队列 顺序队列 链式队列 字符串 线性表 线性表 线性表的抽象数据类型定义 templat ...

  9. HIT 数据结构复习

    一些奇怪的概念 四种逻辑结构:集合结构.线性结构.树结构.图结构 存储结构:顺序存储.链式存储.索引(index)存储.散列(Hash)存储 坑! Q:为什么说线索二叉树是一种物理结构(存储结构)? ...

  10. 计算机考研的意义,2019计算机考研数据结构复习:重要意义

    2019考研复习正在紧张的进行中,为了更好的帮助同学们学习.新东方在线为大家整理了"2019计算机考研数据结构复习:重要意义"的相关信息,提醒各位考生要合理安排复习时间,做好复习规 ...

最新文章

  1. Interview QA - 房间里有三盏灯,屋里有三个开关分别控制这三盏灯。如何只进入房间一次就知道哪一个开关控制哪一盏灯?
  2. Java 离 Linux 内核有多远?
  3. 老男孩Linux运维第41期20171016第六周学习重点课堂记录
  4. Spark on Yarn:任务提交参数配置
  5. java datarow 使用_DataRow中的链接(数据表)
  6. linux的usermod命令参数,linux usermod命令参数及用法详解
  7. iOS开发之各种动画各种页面切面效果
  8. 计算机专业岗位细分及学习必备清单
  9. 招聘 | 语言资源高精尖创新中心研发人员招聘启事
  10. 小程序的开发框架MINA及小程序的启动机制
  11. 使用猴子测试工具(1)
  12. 最好用的音轨分离软件spleeter:处理一首歌仅几秒,上线一周收获2.4k星 | 附实测...
  13. python切片详解_python切片及sys.argv[]用法详解
  14. 微信卡券会员卡跳转外链php,微信卡券跳转小程序遇到的小坑
  15. form表单提交的方法
  16. outlook一直显示尝试连接服务器,OUTLOOK EXPRESS老是连接不上服务器怎么办?
  17. 卖二手书的多抓鱼是如何逆袭上位的!
  18. 超级详细的CentOS8.2搭建个人网站(WordPress)
  19. AJAX和JSON介绍
  20. 智能洗地机好不好用、洗地机和扫地机哪个实用?十大洗地机测评

热门文章

  1. 电脑小写字母怎么切换_电脑上也可以使用九州音集啦!
  2. 分布式的客户端-服务器结构_MMORPG游戏服务器技术选型参考
  3. pads layout 中 hatch和flood之区别
  4. Enum in C#
  5. 记录: 开发中的2个线程的使用问题
  6. python3(十)pickle库
  7. python3(三)Matplotlib
  8. ai模型_这就是AI的样子:用于回答问题的BiDAF模型
  9. 一台7纳米光刻机月产能有多大,何为能卖上亿美元?
  10. SDCC 2016数据库峰会(深圳站)学习笔记