这篇文章主要是为了温习下平衡二叉树,同时添加了树型打印的接口,让平衡二叉树的添加和删除更容易理解。

接下来的篇幅很长,需要有很多的耐心,做好了准备接下来往下看吧。

通俗的来说:

二叉树就是节点度最大为2的树,也就是最多包含两个子树,左子树和右子树,包含了空树。

二叉排序树(Binary Sort Tree)是在二叉树的基础上进行了规整,满足以下规则:

    1)节点的值是能够进行大小比较的。

    2)如果节点的左子树不为空,则左子树上的节点都比该节点小。

    3)如果节点的右子树不为空,则右子树上的节点都比该节点大。

    4)节点的左、右子树同时也是二叉排序树。

 

看完规则,那满足二叉排序树的树有很多,如下列图:

                  

     图1                 图2

                   

        图3                                                 图4

上面的图都满足二叉排序树,左边的节点都比右边的节点小,我们知道二分查找的最好时间复杂度为O(log2n),而二叉排序树的查找类似于二分查找,要么找左边,要么找右边。

在上述的图中,图1中如果要寻找节点5,则必须将整个树全部遍历一遍,才能比较找到5这个节点,图2花的步数会少一些,图3和图4就花费的步数就更少了。

所以为了让二叉排序树也能像二分查找一样,保持接近于最好的时间复杂度O(log2n),则二叉排序树的深度越少越好,这样就有了平衡二叉树(AVL)。

 

平衡二叉树其实就是所有节点的左右子树高度相差不超过1的二叉排序树,包含空树。

所以平衡二叉树是要满足二叉排序树的规则的,所以平衡二叉树只是二叉排序树的一个约定的特例,就比如皇帝柑之于柑橘,只是好吃了一点而已。

左右子树的高度差都不超过1,没有要求是左边高还是右边高,那就存在以下三种情况:

    注:(有的以高度为0开始,这里高度以1开始,也就是空节点高度为0,一个节点的高度为1)

    如果一样高则高度一致,所以高度差为0;

    如果左边高则左边比右边高,满足平衡二叉树高度不超过1,我们把高度差记为1(左子树高度减去右子树高度);

              如果右边高则右边比左边高,满足平衡二叉树高度不超过1,我们把高度差记为-1(左子树高度减去右子树高度);

       这就是平衡因子的概念,平衡因子是相对于每一个节点而言的,概括就是左高减右高,如果要求出平衡因子,那么肯定是某个节点的平衡因子,求解得出来的平衡因子也是相对

某一个节点而言的。接下来我们讨论下这三种情况,并且解释下平衡因子的标识,以下三种情况以高度差来表示平衡因子。

  1)两边一样高,也就是高度差为0,如下图

                                     

      图5                            图6

               在图5和图6中,

    首先看下图5,单个节点构成一棵二叉树,只有根节点,根节点没有左节点和右节点,所以图5中节点高度差为0。记录为下图

    

 

    再看图6,首先根节点有了两个子节点,也可以称为子树。首先1节点和3节点属于叶子节点(树上度为0的节点,没有左右子树,长得和图5中的节点很像),

     所以1节点和3节点的高度差为0。

    

 

    再来看2节点,2节点左边有一个1节点,所以左边的高度为1,同理,右边的3节点的高度也为1,高度差为两边

          高度差,1-1=0。

    

 

  2)左边高,也就是左边的高度比右边高1,如下图

                                  

                    图7                                 图8

       先看图7,图中有两个节点,1节点属于叶子节点,没有左右子树,1节点的高度差为0,然后看2节点,2节点左边有一个1节点,左边高度为1,

    右边是没有节点的,高度为0,所以高度差为1-0=1。

    

 

    在图7的基础上补上节点3和4形成图8,节点1和4属于叶子节点,高度差为0, 2节点高度差为1,我们先标上已知的高度差

    

 

    然后看下3节点,左边子树包含了1节点和2节点,高度为2,右边只有一个4节点,高度为1,所以高度差为2-1=1,所以图中

    节点2和节点3的左子树都比右子树高1。

              

  3)右边高,也就是右边的高度比左边高1,但是按照平衡因子计算方法,左高减右高,所以高度差是-1,如下图

                               

                        图9                                       图10

     在图9中,我们根据前面查看的示例,可以发现右边肯定是比左边高的,还是来标高度差,首先1节点和4节点不用说了

    属于叶子节点,所以1节点和4节点的高度差为0,还要补充的是,我们查看高度差,采取从我们理解的下到上来标识,先叶子节点,

    然后逐层上升。

    

     然后3节点的高度,左边高度为0,右边有一个4节点,所以高度差为0-1=-1,同理2节点的左边子树高度为1,右边高度为2,所以

    高度差为1-2=-1。

               

    讨论完这三种情况,我们找一个平衡二叉树来标识平衡因子,下面还是按照平衡因子来说,我们来标识图11的平衡因子

               

                                      图11

     标完的图如下:

                        

                     可能-1的标识不是很清晰,看框的大小吧,-1的框比1的框要大一些的。

 

说完平衡因子,接下来说下二叉排序树,为什么说这个呢,因为平衡二叉树也是属于二叉排序树,只是对高度做了限制,所以平衡二叉树的添加和删除都是和二叉排序树一样的

只是当添加和删除了节点之后,还是二叉排序树,但是可能不满足平衡二叉树的要求,所以就会有后续的旋转啥的。先说二叉排序树的添加和删除,以添加起头。

 

二叉排序树添加节点

首先最先加入的节点肯定是根节点,我们以上面的图11为例,我们插入的顺序定为{5,2,1,4,3,6,7},先插入5节点,

插入2节点,先和5进行比较,按照二叉排序树的要求,比节点小,就往左走,比节点大,就往右走,那么和节点一样大呢

那就不要了,不加入二叉排序树了。因为2比5小,所以往左找,发现没有左节点,2节点就作为5的左节点。

 

插入1节点,比5小,往左,比2小,再往左,没找到节点就添加

 

插入4节点,比5小,往左走,比2大,往右走,没找到节点就添加

 

插入3节点,比5小,往左走,比2大,往右走,比4小,往左走,没找到节点就添加

 

插入6节点,比5大,往右走,没找到节点就添加

 

插入7节点,比5大,往右走,比6大,往右走,没找到节点就添加

 

这样就和图11是一致的了,如果我们在图11的基础上再加入一些节点来说明有相同节点如何处理。那么插入队列就改成

{5,2,1,4,3,6,7,9,8,7},加入了后面的9,8,7三个节点,先插入98.

              

 

然后插入7节点,比5大,往右走,比6大,往右走,发现和已存的节点7相等,取消插入,直接丢弃。

所以最后的二叉排序树是

     图12

 

二叉排序树添加的代码

首先二叉树的节点的定义如下:

 1)结构体定义

typedef struct _BiTree
{
    int node_data;//节点值

    int bf;//平衡因子

    struct _BiTree *left;//左子树指针
    struct _BiTree *right;//右子树指针
    _BiTree(int data)
        : node_data(data), bf(0), left(nullptr), right(nullptr)
    {}
}BiTreeType, *BiTreeTypePtr;

2)二叉排序树插入节点

  我们回想之前的插入节点的逻辑:如果树为空,则添加节点为根节点,不为空,则查找后续节点,如果比查找节点小往左找,如果比查找节点大则往右,

  如果存在查找节点等于预插入的节点,则丢弃预插入的节店,如果不存在,直到找到树的最下面,也就是最后找到nullptr,则插入节点。

    插入的代码按照之前画的图的思路,

  1)先判断是不是空树,空树直接添加,

  2)如果不是空树,则要找到合适位置,如果节点存在,则不添加,

  3)反之节点不存在,接着往下找,每一次查找,小于查找节点往左找,大于查找节点往右找,最后添加节点。

