此文章为从二叉树到红黑树系列文章的第二节,主要介绍介绍二叉树抽象基类的基本组成。为后续BST,AVL和RedBlack做好铺垫
在阅读本文前,强烈建议你看下前面的文章的目录、前言以及基本介绍,否则你无法理解后面的内容。链接如下:
int _size;//二叉树的规模
BinNodePtr _root;//二叉树的树根
构造,析构
判空
获取二叉树的规模、根节点
获取二叉树当前高度
四种遍历(结合根节点使用)
获取当前节点的父亲的孩子的引用。(这句话有点拗口,但这个函数是整个流程的关键)
FromParentTo
更新节点的高度 updateHeight
更新节点以及其祖先的高度 updateHeightAbove
删除所有节点 remove
插入节点insert(为了方便起见,设为纯虚函数,使这个二叉树基类不能实例化)
template<typename T=int>
class BinTree {
protected:
using BinNodePtr = BinNode<T>*;
using BinTreePtr = BinTree<T>*;
protected:
int _size;//二叉树的规模
BinNodePtr _root;//二叉树的树根
public:
BinTree() :_size(0), _root(nullptr) {}
virtual ~BinTree() {
//std::cout << "调用析构函数" << std::endl;
if (0 < _size)remove(_root);
}
public:
constexpr int size()const { return _size; }//规模
constexpr bool empty()const { return !_root; }//判空
inline BinNodePtr root()const {//返回树根
return (_root) ? _root : nullptr;
}
constexpr int getHeight()const {//获取树高度
return (this->_root) ? this->_root->_height : -1;
}
public:
template<typename VST>//层次遍历
void travLevel(VST visit) {
if (_root)
_root->travLevel(visit);
}
template<typename VST>//先序遍历
void travPre(VST visit,const int&method=1) {
if (_root)
_root->travPre(visit, method);
}
template<typename VST>//中序遍历
void travIn(VST visit, const int& method = 1) {
if (_root)
_root->travIn(visit, method);
}
template<typename VST>//后序遍历
void travPost(VST visit, const int& method = 1) {
if (_root)
_root->travPost(visit, method);
}
protected:
virtual int updateHeight(BinNodePtr x)const;//更新节点x的高度/*vs开最新版本的c++就可以用virtual constexpr*/
void updateHeightAbove(BinNodePtr x)const;//更新节点x及其祖先的高度
protected:
//注意返回值为引用,不然无法作为左值
BinNodePtr& FromParentTo(BinNodePtr x) {/*用这个更改x的父亲的左右孩子*//*接受者一般要加个引用接收*/
return (IsRoot(x) ? this->_root : (IsLChild(x) ? x->_parent->_lchild : x->_parent->_rchild));
}
protected:
virtual BinNode<T>* insert(const T& data) = 0;//插入节点,为了方便起见,设为纯虚函数,即这个基类为抽象类,没有实例
private:
void remove(BinNodePtr x);//只用于析构函数,因此不管高度等其他因素,直接暴力全部删除
};//class BinTree
这个函数对于AVL树和RedBlack树的插入和删除而言,至关重要。我会在AVL和RedBlack树的相应部分再次说明这个函数的作用。
//注意返回值为引用,不然无法作为左值
BinNodePtr& FromParentTo(BinNodePtr x) {/*用这个更改x的父亲的左右孩子*//*接受者一般要加个引用接收*/
return (IsRoot(x) ? this->_root : (IsLChild(x) ? x->_parent->_lchild : x->_parent->_rchild));
}
此处stature是本系列文章第一节的在全局区定义的函数,头文件为BInNode_Macro.h。
//更新节点x的高度
template<typename T>
int BinTree<T>::updateHeight(BinNodePtr x)const {
//高度为左右子树的高度的最大值
return x->_height = 1 + std::max(stature(x->_lchild), stature(x->_rchild));
}
通常,还需要从当前节点出发沿parent指针逆行向上,依次更新各代祖先的高度记录,直到这个祖先的高度不变时停止。
//更新节点x及其祖先的高度
template<typename T>
void BinTree<T>::updateHeightAbove(BinNodePtr x)const{
if (x == nullptr)
return;
updateHeight(x);
do {
x = x->_parent;
if (x == nullptr)//如果父节点为空节点,则返回
return;
int currentHeight = x->_height;//记录节点当前的高度
int afterHeight = updateHeight(x);
if (currentHeight == afterHeight)//只要其高度没有更新,那么就可以跳出循环
break;
} while (x);//当此节点不为空
}
这个函数用于析构函数中,用于所有树的析构,无论是BST,AVL还是RedBlack。
//=========删除============//
template<typename T>
void BinTree<T>::remove(BinNodePtr x) {
if (nullptr==x)
return;
remove(x->_lchild);
remove(x->_rchild);/*处于方便,用递归删除*/
release(x->_data);
release(x);
}
在(五)中的删除代码中,可以看到用到了release函数来进行节点的删除,其位于头文件release.h中。
#pragma once
template<typename T>
struct Cleaner {
static void clean(T& x) {
#ifdef DEBUG
printf("删除了\n");
#endif // DEBUG
}
};
template<typename T>
struct Cleaner<T*> {
static void clean(T*& x) {//注意这里是引用,方便删除后,直接变成空指针
if (x) {
delete x;
x = nullptr;
}
}
};
template<typename T>
static void release(T &x) {//利用模板偏特化,删除节点,若为指针,则delete,若不为指针,则不做任何操作。
Cleaner<T>::clean(x);
}
#pragma once
#include"BinNode.h"
#include "release.h"
namespace mytree {
using namespace mytree_marcro;
template<typename T=int>
class BinTree {
protected:
using BinNodePtr = BinNode<T>*;
using BinTreePtr = BinTree<T>*;
protected:
int _size;//二叉树的规模
BinNodePtr _root;//二叉树的树根
public:
BinTree() :_size(0), _root(nullptr) {}
virtual ~BinTree() {
//std::cout << "调用析构函数" << std::endl;
if (0 < _size)remove(_root);
}
public:
constexpr int size()const { return _size; }//规模
constexpr bool empty()const { return !_root; }//判空
inline BinNodePtr root()const {//返回树根
return (_root) ? _root : nullptr;
}
constexpr int getHeight()const {//获取树高度
return (this->_root) ? this->_root->_height : -1;
}
public:
template<typename VST>//层次遍历
void travLevel(VST visit) {
if (_root)
_root->travLevel(visit);
}
template<typename VST>//先序遍历
void travPre(VST visit,const int&method=1) {
if (_root)
_root->travPre(visit, method);
}
template<typename VST>//中序遍历
void travIn(VST visit, const int& method = 1) {
if (_root)
_root->travIn(visit, method);
}
template<typename VST>//后序遍历
void travPost(VST visit, const int& method = 1) {
if (_root)
_root->travPost(visit, method);
}
protected:
virtual int updateHeight(BinNodePtr x)const;//更新节点x的高度/*vs开最新版本的c++就可以用virtual constexpr*/
void updateHeightAbove(BinNodePtr x)const;//更新节点x及其祖先的高度
protected:
//注意返回值为引用,不然无法作为左值
BinNodePtr& FromParentTo(BinNodePtr x) {/*用这个更改x的父亲的左右孩子*//*接受者一般要加个引用接收*/
return (IsRoot(x) ? this->_root : (IsLChild(x) ? x->_parent->_lchild : x->_parent->_rchild));
}
protected:
virtual BinNode<T>* insert(const T& data) = 0;//插入节点,为了方便起见,设为纯虚函数,即这个基类为抽象类,没有实例
private:
void remove(BinNodePtr x);//只用于析构函数,因此不管高度等其他因素,直接暴力全部删除
};//class BinTree
//================更新高度==================//
//更新节点x的高度
template<typename T>
int BinTree<T>::updateHeight(BinNodePtr x)const {
return x->_height = 1 + std::max(stature(x->_lchild), stature(x->_rchild));//高度为左右子树的高度的最大值
}
//更新节点x及其祖先的高度
template<typename T>
void BinTree<T>::updateHeightAbove(BinNodePtr x)const{
if (x == nullptr)
return;
updateHeight(x);
do {
x = x->_parent;
if (x == nullptr)//如果父节点为空节点,则返回
return;
int currentHeight = x->_height;//记录节点当前的高度
int afterHeight = updateHeight(x);
if (currentHeight == afterHeight)//只要其高度没有更新,那么就可以跳出循环
break;
} while (x);//当此节点不为空
}
//=========删除============//
template<typename T>
void BinTree<T>::remove(BinNodePtr x) {
if (nullptr==x)
return;
remove(x->_lchild);
remove(x->_rchild);/*处于方便,用递归删除*/
release(x->_data);
release(x);
}
}//namespace mytree
学一个东西,不知道其道理,不高明!