(十四)数据结构之平衡二叉树

利用平衡二叉树进行动态查找

1.什么是平衡二叉树

平衡二叉树也是树的数据结构中很重要的一个组成,底部实现方面,采用二叉链表来存储二叉树;节点插入删除方面,特色是会根据当前状况调整二叉树的形态使咱们的二叉树始终知足必定的要求和规则,大大提升了二叉树应用的广泛性和查找效率。ios

2.与二叉排序树的关系

在上一篇博客中(数据结构之二叉排序树)咱们就曾提到过平衡二叉树,那么这二者有什么区别和联系?
1.联系:二者间最直接的关系就是平衡二叉树是二叉排序树的改进,或者说升级版。咱们也提到过利用二叉排序进行动态查找时若是咱们输入单调不减或者单调不增的数据那么二叉排序树此时就会退化成单支树(单链树,没有分支出现),这是咱们查找的效率下降到了O(n),显然不是咱们但愿的结果。所以为了防止这种单支树的出现,诞生了平衡二叉树。
2.区别:最直接的区别就是二叉排序树在动态查找的过程当中不须要对树的形态进行调整,而平衡二叉树则须要在插入或者删除的过程当中进行调整,使其维持咱们所定的规则。
说了好几回规则那么平衡二叉树的规则究竟是什么?
首先咱们要了解什么是平衡因子,c++

对于一个节点,它的平衡因子等于其左子树深度减去其右子树深度(也能够右减左,这里咱们约定是左减右)web

因此平衡二叉树的规则就是,算法

对于树中的任意一个节点,其平衡因子的绝对值不能超过1,也就是说
平衡因子的值只能是0,1,和-1。若是不知足则须要咱们设计程序对其进行调整使之知足规则数据结构

3.算法设计与分析(过程图解)

算法以及过程分析:
咱们只须要协调好几种树的调整方式便可,因为做图讨论比较麻烦,所以我花了一点时间花了张图罗列了一下全部的状况,这样你们看起来也方便清晰一点(还配了一些文字说明,字丑勿见怪)
illustrate
只要按着这些状况分别处理一下具体转换状况就能够了。
(其实这些状况两两对称,只要写出其中一边的状况那么另外一边我相信你也可以理解了)
时间复杂度方面:与log n属于同数量级,能够说性能至关不错。svg

4.难点分析

这个程序很短,也不难,可是属实害我搞了好长时间,,主要的难点不是在于怎么调整这个二叉树,由于调整二叉树形态方面确实很简单, 无非就是L型向右旋转,R型向左旋转,LR型先右转后左转,RL型先左转后右转。困难的是对于不一样旋转状况全部节点的平衡因子大小的计算,举个LR型例子,这里不只涉及到根节点的左孩子的平衡因子,在作孩子的平衡因子为-1时还涉及到作孩子的右孩子的平衡因子大小,就显得比较复杂。这里咱们须要作的是把相应状况在纸上罗列出来挨个处理,画图理解,才能比较快的理解到精髓。
一开始我发现了困难而后打开书籍进行对照才发现书上写的和个人想法些许不一样,致使我一度怀疑书上是否存在错误,遗憾的是很显然是我错了,而且想找到这种教科书的错误很显然也是至关不容易的一件事。
总之,重点就是:画图+理解。性能

5.代码实现

下面贴上代码:学习

#include <iostream>
#include <process.h>
using namespace std;
//------------------------平衡二叉树的创建和基于此的动态查找----------------------//
//===============================================================================
typedef struct BTNode { //二叉树结点的结构体设计
    int bf; //balance factor
    int val; //value
    BTNode *lchild,
           *rchild;
    BTNode(int x) : val(x) {bf = 0; lchild = rchild = nullptr;}
} *pBTNode;
//===============================================================================
//以root为根节点向右旋转
void turnRight(pBTNode &root) {
    pBTNode tmp = root->lchild;
    root->lchild = tmp->rchild; //注意到这里容易遗漏状况
    tmp->rchild = root; root = tmp;
}
//以root为根节点向左旋转
void turnLeft(pBTNode &root) {
    pBTNode tmp = root->rchild;
    root->rchild = tmp->lchild;
    tmp->lchild = root; root = tmp;
}