bool InsertNode(BiTreeTypePtr &bi_tree, int data)
{
    /*根节点*/
    if (bi_tree == nullptr)
    {
        BiTreeTypePtr node = new BiTreeType(data);
        bi_tree = node;
        return true;
    }
    
    /*在已存的二叉树中查找节点,如果不存在,就找到合适的位置加入节点*/
    BiTreeTypePtr p = nullptr;
    if (!SearchBBST(bi_tree, data, nullptr, p))
    {
        BiTreeTypePtr node = new BiTreeType(data);
        if (p->node_data > data)
        {
            p->left = node;
        }
        else
        {
            p->right = node;
        }
            
        return true;
    }

    return false;
}

 

 搜索的代码如下:小往左,大往右,找到nullptr表示走到头

bool SearchBBST(BiTreeTypePtr bi_tree, int data, BiTreeTypePtr parent, BiTreeTypePtr &choose_node)
{
    /*找到了最后为空的位置,没有找到节点,记录父节点,方便添加*/    
    if (nullptr == bi_tree)
    {
        choose_node = parent;
        return false;
    }        
    else if (bi_tree->node_data == data)
    {
        choose_node = bi_tree;
        return true;
    }
    else if (bi_tree->node_data > data)
    {
        return SearchBBST(bi_tree->left, data, bi_tree, choose_node);
    }
    else
    {
        return SearchBBST(bi_tree->right, data, bi_tree, choose_node);
    }
}

 

二叉排序树删除节点

插入节点就是在二叉树中找到对应的位置就添加节点,如果节点存在就丢弃节点,插入成功的节点都是叶子节点。接下来是删除节点,删除节点和插入不一样,

删除的节点可能是叶子节点,也有可能不是叶节点,存在左子树或者右子树,又或者两者都有,这样删除节点之后,子树必须接上来,继续组成一个二叉排序树。

当然如果节点不存在,找不到就没得删了。

接下来说删除节点的4个情况:以图12为例

    1)删除的是叶子节点

      比如删除的是1、3、8节点,这几个节点是没有子树的,以删除8节点为例

          

      找到8节点,删除

                  

 

    2)删除的节点存在节点,没有右节点

      比如删除4节点,4节点存在左边的3节点,右边是空的

                        

      

 

 

                   

 

    3)删除的节点存在节点,没有左节点

      比如删除6,7节点,6节点存在右边的7节点 ,  7节点存在右边的9节点,而左边是空的

      以删除7节点为例

                                  

 

             

 

     所以前三种情况中,删除节点之后,都是让节点的左节点或者右节点节点顶上来,叶子节点也是如此,可以认为叶子节点的左右节点

    为空,删除叶子之后,空给了叶子节点的父节点。这时候父节点也变成了叶子节点

      

    4)删除的节点存在节点和节点

      比如删除2节点,2节点存在左边的1节点和右边的3节点,同时存在左子树和右子树的节点删除的时候,会跟前三种情况不一样,

    原则是这样的,如果删除的节点同时存在左子树和右子树,则让该节点的前继节点或者后继节点来代替该节点,然后删除前继节点或者

    后继节点,前继节点:数值比删除节点小并且大小最接近删除节点的值,后继节点:数值比删除节点大并且大小最接近删除节点的值。

    这里以后继节点来演示,首先看如何找后继节点,因为找后继节点,所以是要比节点大,那就往右走,再要找到大小最接近删除节点的值,

              那就是找右边最小的值。找小的值就往左边找。

    那找后继就是往右边找最左边的值。

    那找上图2节点的后继节点先往右,找到了3节点,再往左,发现没有节点了,所以3就是要找的节点

    

    这种情况是在3节点之后没有节点了,那再以其他情况解析下

    

      图13

   比如删除图13中的2节点,先往右找到5,然后往左找,一直找,会找到最左边的3节点

  找到了后继节点,则删除有左子树和右子树的节点的如下图:

            

              

 

 复杂一点的就是:

                      

 

                

 

二叉排序树删除节点的代码

   删除节点需要分4种情况,但是删除叶子节点的情况,跟只有左子树或者右子树的逻辑一致,可以按照后者来理解,也就是删除叶子节点的时候

     将叶子节点的左子树或者右子树给父节点,反正最后都会成为nullptr。

  1)首先要找到删除的节点,找不到都是扯淡,找节点跟添加时候的搜索是一样的,都满足一样的规则,小往左,大往右

  2)判断左子树是否为空,如果为空,标记父节点,将指向父节点的指针指向右子树,这时候可以想象,如果右子树为空,相当于删除的是叶子节点

   如果右子树不为空,则就是上面图中右子树顶上来的情况

/*左为空,删除父节点,右顶上*/
BiTreeTypePtr tmp;
if (nullptr == bi_tree->left)
{
    tmp = bi_tree;
    bi_tree = bi_tree->right;
    delete tmp;
    tmp = nullptr;
}

  3)同理,右边的处理和左边一致,右边为空,左边不为空

/*右为空,删除父节点,左顶上*/
BiTreeTypePtr tmp;
if (nullptr == bi_tree->right)
{
    tmp = bi_tree;
    bi_tree = bi_tree->left;
    delete tmp;
   tmp = nullptr; }

  4)左子树和右子树都不为空的情况:找后继,右边找最小的值。后继节点替换删除节点,删除后继节点

/*先往右找大的节点*/
BiTreeTypePtr p = bi_tree->right;
BiTreeTypePtr tmp = p;
/*往左找最接近的值,后继值*/
while (nullptr != p->left)
{
    tmp = p;
    p = p->left;
}

/*后继节点的值给删除节点,然后后继节点的父节点左子树指向后继节点的右子树*/
bi_tree->node_data = p->node_data;
tmp->left = p->right;
delete p;
p = nullptr;

其中有一句tmp->left = p->right;

这句的意思是后继节点的父节点指向后继节点的右子树,因为在此刻后继节点是找到的最左边的值,

是肯定没有左子树的,但是可能有右子树,这时候删除后继节点,右子树得接上父节点。

所以上面while中tmp = p;就是在保存后继节点的父节点。

在前面的图中

      

上两图都是没有右子树的,加上代码中是找到最左边的值,所以后继节点是没有左子树的,所以这种情况就相当于节点的子树都没有,等同于叶子节点,删了就直接删了

但是如果有右子树的情况呢,我们加上一个3.5的点,在3节点替换了删除节点,删除3节点,那么3.5节点就需要接上3的父节点,不然后面的

数据就全丢了,因为后面的节点是属于3的节点,不管多大,都应该是比3的父节点小的,所以后面的所有节点都应该放在4的左子树上。

        

 

删除的代码总结起来如下:

