数据结构与算法——二叉树的实现(C++)

二叉树的实现

1 BinNode类

BinNode作为二叉树的节点,其定义的规范性十分基础又十分重要。以下是"binnode.h"中的程序内容。

#pragma once
#define BinNodePosi(T) BinNode<T>* //节点位置 
#define stature(p) ((p) ? (p)->height: -1) //节点高度

//BinNode状态与性质的判断
#define IsRoot(x) (!((x).parent))
#define IsLeaf(x) (!((x).lc || (x).rc))  
#define IsLChild(x) (!((x).parent) && (&(x) == (x).parent->lc))
#define IsRChild(x) (!((x).parent) && (&(x) == (x).parent->rc))
#define HasParent(x) ((x).parent) 
#define HasLChild(x) ((x).lc) 
#define HasRChild(x) ((x).rc) 
#define HasChild(x) (HasLChild(x) || HasRChild(x))                  //至少拥有一个孩子 
#define HasBothChild(x) (HasLChild(x) && HasRChild(x))              //同时拥有两个孩子 
#define sibling(p) (IsLChild(*(p)) ? (x).parent.rc: (x).parent.lc)          //兄弟 
#define uncle(p) (sibling((p).parent))                                              //叔叔 
#define FromParentTo(x)  ( IsRoot(x) ? _root : ( IsLChild(x) ? (x).parent->lc : (x).parent->rc ) )         //来自父亲的引用
 
typedef enum { RB_RED, RB_BLACK } RBColor; //节点颜色
 
template <typename T> struct BinNode{
	BinNodePosi(T) parent;
	BinNodePosi(T) lc;
	BinNodePosi(T) rc; //父亲、孩子
    int npl;
    RBColor color;
	T data; int height;  //高度、子树规模
    //构造函数
    BinNode():
        parent( nullptr ), lc( nullptr ),rc( nullptr ),height ( 0 ),npl( 1 ),color ( RB_RED ) {}
    BinNode( T e, BinNodePosi(T) p = nullptr, BinNodePosi(T) lc = nullptr, BinNodePosi(T) rc = nullptr, int h = 0, int l = 1, RBColor c = RB_RED):
        data( e ), parent( p ), lc( lc ), rc( rc ),height ( h ),npl( 1 ),color ( c ) {}
    //操作接口
    int size();
	BinNodePosi(T) insertAsLC( T const & ); //作为左孩子插入新节点
	BinNodePosi(T) insertAsRC( T const & ); //作为右孩子插入新节点
	BinNodePosi(T) succ(); //(中序遍历意义下)当前节点的直接后继
	template <typename VST> void travLevel(VST &); //子树层次遍历
	template <typename VST> void travPre(VST &); //子树先序遍历
	template <typename VST> void travIn(VST &); //子树中序遍历
	template <typename VST> void travPost(VST &); //子树后序遍历
};

//作为左孩子插入
template <typename T> BinNodePosi(T) BinNode<T>::insertAsLC(T const &e)
	{return lc = new BinNode( e, this);}
//作为右孩子插入
template <typename T> BinNodePosi(T) BinNode<T>::insertAsRC(T const &e)
	{return rc = new BinNode( e, this);}

//size()
template <typename T> int BinNode<T>::size(){//后代总数,以其为根的子树的规模
	int s = 1; //计入本身
	if (lc) s += lc->size(); //递归计入左子树规模
	if (rc) s += rc->size(); //递归计入右子树规模
	return s;
}//O(n=|size|)
//"release.h"
#pragma once

#ifdef _DEBUG
#include <stdio.h>
#include <typeinfo>
#endif

/******************************************************************************************
 * 列表、向量等结构内的节点中,可以存放基本类型或构造类型
 * 按照本书约定,出于效率的考虑,对于后一情况通常只保存对象的指针
 * 因此,在主体结构析构之前,需要首先释放这些成员对象所占的空间
 * 此处,借助C++中偏特化技术区分上述两种情况,并做对应处理
 ******************************************************************************************/

