这篇文章主要是为了温习下平衡二叉树,同时添加了树型打印的接口,让平衡二叉树的添加和删除更容易理解。
接下来的篇幅很长,需要有很多的耐心,做好了准备接下来往下看吧。
通俗的来说:
二叉树就是节点度最大为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三个节点,先插入9和8.

然后插入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)
#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; }
代码中的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,位数多了打印可能
出现偏差。
到现在为止,写完了,如果发现有什么问题,欢迎提出,方便我能及时改正,谢谢
浙公网安备 33010602011771号