bool DeleteNode(BiTreeTypePtr &bi_tree, int data)
{
    if (nullptr == bi_tree)
    {
        return false;
    }
    else if (bi_tree->node_data > data)
    {
        return DeleteNode(bi_tree->left, data);
    }
    else if (bi_tree->node_data < data)
    {
        return DeleteNode(bi_tree->right, data);
    }
    else
    {
        /*左为空,删除父节点,右顶上*/
        BiTreeTypePtr tmp;
        if (nullptr == bi_tree->right)
        {
            tmp = bi_tree;
            bi_tree = bi_tree->left;
            delete tmp;
        }
        else if (nullptr == bi_tree->left)
        {
            /*右为空,删除父节点,左顶上*/
            tmp = bi_tree;
            bi_tree = bi_tree->right;
            delete tmp;
        }
        else
        {
            /*先往右找大的节点*/
            BiTreeTypePtr p = bi_tree->right;
            tmp = p;
            /*往左找最接近的值,后继值*/
            while (nullptr != p->left)
            {
                tmp = p;
                p = p->left;
            }
           
            /*后继节点的值给删除节点,然后后继节点的父节点左子树指向后继节点的右子树*/
            bi_tree->node_data = p->node_data;
            tmp->left = p->right;
            delete p;
        }
        
        return true;
    }
} 

这时候可能有疑问,说添加节点为什么不直接搞成递归,还准们搞了一个搜索接口在里面,我的回答是完全可以的。

那么二叉排序树的搜索、添加、删除的代码综合如下:

先补上makefile,然后弄上代码,本人懒了一下,所有的验证代码都写在了构造函数中,没有写在main函数中。

如果对makefile不了解,在linux下创建一个文件,命名makefile,拷贝下面的makefile中的内容到创建makefile中,然后将代码

拷贝到main.cpp中,直接敲命令make就可以进行编译

注:如果linux中gcc版本过低,就是通过gcc -v查看到的版本发现低于4.7的,可以按如下方式做

  1)将makefile中-std=c++11删除

  2)将nullptr改为NULL

  3)to_string可以用如下代码代替

#pragma once

#include <string>
#include <sstream>

using std::string;
using std::stringstream;

template <typename ValType>
string ToString(ValType val)
{
    stringstream ss;
    ss << val;
    return ss.str();
}

makefile代码如下:

CC := g++

EXEC = bbst_test

RELEASE = ./

INCLUDES = -I. \

CFLAGS = $(INCLUDES) -Wall -O3 -std=c++11

LIBS = -L$(RELEASE)

CPPPATH = ./main.cpp

$(EXEC):
        $(CC) -o $(EXEC) $(CFLAGS) $(CPPPATH) $(LIBS)

clean:
        rm -rf *.o $(EXEC)
makefile
#include <string>
#include <iostream>
#include <cstdio>
#include <map>

using std::string;
using std::cout;
using std::endl;
using std::map;
using std::to_string;

typedef struct _BiTree
{
    int node_data;

    int bf;

    int height;

    int position;
    int splice;

    struct _BiTree *left;
    struct _BiTree *right;
    _BiTree(int data)
        : node_data(data), bf(0), height(0),
        position(0), splice(0), left(nullptr), right(nullptr)
    {}
}BiTreeType, *BiTreeTypePtr;

class BBST
{
public:
    BBST()
    {
        int arr[] = {49, 38, 65, 97, 76, 13, 27, 50};
//        int arr[] = {2,1,0,3,4,5,6,9,8,7};

        m_bi_tree_depth = 0;
        m_root_ptr = nullptr;
        for (unsigned i=0; i<sizeof(arr)/sizeof(arr[0]); ++i)
        {
            InsertNode(m_root_ptr, arr[i]);
        }

        PromptExistBiTree();

    }
    
    ~BBST()
    {
        Destory(m_root_ptr);
    }

private:

    void Destory(BiTreeTypePtr &bi_tree)
    {
        if (bi_tree)
        {
            Destory(bi_tree->left);
            Destory(bi_tree->right);

            delete bi_tree;
            bi_tree = nullptr;
        }
    }
    

    bool SearchBBST(BiTreeTypePtr bi_tree, int data, BiTreeTypePtr parent, BiTreeTypePtr &choose_node)
    {
        if (nullptr == bi_tree)
        {
            /*找到了最后为空的位置,没有找到节点,记录父节点,方便添加*/
            choose_node = parent;
            return false;
        }        
        else if (bi_tree->node_data == data)
        {
            choose_node = bi_tree;
            return true;
        }
        else if (bi_tree->node_data > data)
        {
            return SearchBBST(bi_tree->left, data, bi_tree, choose_node);
        }
        else
        {
            return SearchBBST(bi_tree->right, data, bi_tree, choose_node);
        }
    }

    bool InsertNode(BiTreeTypePtr &bi_tree, int data)
    {
        /*根节点*/
        if (bi_tree == nullptr)
        {
            BiTreeTypePtr node = new BiTreeType(data);
            bi_tree = node;
            return true;
        }

        /*在已存的二叉树中查找节点,如果不存在,就找到合适的位置加入节点*/
        BiTreeTypePtr p = nullptr;
        if (!SearchBBST(bi_tree, data, nullptr, p))
        {
            BiTreeTypePtr node = new BiTreeType(data);
            if (p->node_data > data)
            {
                p->left = node;
            }
            else
            {
                p->right = node;
            }
            
            return true;
        }

        return false;
    }

    bool DeleteNode(BiTreeTypePtr &bi_tree, int data)
    {
        if (nullptr == bi_tree)
        {
            return false;
        }
        else if (bi_tree->node_data > data)
        {
            return DeleteNode(bi_tree->left, data);
        }
        else if (bi_tree->node_data < data)
        {
            return DeleteNode(bi_tree->right, data);
        }
        else
        {
            /*左为空,删除父节点,右顶上*/
            BiTreeTypePtr tmp;
            if (nullptr == bi_tree->right)
            {
                tmp = bi_tree;
                bi_tree = bi_tree->left;
                delete tmp;
            }
            else if (nullptr == bi_tree->left)
            {
                /*右为空,删除父节点,左顶上*/
                tmp = bi_tree;
                bi_tree = bi_tree->right;
                delete tmp;
            }
            else
            {
                /*先往右找大的节点*/
                BiTreeTypePtr p = bi_tree->right;
                tmp = p;
                /*往左找最接近的值,后继值*/
                while (nullptr != p->left)
                {
                    tmp = p;
                    p = p->left;
                }

                /*后继节点的值给删除节点,然后后继节点的父节点左子树指向后继节点的右子树*/
                bi_tree->node_data = p->node_data;
                tmp->left = p->right;
                delete p;
            }
            
            return true;
        }
    }

    void PromptExistBiTree()
    {
        m_level_str_map.clear();
        m_bi_tree_depth = 0;
        MarkNodeHeight(m_root_ptr, 0);
        MarkBinaryTree(m_root_ptr, GetRootPosition(m_bi_tree_depth));
        SpliceBiTreeStr(m_root_ptr);

        PaintBiTree();
    }

    void MarkNodeHeight(BiTreeTypePtr &bi_tree, int height)
    {
        if (bi_tree == nullptr)
        {
            return;
        }
        
        bi_tree->height = height;
        if (height > m_bi_tree_depth)
        {
            m_bi_tree_depth = height;
        }

        if (bi_tree->left != nullptr)
        {
            MarkNodeHeight(bi_tree->left, height + 1);
        }

        if (bi_tree->right != nullptr)
        {
            MarkNodeHeight(bi_tree->right, height + 1);
        }
    }

    void MarkBinaryTree(BiTreeTypePtr &bi_tree, int position)
    {
        if (bi_tree == nullptr)
        {
            return;
        }
        
        bi_tree->position = position;
        bi_tree->splice = GetRootSplice(m_bi_tree_depth - bi_tree->height);
    
//        cout << bi_tree->height << ":" << bi_tree->splice << ":" << bi_tree->position << ":" << bi_tree->data << endl;

        if (bi_tree->left != nullptr)
        {
            MarkBinaryTree(bi_tree->left, position - GetRootPosition(m_bi_tree_depth - bi_tree->height - 1) - 1);
        }

        if (bi_tree->right != nullptr)
        {
            MarkBinaryTree(bi_tree->right, position + GetRootPosition(m_bi_tree_depth - bi_tree->height - 1) + 1);
        }
    }