namespace dtl 
{

template <typename T> 
struct Cleaner {
   static void clean ( T x ) { //相当于递归基
#ifdef _DEBUG
      static int n = 0;
      if ( 7 > strlen ( typeid ( T ).name() ) ) { //复杂类型一概忽略,只输出基本类型
         printf ( "\t<%s>[%d]=", typeid ( T ).name(), ++n );
         //print ( x );
         printf ( " purged\n" );
      }
#endif
   }
};

template <typename T> 
struct Cleaner<T*> {
   static void clean ( T* x ) {
      if ( x ) { delete x; } //如果其中包含指针,递归释放
#ifdef _DEBUG
      static int n = 0;
      printf ( "\t<%s>[%d] released\n", typeid ( T* ).name(), ++n );
#endif
   }
};

template <typename T> 
inline void release ( T x ) { Cleaner<T>::clean ( x ); }

}

2 BinTree类

BinTree类是二叉树实现的主体部分,主要包括二叉树的构造,插入与遍历等功能函数。

#include "binnode.h"
#include "release.h"
#include <iostream>
#include <memory>
#include "../dsa_queue_20200717/queue.h"

#define max(a,b) ( a > b ? a : b)

template <typename T> class BinTree{
protected:
	int _size;//规模
	
	virtual int updateHeight( BinNodePosi(T) x); //更新节点x的高度
	void updateHeightAbove( BinNodePosi(T) x); //更新x及祖先的高度
public:
	BinNodePosi(T) _root; //根节点
    //构造函数
    BinTree():_size( 0 ), _root ( nullptr ) {}
    //~BinTree(){ if ( 0 < _size ) remove( _root );}

    //函数接口 
	int size() const { return _size; } //规模
	bool empty() const { return !_root;} //判空
	BinNodePosi(T) root() const {return _root; } //树根
    
    BinNodePosi(T) insertAsRoot(T const& e); //作为根节点插入
	BinNodePosi(T) insertAsLC(BinNodePosi(T) x, T const& e);//作为左孩子节点接入
	BinNodePosi(T) insertAsRC(BinNodePosi(T) x, T const& e);//作为右孩子节点接入
	BinNodePosi(T) attachAsLC(BinNodePosi(T) x, BinTree<T>* &S);//作为左子树接入
	BinNodePosi(T) attachAsRC(BinNodePosi(T) x, BinTree<T>* &S);//作为右子树接入

	int remove(BinNodePosi(T) x);//删除以节点x为根的子树
    template <typename VST> void travPre(BinNodePosi(T) x, VST& visit) { if (_root) _root->travPre(visit); }//先序遍历
    template <typename VST> void travPost(const VST& visit) { if (_root) _root->travPost(visit); }//后序遍历
	template <typename VST> void travLevel(const VST& visit) { if (_root) _root->travLevel(visit); }//层次遍历
	template <typename VST> void travIn( const VST& visit) { if (_root) _root->travIn(visit); }//中序遍历

	void traverse (BinNodePosi(T) x, void (*)(T&) );
};
template <typename T> void BinTree<T>::traverse ( BinNodePosi(T) x, void (*visit )( T& ))
{
    if( !x ) return;
	visit( x->data );
	traverse( x->lc, visit );

	traverse( x->rc, visit );
	
}
//先序遍历
template <typename T, typename VST> void travPre(BinNodePosi(T) x, VST& visit) {
	if( !x ) return;
	//BinNodePosi(T) x = root();
	visit( x->data );
	travPre( x->lc, visit );
	travPre( x->rc, visit );

}
//后序遍历
template <typename T, typename VST> void travPost(BinNodePosi(T) x, VST& visit) {
	if( !x ) return;
	travPost( x->lc, visit );
	travPost( x->rc, visit );
	visit( x->data );

}
//中序遍历
template <typename T, typename VST> void travIn(BinNodePosi(T) x, VST& visit) {
	if( !x ) return;
	travIn( x->lc, visit );
	visit( x->data );
	travIn( x->rc, visit );


}
//层次遍历
template <typename T> template <typename VST> void BinNode<T>::travLevel ( VST& visit){
	Queue<BinNodePosi(T)> Q;
	Q.enqueue( this );
	while( !Q.empty()){
		BinNodePosi(T) x = Q.dequeue();visit( x->data);
		if( HasLChild(*x )) Q.enqueue( x->lc);
		if( HasRChild(*x )) Q.enqueue( x->rc);
	}
}
//更新节点x的高度
template <typename T>
int BinTree<T>::updateHeight(BinNodePosi(T) x)
{
	return x->height = 1 + max(stature(x->lc), stature(x->rc));
}