void leftBalance(pBTNode &root) {
    pBTNode tmp = root->lchild, tmp_2;
    switch(tmp->bf) {
        case 1: root->bf = tmp->bf = 0; turnRight(root); break; //situation 1
        case -1:
            tmp_2 = tmp->rchild; //这就是我所说的须要考虑得更多的状况
            switch(tmp_2->bf) {
                case -1: root->bf = 0; tmp->bf = 1;  break;
                case 0:  root->bf = tmp->bf = 0;     break;
                case 1:  root->bf = -1; tmp->bf = 0; break;
            }
            tmp_2->bf = 0; turnLeft(tmp); turnRight(root);
    }
}
void rightBalance(pBTNode &root) {
    pBTNode tmp = root->rchild, tmp_2;
    switch(tmp->bf) {
        case -1: root->bf = tmp->bf = 0; turnLeft(root); break;
        case 1:
            tmp_2 = tmp->lchild;
            switch(tmp_2->bf) {
                case 0:  root->bf = tmp->bf = 0;     break;
                case 1:  root->bf = 0; tmp->bf = -1; break;
                case -1: root->bf = 1; tmp->bf = 0;  break;
            }
            tmp_2->bf = 0; turnRight(tmp); turnLeft(root);
    }
}
// insert or search the Binary Tree
int OperateBalanceBinaryTree(pBTNode *root, int target, bool &deepthIncrease) {
    if (!*root) {*root = new BTNode(target); deepthIncrease = true; return 0;} //ok

    if (target == (*root)->val) {deepthIncrease = false; return -1;} //insert fail
    if (target < (*root)->val) {
        if (OperateBalanceBinaryTree(&(*root)->lchild, target, deepthIncrease)) return -1; //insert fail
        if (deepthIncrease) 
            switch((*root)->bf) {
                case 0:  (*root)->bf = 1;    deepthIncrease = true;  break;
                case -1: (*root)->bf = 0;    deepthIncrease = false; break;
                case 1:  leftBalance(*root); deepthIncrease = false; break;
                default: cout << stderr << "error!"; system("pause");
            }
    }
    else {
        if (OperateBalanceBinaryTree(&(*root)->rchild, target, deepthIncrease)) return -1; //insert fail
        if (deepthIncrease)
            switch((*root)->bf) {
                case 0:  (*root)->bf = -1;    deepthIncrease = true;  break;
                case -1: rightBalance(*root); deepthIncrease = false; break;
                case 1:  (*root)->bf = 0;     deepthIncrease = false; break;
                default: cout << stderr << "error!"; system("pause");
            }
    }
    return 0; //ok
}

int main()
{
    pBTNode root = nullptr;
    int val; char buff; bool deepthIncrease;

    cout << "请输入要插入的数据:" << endl;

    do {
        cin >> val >> noskipws >> buff; //输入数据

        if (!OperateBalanceBinaryTree(&root, val, deepthIncrease)) cout << "该元素不存在,插入成功!" << endl;
        else                                                       cout << "该元素已存在,插入失败!" << endl;
        if (buff == '#') {/* 销毁平衡二叉树 */break;} //销毁二叉树的过程就不写了
    } while (buff != '#' && !(deepthIncrease = false));

    cout << "已销毁平衡二叉树并退出程序!" << endl; system("pause");

    return 0;
}

6.运行截图

runtime
能够看到程序成功运行并完成相应的功能,大功告成!spa

7.题外话

平衡二叉树是二叉排序树的升级版,那么平衡二叉树有没有升级版呢?确定是有的,那就是红黑平衡二叉树,而且令我印象十分深入的是咱们的课程设计项目就是选择了基于红黑平衡二叉树的动态查找。而且红黑平衡二叉树的效率比平衡二叉树更高,由于它不须要频繁的对二叉树进行调整。而且在c++ STL得map容器中就是内置了一个红黑平衡二叉树,效率是至关的高。具体细节这里不予讨论。.net

8.结束语

仍是跟之前同样的问题:轻敌。本来觉得已经学习过平衡二叉树的各类旋转原理就能绝不费力地写出来这个程序,谁知道困难重重,甚至耗费了我几天的时间来完成(加上最近在准备开校的网课事宜,就耽搁了比较长的时间)。最后还想说,网课就要开始了,必需要督促本身尽心尽力的学习,汇编,编译原理,算法设计与分析,数学建模都是我所向往而且很喜欢的课程,必定要好好学,及时总结。
结尾箴言:我永远不会被打倒,除非我本身倒下。