    void SpliceBiTreeStr(BiTreeTypePtr &bi_tree)
    {
        if (bi_tree != nullptr)
        {
            string p_str = FindLevelStr(bi_tree->splice);

            if (!p_str.empty())    
            {                
                p_str.append(bi_tree->position - p_str.size(), ' ');
            }
            else
            {            
                p_str.append(bi_tree->position, ' ');
            }

            p_str.append(to_string(bi_tree->node_data));                
            m_level_str_map[bi_tree->splice] = p_str;
            
#if 1
            if (bi_tree->height != m_bi_tree_depth)
            {
                int brance_splice = GetRootPosition(m_bi_tree_depth - bi_tree->height - 1);
                for (int i=1; i<=brance_splice; ++i)
                {
                    string brance_str = FindLevelStr(bi_tree->splice - i);
                    
                    if (!brance_str.empty())
                    {
                        brance_str.append(bi_tree->position - brance_str.size() - i, ' ');    
                    }
                    else
                    {
                        brance_str.append(bi_tree->position - i, ' ');
                    }

                    if (bi_tree->left != nullptr)
                    {
                        brance_str.append("/");
                    }
                    else
                    {
                        brance_str.append(" ");
                    }


                    if (bi_tree->right != nullptr)
                    {
                        brance_str.append(i*2 - 1, ' ');
                        brance_str.append("\\");
                    }
                                            
                    m_level_str_map[bi_tree->splice - i] = brance_str;
                }
            }
#endif
            if (bi_tree->left != nullptr)
            {
                SpliceBiTreeStr(bi_tree->left);
            }

            if (bi_tree->right != nullptr)
            {
                SpliceBiTreeStr(bi_tree->right);
            }
        }
    }

    void PaintBiTree()
    {
        int splice = -1;
        while ((splice = SelectLowestNode()) != -1)
        {
            cout << m_level_str_map[splice] << endl;
            m_level_str_map.erase(splice);
        }
    }

    int SelectLowestNode()
    {
        int mininal_num = -1;
        for (auto map_pair : m_level_str_map)
        {
            if (map_pair.first > mininal_num)
            {
                mininal_num = map_pair.first;
            }
        }

        return mininal_num;
    }

    int GetRootPosition(int height)
    {
        return abs(GetPrintSide(height, 1) * 3 - 1);
    }

    int GetRootSplice(int height)
    {
        int splice_num = GetPrintSide(height, 1) * 3 - 1;
        return (splice_num > 0) ? splice_num : 0;
    }

    int GetPrintSide(int height, int side)
    {
        if (height <= 0)
        {
            return 0;
        }
        else if (height == 1)
        {
            return side;
        }
        else
        {
            return GetPrintSide(height-1, side*2);
        }
    }

    string FindLevelStr(int splice)
    {
        string tmp_str;
        for (auto node : m_level_str_map)
        {
            if (node.first == splice)
            {
                return node.second;
            }
        }

        return tmp_str;
    }

private:
    BiTreeTypePtr m_root_ptr;

    int m_bi_tree_depth;

    map<int, string> m_level_str_map;
};

int main()
{
    BBST bb_st;
    
    return 0;
}
二叉排序树search、insert、delete

 代码中的PromptExistBiTree为打印接口,会以树形打印数据,可以在你想调试的代码的位置打印数据,比如可以放在插入接口中

打印逻辑如下图,可以对着代码看

 

运行完的结果应该如下:

 

平衡二叉树

接下来就是平衡二叉树的操作了,前面的理解清楚再来看最好了

我们拿前面结果中的二叉排序树来标记平衡因子

           

        

可以看到38这个节点的平衡因子已经不再有效范围内了,高度差已经达到2了。所以为了让二叉排序树能够变成平衡二叉树,就需要

将38的平衡因子降到有效范围之内(-1、0、1),那么就是接下来说的事情------旋转

旋转的4种情况先列举出来:

1)单次右旋转,(找左,右变左,父变右),

 先标下图的平衡因子

             

那么平衡因子右问题的是3节点,那么找左就是找问题节点3的左节点,因为左边高,所以肯定是存在左节点的。

找到左子节点为2,然后右变左是子节点的右子树变成父节点的左子树,最后父变右就是父节点变成子节点的右节点。

在变动完成后,调整平衡因子就可以了

用图来演示就是:

  I.找左

    

      II.右变左

   因为2的右节点为空,所以将3的左节点置空  

       

  III.父变右

  

   父节点变成右节点,也就是3节点变成2节点的右节点,

  重新调整下平衡因子

       

 

接下来还是演示一下存在右节点的的操作

         

      4节点的平衡因子为2,不属于有效范围,右旋转,首先找左

  

  右变左,2的右节点3变成父节点4的左节点

      

     父变右

   

  重新标识平衡因子

  

 

可以看出来,当平衡因子为正的时候,进行右旋转

void R_Rotate(BiTreeTypePtr &bi_tree)
{
    cout << "    right rotate " << endl;
    BiTreeTypePtr tmp = bi_tree->left;//找左
    bi_tree->left = tmp->right;//右变左
    tmp->right = bi_tree;//父变右
    bi_tree = tmp;//节点接上
}

 

 

2)单次左旋转,(找右,左变右,父变左)

 直接图来标识,

            

  

当平衡因子为负的时候,进行坐旋转

子节点存在左节点的情况可以自己验证下,这里就不演示了

void L_Rotate(BiTreeTypePtr &bi_tree)
{
    cout << "    left rotate " << endl;
    BiTreeTypePtr tmp = bi_tree->right;//找右
    bi_tree->right = tmp->left;//左变右
    tmp->left = bi_tree;//父变左
    bi_tree = tmp;//节点接上
}

 

 

3)先右旋后左旋

为什么会有这个旋转呢,先按照单旋转来演示下,如果存在以下情况:

            

按照之前的问题节点的平衡因子-2,为负,左旋转

    

可以看到依旧是不符合要求,再进行右旋转,会发现又变回了最开始的情况,可以大家自己试一试

碰到这种情况,再看看图中的平衡因子,会发现跟单次旋转的不一样,单次旋转的问题节点和子节点的平衡因子

是同符号的,也就是同为正或者同为负。

而刚才的图中问题节点和子节点的平衡因子符号是相反的,互为正负。

这样就需要两次旋转,怎么旋转呢,先看子节点的平衡因子,还是正-右转,负-左转

那么重新以上图来演示

               

这时候先看子节点,3节点的平衡因子为1,则右转,右转的支点为3节点。这时候3变成支点之后,那么右转就要按照右转的规则

           

 

          

 

 

这时候可以发现,这已经跟前面单次左旋转的情况是一样的,负-左转,然后支点变成了之前的父节点1

按照单次左转的操作,大家可以试一下。这样最后得到的结果也是前面单次旋转得到的结果

 

 

4)先左旋后右旋

 还记得前面3)先右旋后左旋的开始,单次旋转无法解决问题的结果,结果如下图

  

可以看出平衡因子互为正负,那么先看子节点的,负-左转,然后父节点正-右转,直接以图来标识

          

 

 

 