//更新树的高度
template <typename T>
void BinTree<T>::updateHeightAbove(BinNodePosi(T) x) 
{
	while (x) { updateHeight(x); x = x->parent; }
}

//空树插入根节点
template <typename T>
BinNodePosi(T) BinTree<T>::insertAsRoot(T const& e)
{
	_size = 1; return _root = new BinNode<T> (e);
}

//作为节点x的左孩子插入
template <typename T>
BinNodePosi(T) BinTree<T>::insertAsLC(BinNodePosi(T) x,T const& e)
{
	_size++; x->insertAsLC(e); updateHeightAbove(x); return x->lc;
}
//作为节点x的右孩子插入
template <typename T>
BinNodePosi(T) BinTree<T>::insertAsRC(BinNodePosi(T) x,T const& e)
{
	_size++; x->insertAsRC(e); updateHeightAbove(x); return x->rc;
}
//S作为节点x的左子树插入
//S本身被置空
template <typename T>
BinNodePosi(T) BinTree<T>::attachAsLC(BinNodePosi(T) x, BinTree<T>* &S)
{
	x->lc = S->_root;
	x->lc->parent = x;

	_size += S->_size;
	updateHeightAbove(x);
	S->_root =nullptr;
	S->size = 0; dtl::release(S); S = nullptr; return x;
}
//S作为节点x的右子树插入
//S本身被置空
template <typename T>
BinNodePosi(T) BinTree<T>::attachAsRC(BinNodePosi(T) x, BinTree<T>* &S)
{
	x->rc = S->_root;
	x->rc->parent = x;

	_size += S->_size;
	updateHeightAbove(x);
	S->_root = nullptr;
	S->size = 0; dtl::release(S); S = nullptr; return x;
}

template <typename T>//二叉树删除x节点及其后代
int BinTree<T>::remove(BinNodePosi(T) x)
{
	FromParentTo( *x ) = nullptr;
	updateHeightAbove ( x->parent );
	int n = removeAt( x ); _size -=n;
	return n;
}

template <typename T>//删除x节点及其后代,返回删除节点数
static int removeAt(BinNodePosi(T) x)
{
	if (!x)return 0;//递归基
	int n = 1 + removeAt(x->lc) + removeAt(x->rc);
	dtl::release(x->data); dtl::release(x); return n;
}

3 程序测试

撰写以下的主程序对二叉树进行测试,分别测试了二叉树的基于函数指针的各种遍历与二叉树的删除子树操作,结果可见注释。

/*
* The program aims to test BinTree class
* author@Ripples
* 20200723
*/
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include "bintree.h"
#include "../dsa_vector_200622/dsa_vector.h"

using namespace std;

template<typename T> void returnValue(T& a)
{
    cout << "return_value: " << a << endl;
}

int main(int argc,char* argv[])
{
	//构造树
	BinTree<char> bt_test;
	bt_test.insertAsRoot('b');
	bt_test.insertAsLC(bt_test.root(), 'a');
	bt_test.insertAsRC(bt_test.root(), 'f');
	bt_test.insertAsLC(bt_test.root()->rc, 'd');
	bt_test.insertAsRC(bt_test.root()->rc, 'g');
	bt_test.insertAsLC(bt_test.root()->rc->lc, 'c');
	bt_test.insertAsRC(bt_test.root()->rc->lc, 'e');
	/*
						b
	     	a							f
								d				g
							c		e
	*/
	//前序遍历:b,a,f,d,c,e,g
	//后序遍历:a,c,e,d,g,f,b
	//中序遍历:a,b,c,d,e,f,g
	void (* visit)(char& ) = &returnValue;
	bt_test.traverse(bt_test.root(),visit);

	//删除右子树之后遍历
	cout << "modifed:" << endl;
	bt_test.remove(bt_test.root()->rc);
	bt_test.traverse(bt_test.root(),visit);//b,a

	return 0;
}

4 总结

二叉树作为一个承接向量、列表,启出搜索树(二叉搜索树、平衡二叉搜索树与AVL树)和高级搜索树(伸展树、B-树与红黑树)的重要节点,其研究有深远的意义。二叉树的实现基本完成,仍存在一些细节有待完善。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值