可以看到旋转完了之后,就能达到平衡的状态了

 

平衡二叉树添加节点

那接下来就是平衡二叉树添加节点,平衡二叉树添加节点和二叉排序树添加节点都是一样的,还是找合适的位置,

但是平衡二叉树较二叉排序树更严格,所以添加完节点之后还是需要考虑平衡性的。我们还是以二叉排序树用的节点来演示

节点为:{49, 38, 65, 97, 76, 13, 27, 50}

    1)根节点

    

    2) 添加节点38

    

    3)添加节点65

    

    4)添加节点97

    

    5)添加节点76

            平衡因子子节点1,正-右转,父节点-2,负-左转

    

    6)添加节点13

    

    7)添加节点27

    平衡因子子节点-1,负-左转,父节点2,正-右转

    

    8)添加节点50

    

 

最后调整完的样子是这样的,跟二叉排序树完成时候的样子还是差别一些的

那下面开始讲解代码怎么弄

首先,我们可以看到添加节点会引起一连串的变化,从节点到根节点的一条路上的节点的平衡因子都可能发生了改变,因为影响平衡因子的就是子树的高低

所以其他没有发生子树改变,所以不会发生平衡因子改变。而从根节点一路找下来的这条路都会发生子树发生变化,所以这一路的根节点的平衡因子可能会发

生变化。

当我们添加完节点的时候,刚好处于树的最下端,这时候去改变这条路上的节点平衡因子,可以一路按照查找的路径返回回去,这就是递归的好处。

接下来我们要从代码角度讨论,所以先定义三个变量来标识正常的平衡因子

#define LH 1    //左高
#define EH 0    //等高
#define RH -1   //右高

我们分析当添加一个节点,如果父节点之前是EH,则再添加节点高度会增加1层

如果父节点是LH,在右边添加节点,或者父节点是RH,在左边添加节点,则高度不会添加,但是父节点的平衡因子会变成EH

如果父节点是LH,接着在左边添加节点,则LH会接着添加一层,会出现不平衡的状况

同理,如果父节点是RH,接着在右边添加节点,则RH会接着添加一层,会出现不平衡的状态

这时候就需要旋转,旋转是为了让节点的层次减一,变成之前的平衡状态,这时候相当于添加了节点,高度还是没有增加

以图说明问题

  1)父节点是EH,添加左节点或者右节点,高度会增加

  

  

 

  我们可以看到往左边加肯定是左边会高,右边加肯定是右边高

  2)如果父节点不是EH,而是某一边高,然后添加另外一边,保持节点平衡了,高度不变

         

 

        

  3)如果父节点不是EH,是某一边高,然后接着往高的一遍添加

           或者

 

或者,可以看到的是树的高度没有增加,经过旋转了之后,高度是重新跟之前一样了

      或者

  或者  也同上面,高度是没有增加的

 

接下来讨论下平衡因子的变化,首先添加进去的叶子节点的平衡因子肯定是0.

如果没有旋转,平衡因子的变化很好计算,首先叶子节点添加叶子节点,叶子节点的平衡因子由EH变为LH或者RH

如果是已经有左节点或者右节点,再添加另外一边的节点,则将父节点的平衡因子改为EH

如果有旋转,则存下以下的情况,左边高,右旋转,左右旋转

1)右旋转

    

         或者

 

或者

                   

     可以看到添加的节点的平衡因子最后还是0,要么变成了父节点,带两个子节点,要么还是作为叶子节点,但是之前的父节点65

  的平衡因子变为了0,子节点50也变成了0,也就是问题节点和子节点都变成了0,

  这里的插入节点往上是依次累加到问题节点,如果不是累加到问题节点,而是像下面

            

  这样插入节点是经过两个LH的节点了才到问题节点,这样的可以扩展开

  

 

      

  这种类型的经过单次右旋转可达到平衡,最后的平衡因子为

  

  

按照之前的办法,插入1节点,之后回溯,判断平衡因子是否符合,首先插入1节点,回溯到2节点,2节点之前是EH,因为插入了左边节点

所以2节点变成LH,同理3也由EH变成了LH,再回溯到5节点,出现问题,经过右转,比较后两幅图,发现节点5平衡因子变成了0,子节点3的

平衡因子也变成了0,再扩展成更大的同样的树,得出的结论还是一样的。

  所以可以得出这样单次旋转的结论是问题节点和子节点的平衡因子为0

  关于右边高的左旋转,各位可以试试,对称的做法,所以得出的结论还是一致的。

  

  考虑玩了单次旋转,接下来考虑下两次旋转,本节开始已经考虑过了依次累加平衡因子绝对值大小的图(如下)

  

  接下来也考虑下不是一直累加高度到问题节点的

         或者

 

 

 

 或者

通过比较最后两幅图,可以发现最后2、5、8节点都发生了改变,涉及到的改变节点多了一个,并且还插入节点的父节点右关系,就如上图的5节点

为-1和1的情况下,值的改变也不同。

同样有人会问,如果扩充到中间有多个平衡因子不递增,到最后才递增呢,,因为左边高,后续回溯的节点的平衡因子都是1,如下图

这个其实类似前面的,单次右旋转复杂的那一个,所以单次右旋转就可以解决。

所以到现在,可以知道右旋转中右两个特例

 或者

 

说了那么多,右旋转算是完了,其他的都好解决,两次旋转的这两个特例需要记住,现在讲右旋转和左右旋转都归到左平衡,

左边高,所以右旋转嘛

void LeftBlance(BiTreeTypePtr &bi_tree)
{
    BiTreeTypePtr tmp = bi_tree->left;
    switch (tmp->bf)
    {
        case RH:
            {
                cout << "LeftBlance RH lR_Rotate" << endl;
                BiTreeTypePtr p = tmp->right;
                switch (p->bf)
                {
                    case LH://特例中插入节点在左边
                        tmp->bf = EH;
                        bi_tree->bf = RH;
                        break;

                    case RH://特例中插入节点在右边
                        tmp->bf = LH;
                        bi_tree->bf = EH;
                        break;

                    case EH://删除节点的时候走的分支
                        tmp->bf = EH;
                        bi_tree->bf = EH;
                }
                    
                p->bf = EH;
                L_Rotate(bi_tree->left);
                R_Rotate(bi_tree);
                break;
            }

        case LH://单次旋转
            cout << "LeftBlance LH R_Rotate" << endl;
            tmp->bf = bi_tree->bf = EH;
            R_Rotate(bi_tree);
            break;

        case EH://删除节点的时候才走的分支
            cout << "LeftBlance EH R_Rotate" << endl;
            tmp->bf = RH;
            bi_tree->bf = LH;
            R_Rotate(bi_tree);
        }
    }

代码照着图慢慢推导就懂了。

同理右边高的情况和左边的对称,原理是一样的,所以按照之前的方法推到一次就可以得到结果

 2)左旋转

  这里就不啰嗦了,情况还是跟右旋转一样,特例也还是两种,直接上代码,还是按照图推导试试,

  推导的过程不要嫌麻烦,弄清楚了再往后最好,这里直接贴代码

  

void RightBlance(BiTreeTypePtr &bi_tree)
{
    BiTreeTypePtr tmp = bi_tree->right;
    switch (tmp->bf)
    {
        case LH:
            {
                cout << "RightBlance LH RL_Rotate" << endl;
                BiTreeTypePtr p = tmp->left;
                switch (p->bf)
                {
                    case LH://特例中插入节点在左侧
                        tmp->bf = RH;
                        bi_tree->bf = EH;
                        break;

                    case RH://特例中插入节点在右侧
                        tmp->bf = EH;
                        bi_tree->bf = LH;
                        break;

                    case EH://删除节点时候走的分支
                        tmp->bf = EH;
                        bi_tree->bf = EH;
                        break;
                }
                
                p->bf = EH;
                R_Rotate(bi_tree->right);
                L_Rotate(bi_tree);
            
                break;
            }

        case RH://单次左旋转
            cout << "RightBlance RH L_Rotate" << endl;
            tmp->bf = bi_tree->bf = EH;
            L_Rotate(bi_tree);
            break;

        case EH://删除节点时候走的分支
            cout << "RightBlance EH L_Rotate" << endl;
            tmp->bf = LH;
            bi_tree->bf = RH;
            L_Rotate(bi_tree);
            break;
    }
}

 

平衡二叉树删除节点

平衡二叉树删除节点,跟插入节点也是一个对称的过程,加入节点高度增加的情况,删除节点也会导致高度降低,同理

插入节点导致高度不变的情况,删除节点也会导致高度不变,这时候删除节点考虑的是节点的父节点

1)如果父节点的左子树或者右子树为空,只有一侧的子树,将子树删除,父节点的高度会降低

2)如果父节点的左子树和右子树都存在,删除其中一个,另一个还存在,则不会导致高度发生变化

3)需要注意的是,删除节点跟二叉排序树是一样的过程,左右子树同时存在是要找后继节点的。

4)删除玩了节点之后,这时候需要考虑平衡性,我们可以想到的是,如果之前是左边高,右边低,如果还删除右边,并且导致

右边高度降低,这时候肯定是不平衡的了。这个跟哪边高我还死命加节点是一个理。

我们先考虑不是特例的情况,删除导致的右旋转

这时候删除4节点

出现了我们很熟悉的单次右旋转能解决的问题,所以看上面的情况,可以知道,本来是LH,左边高,这时候还去删除右边,导致右边高度降低

这时候出现了不平衡,进行单次右旋转就可以了。复杂扩展就不讨论了,接下来讨论删除出现特例的情况,

 

删除节点9

 就出现了我们很熟悉的特例

特例的平衡跟之前是一样的,先左转,然后右转,也就删除和插入节点最后的图一样,那么平衡的方法肯定是一样的。

 

删除节点的步骤也需要自己结合图多推导,可以看出来主要部分在平衡因子的判断和旋转上,并且记好几个特例

全部代码如下:

  1 #include <string>
  2 #include <iostream>
  3 #include <cstdio>
  4 #include <map>
  5 
  6 using std::string;
  7 using std::cout;
  8 using std::endl;
  9 using std::map;
 10 using std::to_string;
 11 
 12 #define LH 1
 13 #define EH 0
 14 #define RH -1
 15 
 16 typedef struct _BiTree
 17 {
 18     int node_data;
 19 
 20     int bf;
 21 
 22 #if 1//这三个变量是用来打印的
 23     int height;
 24 
 25     int position;
 26     int splice;
 27 #endif
 28 
 29     struct _BiTree *left;
 30     struct _BiTree *right;
 31     _BiTree(int data)
 32         : node_data(data), bf(0), height(0),
 33         position(0), splice(0), left(nullptr), right(nullptr)
 34     {}
 35 }BiTreeType, *BiTreeTypePtr;
 36 
 37 class BBST
 38 {
 39 public:
 40     BBST()
 41     {
 42         int arr[] = {49, 38, 65, 97, 76, 13, 27, 50};
 43 //        int arr[] = {2,1,0,3,4,5,6,9,8,7};
 44         m_bi_tree_depth = 0;
 45         m_root_ptr = nullptr;
 46         for (unsigned i=0; i<sizeof(arr)/sizeof(arr[0]); ++i)
 47         {
 48             bool taller = false;
 49             InsertAVLNode(m_root_ptr, arr[i], taller);
 50         }
 51 
 52 //        for (unsigned i=0; i<sizeof(arr)/sizeof(arr[0]); ++i)
 53 //        {
 54 //            InsertNode(m_root_ptr, arr[i]);
 55 //        }
 56 
 57         PromptExistBiTree();
 58 
 59 #if 0
 60         bool lower = false;
 61         DeleteAVLNode(m_root_ptr, 76, lower);
 62         PromptExistBiTree();
 63 
 64         pre_recursive_order(m_root_ptr);
 65         cout << endl;
 66 
 67         DeleteAVLNode(m_root_ptr, 65, lower);
 68         PromptExistBiTree();
 69 
 70         pre_recursive_order(m_root_ptr);
 71         cout << endl;
 72 
 73         DeleteAVLNode(m_root_ptr, 97, lower);
 74         PromptExistBiTree();
 75 
 76         pre_recursive_order(m_root_ptr);
 77         cout << endl;
 78 
 79         DeleteAVLNode(m_root_ptr, 50, lower);
 80         PromptExistBiTree();
 81 
 82         pre_recursive_order(m_root_ptr);
 83         cout << endl;
 84 #endif
 85     }
 86     
 87     ~BBST()
 88     {
 89         Destory(m_root_ptr);
 90     }
 91 
 92 private:
 93 
 94     void Destory(BiTreeTypePtr &bi_tree)
 95     {
 96         if (bi_tree)
 97         {
 98             Destory(bi_tree->left);
 99             Destory(bi_tree->right);
100 
101             delete bi_tree;
102             bi_tree = nullptr;
103         }
104     }
105     
106 
107     bool SearchBBST(BiTreeTypePtr bi_tree, int data, BiTreeTypePtr parent, BiTreeTypePtr &choose_node)
108     {
109         if (nullptr == bi_tree)
110         {
111             choose_node = parent;
112             return false;
113         }        
114         else if (bi_tree->node_data == data)
115         {
116             choose_node = bi_tree;
117             return true;
118         }
119         else if (bi_tree->node_data > data)
120         {
121             return SearchBBST(bi_tree->left, data, bi_tree, choose_node);
122         }
123         else
124         {
125             return SearchBBST(bi_tree->right, data, bi_tree, choose_node);
126         }
127     }
128 
129     bool InsertNode(BiTreeTypePtr &bi_tree, int data)
130     {
131         if (bi_tree == nullptr)
132         {
133             BiTreeTypePtr node = new BiTreeType(data);
134             bi_tree = node;
135             return true;
136         }
137         
138         BiTreeTypePtr p = nullptr;
139         if (!SearchBBST(bi_tree, data, nullptr, p))
140         {
141             BiTreeTypePtr node = new BiTreeType(data);
142             if (p->node_data > data)
143             {
144                 p->left = node;
145             }
146             else
147             {
148                 p->right = node;
149             }
150             
151             return true;
152         }
153 
154         return false;
155     }
156 
157     bool DeleteNode(BiTreeTypePtr &bi_tree, int data)
158     {
159         if (nullptr == bi_tree)
160         {
161             return false;
162         }
163         else if (bi_tree->node_data > data)
164         {
165             return DeleteNode(bi_tree->left, data);
166         }
167         else if (bi_tree->node_data < data)
168         {
169             return DeleteNode(bi_tree->right, data);
170         }
171         else
172         {
173             BiTreeTypePtr tmp;
174             if (nullptr == bi_tree->right)
175             {
176                 tmp = bi_tree;
177                 bi_tree = bi_tree->left;
178                 delete tmp;
179             }
180             else if (nullptr == bi_tree->left)
181             {
182                 tmp = bi_tree;
183                 bi_tree = bi_tree->right;
184                 delete tmp;
185             }
186             else
187             {
188                 BiTreeTypePtr p = bi_tree->right;
189                 tmp = p;
190                 while (nullptr != p->left)
191                 {
192                     tmp = p;
193                     p = p->left;
194                 }
195 
196                 bi_tree->node_data = p->node_data;
197 
198                 tmp->left = p->right;
199                 delete p;
200             }
201             
202             return true;
203         }
204     }
205 
206     void R_Rotate(BiTreeTypePtr &bi_tree)
207     {
208         cout << "    right rotate " << endl;
209         BiTreeTypePtr tmp = bi_tree->left;
210         bi_tree->left = tmp->right;
211         tmp->right = bi_tree;
212         bi_tree = tmp;
213 
214         PromptExistBiTree();
215     }
216 
217     void L_Rotate(BiTreeTypePtr &bi_tree)
218     {
219         cout << "    left rotate " << endl;
220         BiTreeTypePtr tmp = bi_tree->right;
221         bi_tree->right = tmp->left;
222         tmp->left = bi_tree;
223         bi_tree = tmp;
224 
225         PromptExistBiTree();
226     }
227     
228     void RightBlance(BiTreeTypePtr &bi_tree)
229     {
230         BiTreeTypePtr tmp = bi_tree->right;
231         switch (tmp->bf)
232         {
233             case LH:
234                 {
235                     cout << "RightBlance LH RL_Rotate" << endl;
236                     BiTreeTypePtr p = tmp->left;
237                     switch (p->bf)
238                     {
239                         case LH://特例中插入节点在左侧
240                             tmp->bf = RH;
241                             bi_tree->bf = EH;
242                             break;
243 
244                         case RH://特例中插入节点在右侧
245                             tmp->bf = EH;
246                             bi_tree->bf = LH;
247                             break;
248 
249                         case EH://删除节点时候走的分支
250                             tmp->bf = EH;
251                             bi_tree->bf = EH;
252                             break;
253                     }
254                     
255                     p->bf = EH;
256                     R_Rotate(bi_tree->right);
257                     L_Rotate(bi_tree);
258                 
259                     break;
260                 }
261 
262             case RH://单次左旋转
263                 cout << "RightBlance RH L_Rotate" << endl;
264                 tmp->bf = bi_tree->bf = EH;
265                 L_Rotate(bi_tree);
266                 break;
267 
268             case EH://删除节点时候走的分支
269                 cout << "RightBlance EH L_Rotate" << endl;
270                 tmp->bf = LH;
271                 bi_tree->bf = RH;
272                 L_Rotate(bi_tree);
273                 break;
274         }
275     }
276 
277     void LeftBlance(BiTreeTypePtr &bi_tree)
278     {
279         BiTreeTypePtr tmp = bi_tree->left;
280         switch (tmp->bf)
281         {
282             case RH:
283                 {
284                     cout << "LeftBlance RH lR_Rotate" << endl;
285                     BiTreeTypePtr p = tmp->right;
286                     switch (p->bf)
287                     {
288                         case LH://特例中插入节点在左侧
289                             tmp->bf = EH;
290                             bi_tree->bf = RH;
291                             break;
292 
293                         case RH://特例中插入节点在右侧
294                             tmp->bf = LH;
295                             bi_tree->bf = EH;
296                             break;
297 
298                         case EH://删除节点时候走的分支
299                             tmp->bf = EH;
300                             bi_tree->bf = EH;
301                     }
302                     
303                     p->bf = EH;
304                     L_Rotate(bi_tree->left);
305                     R_Rotate(bi_tree);
306                     break;
307                 }
308 
309             case LH://单次右旋转
310                 cout << "LeftBlance LH R_Rotate" << endl;
311                 tmp->bf = bi_tree->bf = EH;
312                 R_Rotate(bi_tree);
313                 break;
314 
315             case EH://删除节点的时候才走到的分支
316                 cout << "LeftBlance EH R_Rotate" << endl;
317                 tmp->bf = RH;
318                 bi_tree->bf = LH;
319                 R_Rotate(bi_tree);
320         }
321     }
322 
323     bool InsertAVLNode(BiTreeTypePtr &bi_tree, int data, bool &taller)
324     {
325         if (nullptr == bi_tree)
326         {
327             cout << "insert node " << data << endl;
328             BiTreeTypePtr node = new BiTreeType(data);
329 
330             bi_tree = node;
331             PromptExistBiTree();
332             taller = true;
333             return true;
334         }
335         else
336         {
337             if (bi_tree->node_data == data)
338             {
339                 taller = false;
340                 return false;
341             }
342             else if (bi_tree->node_data < data)
343             {
344                 if (!InsertAVLNode(bi_tree->right, data, taller))
345                 {
346                     return false;
347                 }
348 
349                 if (taller)
350                 {
351                     switch (bi_tree->bf)
352                     {
353                         case LH:
354                             bi_tree->bf = EH;
355                             taller = false;
356                             break;
357                             
358                         case EH:
359                             bi_tree->bf = RH;
360                             taller = true;
361                             break;
362                             
363                         case RH:
364                             RightBlance(bi_tree);
365                             taller = false;
366                             break;
367                     }
368                 }
369             }
370             else
371             {
372                 if (!InsertAVLNode(bi_tree->left, data, taller))
373                 {
374                     return false;
375                 }
376 
377                 if (taller)
378                 {
379                     switch (bi_tree->bf)
380                     {
381                         case LH:
382                             LeftBlance(bi_tree);
383                             taller = false;
384                             break;
385                             
386                         case EH:
387                             bi_tree->bf = LH;
388                             taller = true;
389                             break;
390                             
391                         case RH:
392                             bi_tree->bf = EH;
393                             taller = false;
394                             break;
395                     }
396                 }
397             }
398         }
399 
400         return true;        
401     }
402 
403     bool DeleteAVLNode(BiTreeTypePtr &bi_tree, int data, bool &lower)
404     {
405         bool Delete_R = false;
406         if (nullptr == bi_tree)
407         {
408             return false;
409         }
410         else
411         {
412             if (bi_tree->node_data == data)
413             {
414                 BiTreeTypePtr p, s;
415                 lower = true;
416 
417                 if (nullptr == bi_tree->right)
418                 {
419                     s = bi_tree;
420                     bi_tree = bi_tree->left;
421 
422                     free(s);
423 
424                     return true;
425                 }
426                 else if (nullptr == bi_tree->left)
427                 {
428                     s = bi_tree;
429                     bi_tree = bi_tree->right;
430 
431                     free(s);
432 
433                     return true;
434                 }
435                 else
436                 {
437                     p = bi_tree->right;
438                     s = p;
439                     while (p->left)
440                     {
441                         s = p;
442                         p = p->left;
443                     }
444 
445                     bi_tree->node_data = p->node_data;
446                     DeleteAVLNode(bi_tree->right, p->node_data, lower);
447 
448                     Delete_R = true;
449                 }
450             }
451             else if (bi_tree->node_data < data)
452             {
453                 if (!DeleteAVLNode(bi_tree->right, data, lower))
454                 {
455                     return false;
456                 }
457 
458                 Delete_R = true;
459             }
460             else
461             {
462                 if (!DeleteAVLNode(bi_tree->left, data, lower))
463                 {
464                     return false;
465                 }
466 
467                 Delete_R = false;
468             }
469 
470             if (lower)
471             {
472                 if (Delete_R)
473                 {
474                     cout << "right and lower" << endl;
475                     switch (bi_tree->bf)
476                     {
477                         case LH:
478                             cout << "left blance " << endl;
479                             LeftBlance(bi_tree);
480                             lower = true;
481                             break;
482 
483                         case EH:
484                             cout << "EH change to LH" << endl;
485                             bi_tree->bf = LH;
486                             lower = false;
487                             break;
488 
489                         case RH:
490                             cout << "RH change to EH" << endl;
491                             bi_tree->bf = EH;
492                             lower = true;
493                             break;
494                     }
495                 }
496                 else
497                 {
498                     cout << "left and lower" << endl;
499                     switch (bi_tree->bf)
500                     {
501                         case LH:
502                             cout << "LH change to EH" << endl;
503                             bi_tree->bf = EH;
504                             lower = true;
505                             break;
506 
507                         case EH:
508                             cout << "EH change to RH " << endl;
509                             bi_tree->bf = RH;
510                             lower = false;
511                             break;
512 
513                         case RH:
514                             cout << "right blance" << endl;
515                             RightBlance(bi_tree);
516                             lower = true;
517                             break;
518                     }
519                 }
520             }
521         }    
522 
523         return true;
524     }
525 
526     void PromptExistBiTree()
527     {
528         m_level_str_map.clear();
529         m_bi_tree_depth = 0;
530         MarkNodeHeight(m_root_ptr, 0);
531         MarkBinaryTree(m_root_ptr, GetRootPosition(m_bi_tree_depth));
532         SpliceBiTreeStr(m_root_ptr);
533 
534         PaintBiTree();
535     }
536 
537     void MarkNodeHeight(BiTreeTypePtr &bi_tree, int height)
538     {
539         if (bi_tree == nullptr)
540         {
541             return;
542         }
543         
544         bi_tree->height = height;
545         if (height > m_bi_tree_depth)
546         {
547             m_bi_tree_depth = height;
548         }
549 
550         if (bi_tree->left != nullptr)
551         {
552             MarkNodeHeight(bi_tree->left, height + 1);
553         }
554 
555         if (bi_tree->right != nullptr)
556         {
557             MarkNodeHeight(bi_tree->right, height + 1);
558         }
559     }
560 
561     void MarkBinaryTree(BiTreeTypePtr &bi_tree, int position)
562     {
563         if (bi_tree == nullptr)
564         {
565             return;
566         }
567         
568         bi_tree->position = position;
569         bi_tree->splice = GetRootSplice(m_bi_tree_depth - bi_tree->height);
570     
571 //        cout << bi_tree->height << ":" << bi_tree->splice << ":" << bi_tree->position << ":" << bi_tree->data << endl;
572 
573         if (bi_tree->left != nullptr)
574         {
575             MarkBinaryTree(bi_tree->left, position - GetRootPosition(m_bi_tree_depth - bi_tree->height - 1) - 1);
576         }
577 
578         if (bi_tree->right != nullptr)
579         {
580             MarkBinaryTree(bi_tree->right, position + GetRootPosition(m_bi_tree_depth - bi_tree->height - 1) + 1);
581         }
582     }
583 
584     void SpliceBiTreeStr(BiTreeTypePtr &bi_tree)
585     {
586         if (bi_tree != nullptr)
587         {
588             string p_str = FindLevelStr(bi_tree->splice);
589 
590             if (!p_str.empty())    
591             {                
592                 p_str.append(bi_tree->position - p_str.size(), ' ');
593             }
594             else
595             {            
596                 p_str.append(bi_tree->position, ' ');
597             }
598 
599             p_str.append(to_string(bi_tree->node_data));                
600             m_level_str_map[bi_tree->splice] = p_str;
601             
602 #if 1
603             if (bi_tree->height != m_bi_tree_depth)
604             {
605                 int brance_splice = GetRootPosition(m_bi_tree_depth - bi_tree->height - 1);
606                 for (int i=1; i<=brance_splice; ++i)
607                 {
608                     string brance_str = FindLevelStr(bi_tree->splice - i);
609                     
610                     if (!brance_str.empty())
611                     {
612                         brance_str.append(bi_tree->position - brance_str.size() - i, ' ');    
613                     }
614                     else
615                     {
616                         brance_str.append(bi_tree->position - i, ' ');
617                     }
618 
619                     if (bi_tree->left != nullptr)
620                     {
621                         brance_str.append("/");
622                     }
623                     else
624                     {
625                         brance_str.append(" ");
626                     }
627 
628 
629                     if (bi_tree->right != nullptr)
630                     {
631                         brance_str.append(i*2 - 1, ' ');
632                         brance_str.append("\\");
633                     }
634                                             
635                     m_level_str_map[bi_tree->splice - i] = brance_str;
636                 }
637             }
638 #endif
639             if (bi_tree->left != nullptr)
640             {
641                 SpliceBiTreeStr(bi_tree->left);
642             }
643 
644             if (bi_tree->right != nullptr)
645             {
646                 SpliceBiTreeStr(bi_tree->right);
647             }
648         }
649     }
650 
651     void PaintBiTree()
652     {
653         int splice = -1;
654         while ((splice = SelectLowestNode()) != -1)
655         {
656             cout << m_level_str_map[splice] << endl;
657             m_level_str_map.erase(splice);
658         }
659     }
660 
661     int SelectLowestNode()
662     {
663         int mininal_num = -1;
664         for (auto map_pair : m_level_str_map)
665         {
666             if (map_pair.first > mininal_num)
667             {
668                 mininal_num = map_pair.first;
669             }
670         }
671 
672         return mininal_num;
673     }
674 
675     int GetRootPosition(int height)
676     {
677         return abs(GetPrintSide(height, 1) * 3 - 1);
678     }
679 
680     int GetRootSplice(int height)
681     {
682         int splice_num = GetPrintSide(height, 1) * 3 - 1;
683         return (splice_num > 0) ? splice_num : 0;
684     }
685 
686     int GetPrintSide(int height, int side)
687     {
688         if (height <= 0)
689         {
690             return 0;
691         }
692         else if (height == 1)
693         {
694             return side;
695         }
696         else
697         {
698             return GetPrintSide(height-1, side*2);
699         }
700     }
701 
702     string FindLevelStr(int splice)
703     {
704         string tmp_str;
705         for (auto node : m_level_str_map)
706         {
707             if (node.first == splice)
708             {
709                 return node.second;
710             }
711         }
712 
713         return tmp_str;
714     }
715 
716     void pre_recursive_order(BiTreeTypePtr bi_tree)
717     {
718         if (nullptr == bi_tree)
719         {
720             return;
721         }
722 
723         cout << bi_tree->node_data << " : " << bi_tree->bf << endl;
724         pre_recursive_order(bi_tree->left);
725         pre_recursive_order(bi_tree->right);
726     }
727 
728 private:
729     BiTreeTypePtr m_root_ptr;
730 
731     int m_bi_tree_depth;
732 
733     map<int, string> m_level_str_map;
734 };
735 
736 int main()
737 {
738     BBST bb_st;
739     
740     return 0;
741 }
全部代码

makefile跟之前的一样,不需要改动,运行之后的图如下:

我在平衡二叉树的过程中加入了打印,所以会显示步骤,你可以在调试的时候,在任何地方添加打印接口,能直观的看到

平衡二叉树的变化。同时打印接口是方便了解平衡二叉树,所以最好用2位数及以下的数字,不要超过99,位数多了打印可能

出现偏差。

 

到现在为止,写完了,如果发现有什么问题,欢迎提出,方便我能及时改正,谢谢