C++ 数据结构学习 ---- 二叉树

目录

1. 头文件

1.1 二叉树节点文件

1.2 二叉树文件

2. 二叉树相关函数

2.1 更新高度

2.2 插入

2.3 删除

2.4 子树接入

2.5 分离子树

2.6 左子树深入

2.7 寻找左侧最高可见叶节点

3. 相关算法

3.1 层次遍历算法

3.2 先序遍历算法(3种)

3.3 中序遍历(5种)

3.4 后序遍历算法(2种)

4. 完整代码

5. 运行结果截图


1. 头文件

1.1 二叉树节点文件

#include"Stack.h"
#include"Queue.h"

//BinNode状态与性质的判断
 #define IsRoot(x) (!((x).parent))
 #define IsLChild(x) (!IsRoot(x) && (&(x) == (x).parent->lc))
 #define IsRChild(x) (!IsRoot(x) && (&(x) == (x).parent->rc))
 #define HasParent(x) (!IsRoot(x))
 #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 IsLeaf(x) (!HasChild(x))
//与BinNode具有特定关系的节点及指针
#define sibling( p ) ( IsLChild( * (p) ) ? (p)->parent->rc : (p)->parent->lc ) /*兄弟*/
#define uncle(x) (sibling((x)->parent)) /*叔叔*/
#define FromParentTo( x ) /*来自父亲的引用*/ \
( IsRoot(x) ? _root : ( IsLChild(x) ? (x).parent->lc : (x).parent->rc ) )






#define BinNodePosi(T) BinNode<T>* //节点位置
#define stature(p) ((p)?(p->height):-1)//节点高度,空树高度为-1
typedef enum{RB_RED=0,RB_BLACK}RBColor;//节点颜色
//节点类
template <typename T> struct BinNode {
	BinNodePosi(T) parent;//父亲
	BinNodePosi(T) lc;//左孩子
	BinNodePosi(T) rc; //右孩子
	T data; //数据
	int height; //高度(通用)
	int npl;//左式堆
	RBColor color;
	//构造函数
	BinNode() :
	   parent(NULL), lc(NULL), rc(NULL), height(0), npl(1), color(RB_RED) { }
	 BinNode(T e, BinNodePosi(T) p = NULL, BinNodePosi(T) lc = NULL, BinNodePosi(T) rc = NULL,
		           int h = 0, int l = 1, RBColor c = RB_RED) :
		     data(e), parent(p), lc(lc), rc(rc), height(h), npl(l), 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&); //子树后序遍历
	 // 比较器、判等器
	   bool operator> (BinNode const& bn) { return data > bn.data; } //大于
	   bool operator< (BinNode const& bn) { return data < bn.data; } //小于
	   bool operator== (BinNode const& bn){ return data == bn.data; } //等于
	   bool operator!= (BinNode const& bn){ return data != bn.data; } //不等于于
	
};
//统计当前节点后代总数,即以其为根的子树规模
template <typename T> int BinNode<T>::size() { 
	  int s = 1; //计入本身
	  if (lc) s += lc->size(); //递归计入左子树规模
	  if (rc) s += rc->size(); //递归计入右子树规模
	  return s;
}
//将e作为当前节点的左孩子插入二叉树
 template <typename T> BinNodePosi(T) BinNode<T>::insertAsLC(T const& e)
 { return lc = new BinNode(e, this); } 
//将e作为当前节点的右孩子插入二叉树
 template <typename T> BinNodePosi(T) BinNode<T>::insertAsRC(T const& e)
 { return rc = new BinNode(e, this); }
//定位节点v的直接后继
template <typename T> BinNodePosi(T) BinNode<T>::succ() { 
 BinNodePosi(T) s = this; //记录后继的临时变量
	     if (rc) { //若有右孩子,则直接后继必在右子树中,具体地就是
	       s = rc; //右子树中
	       while (HasLChild(*s)) s = s->lc; //最靠左(最小)的节点
	 }
	 else { //否则,直接后继应是“将当前节点包含于其左子树中的最低祖先”,具体地就是
		     while (IsRChild(*s)) s = s->parent; //逆向地沿右向分支,不断朝左上方移动
		     s = s->parent; //最后再朝右上方移动一步,即抵达直接后继(如果存在)	
	 }
  return s;
 }


1.2 二叉树文件

注:要现在当前头文件之前引入二叉树节点头文件


#include "BinNode.h"

template <typename T> class BinTree { 
 protected:
	  int _size; BinNodePosi(T) _root; //规模、根节点
	  virtual int updateHeight(BinNodePosi(T) x); //更新节点x的高度
	  void updateHeightAbove(BinNodePosi(T) x); //更新节点x及其祖先的高度
     public:
	  BinTree() : _size(0), _root(NULL) { } //构造函数
	  ~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),T const& e ); //插入左孩子
	  BinNodePosi(T) insertAsRC(BinNodePosi(T),T const& e); //插入右孩子
	  BinNodePosi(T) attachAsLC(BinNodePosi(T),BinTree<T> *&T); //接入左子树
	  BinNodePosi(T) attachAsRC(BinNodePosi(T),BinTree<T> *&T); //接入右子树
	  int remove(BinNodePosi(T) x); //删除以x为根节点的子树,返回该子树原先的规模
	  BinTree<T>* secede(BinNodePosi(T) x); //将子树x从当前树中摘除,并将其转化成一棵独立的子树
	  template < typename VST> //操作器
	    void travLevel(VST & visit) { if (_root) _root->travLevel(visit); } //层次遍历
	  template < typename VST> //操作器
	    void travPre(VST & visit) { if (_root) _root->travPre(visit); } //先序遍历
	  template < typename VST> //操作器
	    void travIn(VST & visit) { if (_root) _root->travIn(visit); } //中序遍历
	  template < typename VST> //操作器
	   void travPost(VST & visit) { if (_root) _root->travPost(visit); } //后序遍历

	   bool operator< (BinTree<T> const& t) //比较器
		   { return _root && t._root && (_root->data < t._root->data); }
	   bool operator> (BinTree<T> const& t) //比较器
	   {
		   return _root && t._root &&( _root->data> t._root->data);
	   }
	    bool operator== (BinTree<T> const& t) //判等器
		  { return _root && t._root && (_root == t._root); }
		bool operator!= (BinTree<T> const& t) //判等器
		{
			return _root && t._root && (_root != t._root);
		}
}; //BinTree

2. 二叉树相关函数

2.1 更新高度

//大小比较
int max(int x,int y) {
	return x > y ? x : y;
}
//更新节点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) {
	if (x->height == updateHeight(x)) break;
	//当前节点的高度与更新后的节点高度相同,不用再向上更新了,对该节点来说,变化的节点不是本节点的最深路径节点
	updateHeight(x); 
	x = x->parent;
  } 
} //从x出发,覆盖历代祖先。

2.2 插入

//将e当作根节点插入空的二叉树
 template <typename T> BinNodePosi(T) BinTree<T>::insertAsRoot(T const& e)
 { _size = 1; return _root = new BinNode<T>(e); } 
//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; } 
//e插入为x的右孩子
 template <typename T> BinNodePosi(T) BinTree<T>::insertAsRC(BinNodePosi(T) x, T const& e)
 { _size++; x->insertAsRC(e); updateHeightAbove(x); return x->rc; } 

2.3 删除

 //删除二叉树中位置x处的节点及其后代,返回被删除节点的数值
 template < typename T> int BinTree<T>::remove(BinNodePosi(T) x) { //x为二叉树中的合法位置
	    FromParentTo(*x) = NULL; //切断来自父节点的指针
	    updateHeightAbove(x->parent); //更新祖先高度
	    int n = removeAt(x); _size -= n; return n; //删除子树x,更新规模,返回删除节点总数
 }
//释放节点值
template <typename T>void release(T &e) {
	e = NULL;
}
//释放节点空间
template <typename T>void release(BinNodePosi(T) &e) {
	 delete e;
}
//删除二叉树中位置x处的节点及其后代,返回被删除节点的数值
template <typename T> static int removeAt(BinNodePosi(T) x) { //x为二叉树中的合法位置
	  if (!x) return 0; //递归基:空树
	    int n = 1 + removeAt(x->lc) + removeAt(x->rc); //递归释放左、右子树
	   release(x->data); release(x); return n; //释放被摘除节点,并返回删除节点总数
} 

2.4 子树接入

//将S当作节点x的左子树接入二叉树,S本身置空
 template < typename T> BinNodePosi(T) BinTree<T>::attachAsLC(BinNodePosi(T) x,BinTree<T>*&S){ //x->lc == NULL
	   if (x->lc = S->_root) x->lc->parent = x; //接入
	   _size += S->_size; updateHeightAbove(x); //更新全树规模与x所有祖先的高度
	   S->_root = NULL; S->_size = 0; release(S); S = NULL; return x; //释放原树,返回接入位置
}
//将S当作节点x的右子树接入二叉树,S本身置空
 template <typename T>  BinNodePosi(T) BinTree<T>::attachAsRC(BinNodePosi(T) x, BinTree<T>*&S) { //x->rc == NULL
	  if (x->rc = S->_root) x->rc->parent = x; //接入
	  _size += S->_size; updateHeightAbove(x); //更新全树规模与x所有祖先的高度
	  S->_root = NULL; S->_size = 0; release(S); S = NULL; return x; //释放原树,返回接入位置
}

2.5 分离子树

/二叉树子树分离算法:将子树x从当前树中摘除,将其封装为一棵独立子树返回
 template < typename T> BinTree<T>*BinTree<T>::secede(BinNodePosi(T) x) { //x为二叉树中的合法位置
	     FromParentTo(*x) = NULL; //切断来自父节点的指针
	     updateHeightAbove(x->parent); //更新原树中所有祖先的高度
	     BinTree<T>*S = new BinTree<T>; S->_root = x; x->parent = NULL; //新树以x为根
	     S->_size = x->size(); _size -= S->_size; return S; //更新规模,返回分离出来的子树
 }

 //访问节点的数据
 template <typename T>void visits(T e) {
	 std::cout << e << " ";
 }

2.6 左子树深入

 //从当前节点出发,沿左分支不断深入,直至没有左分支的节点;沿途节点遇到后立即访问
template < typename T, typename VST>static void visitAlongVine(BinNodePosi(T) x, VST & visit, Stack<BinNodePosi(T)>&S) {
	   while (x) {
	    visits(x->data); //访问当前节点
	    S.push(x->rc); //右孩子入栈暂存(可优化:通过判断,避免空的右孩子入栈)
	    x = x->lc;  //沿左分支深入一层
	 }
 }

2.7 寻找左侧最高可见叶节点

//在以S栈顶节点为根的子树中,找到最高左侧可见叶节点
template < typename T> static void gotoLeftmostLeaf(Stack<BinNodePosi(T)>&S) { 
	//沿途所遇节点依次入栈
	    while (BinNodePosi(T) x = S.top()) //自顶而下,反复检查当前节点(即栈顶)
		        if (HasLChild(*x)) { //尽可能向左
		           if (HasRChild(*x)) S.push(x->rc); //若有右孩子,优先入栈
		           S.push(x->lc); //然后才转至左孩子
	 }
		 else //实不得已
	        S.push(x->rc); //才向右
	       S.pop(); //返回之前,弹出栈顶的空节点
 } 

3. 相关算法

3.1 层次遍历算法

//二叉树层次遍历算法
 template <typename T> template < typename VST> void BinNode<T>::travLevel(VST & visit) { //元素类型、操作器
	    Queue<BinNodePosi(T)> Q; //辅助队列
	    Q.enqueue(this); //根节点入队
		std::cout << "调用了层次遍历";
	    while (!Q.empty()) { //在队列再次变空之前,反复迭代
	     BinNodePosi(T) x = Q.dequeue(); 
		// visit(x->data); 
		 std::cout << x->data<<" ";//取出队首节点并访问之
	     if (HasLChild(*x)) Q.enqueue(x->lc); //左孩子入队
	     if (HasRChild(*x)) Q.enqueue(x->rc); //右孩子入队
	 }
		std::cout << std::endl;
 }

3.2 先序遍历算法(3种)

 //二叉树先序遍历算法(迭代版#1)
 template < typename T, typename VST>  void travPre_I1(BinNodePosi(T) x, VST & visit) { 
	 Stack<BinNodePosi(T)> S; //辅助栈
	 if (x) S.push(x); //根节点入栈
	 while (!S.empty()) { //在栈变空之前反复循环
	      x = S.pop(); visits(x->data); //弹出并访问当前节点,其非空孩子的入栈次序为先右后左
	      if (HasRChild(*x)) S.push(x->rc); if (HasLChild(*x)) S.push(x->lc);
	 }
 }
//二叉树先序遍历算法(迭代版#2)
template < typename T, typename VST> void travPre_I2(BinNodePosi(T) x, VST & visit) { 
	     Stack<BinNodePosi(T)> S; //辅助栈
	     while (true) {
		      visitAlongVine(x, visit, S); //从当前节点出发,逐批访问
		      if (S.empty()) break; //直到栈空
		      x = S.pop(); //弹出下一批的起点
	 }
 }
//二叉树先序遍历算法(递归版)
 template < typename T, typename VST> void travPre_R(BinNodePosi(T) x, VST & visit) { 
	   if (!x) return;
	   visits(x->data);
	   travPre_R(x->lc, visit);
	   travPre_R(x->rc, visit);
}


 //二叉树先序遍历算法统一入口
 template <typename T> template < typename VST> void BinNode<T>::travPre(VST & visit) {
	 srand(time(NULL));//随机数种子
	    switch (rand() % 3) { //此处暂随机选择以做测试,共三种选择
	       case 1: std::cout<<"调用先序遍历(迭代1)";travPre_I1(this, visit); std::cout<<std::endl;
				break; //迭代版#1
	       case 2: std::cout<<"调用先序遍历(迭代2)";travPre_I2(this, visit); std::cout<<std::endl;
				break; //迭代版#2
	       default:std::cout<<"调用先序遍历(递归)"; travPre_R(this, visit); std::cout<<std::endl;
			    break; //递归版
	 }
 }

3.3 中序遍历(5种)

//二叉树中序遍历算法(递归版)
 template < typename T, typename VST> void travIn_R(BinNodePosi(T) x, VST& visit) {
	 if (!x) return;//递归基
	 travIn_R(x->lc, visit);//先向左递归
	 visits(x->data); //输出当前访问的值
	 travIn_R(x->rc, visit);//在左无可左时在向右递归
 }
//从当前节点出发,沿左分支不断深入,直至没有左分支的节点
 template < typename T> static void goAlongVine(BinNodePosi(T) x, Stack<BinNodePosi(T)>&S) {
	     while (x) { S.push(x); x = x->lc; } //当前节点入栈后随即向左侧分支深入,迭代直到无左孩子
}
//二叉树中序遍历算法(迭代版#1)
template < typename T, typename VST> void travIn_I1(BinNodePosi(T) x, VST & visit) { 
	    Stack<BinNodePosi(T)> S; //辅助栈
	    while (true) {
		      goAlongVine(x, S); //从当前节点出发,逐批入栈
		      if (S.empty()) break; //直至所有节点处理完毕
		      x = S.pop(); 
			  visits(x->data); //弹出栈顶节点并访问之
		      x = x->rc; //转向右子树
	 }
 }
//二叉树中序遍历算法(迭代版#2)
 template < typename T, typename VST> void travIn_I2(BinNodePosi(T) x, VST & visit) { 
	Stack<BinNodePosi(T)> S; //辅助栈
	while (true)
	       if (x) {
	          S.push(x); //根节点进栈
	          x = x->lc; //深入遍历左子树
	}else if (!S.empty()) {
		         x = S.pop(); //尚未访问的最低祖先节点退栈
		         visits(x->data); //访问该祖先节点
		         x = x->rc; //遍历祖先的右子树
	}
		else break; //遍历完成
}
//二叉树中序遍历算法(迭代版#3,无需辅助栈)
 template < typename T, typename VST> void travIn_I3(BinNodePosi(T) x, VST & visit) { 
	     bool backtrack = false; //前一步是否刚从左子树回溯——省去栈,仅O(1)辅助空间
	     while (true)
	       if (!backtrack && HasLChild(*x)) //若有左子树且不是刚刚回溯,则
	          x = x->lc; //深入遍历左子树
	   else { //否则——无左子树或刚刚回溯(相当于无左子树)
	          visits(x->data); //访问该节点
	          if (HasRChild(*x)) { //若其右子树非空,则
			            x = x->rc; //深入右子树继续遍历
			            backtrack = false; //并关闭回溯标志
		 }
		 else { //若右子树空,则
			             if (!(x = x->succ())) break; //回溯(含抵达末节点时的退出返回)
			             backtrack = true; //并设置回溯标志 
		 } 
	 }
 }
//二叉树中序遍历(迭代版#4,无需栈或标志位)
template < typename T, typename VST> void travIn_I4(BinNodePosi(T) x, VST & visit) {
	while (true)
	       if (HasLChild(*x)) //若有左子树,则
	          x = x->lc; //深入遍历左子树
	   else { //否则
	          visits(x->data); //访问当前节点,并
	          while (!HasRChild(*x)) //不断地在无右分支处
	            if (!(x = x->succ())) return; //回溯至直接后继(在没有后继的末节点处,直接退出)
	             else visits(x->data); //访问新的当前节点
	          x = x->rc; //(直至有右分支处)转向非空的右子树
	 }
 }
//二叉树中序遍历算法统一入口
 template <typename T> template < typename VST> void BinNode<T>::travIn(VST& visit) {
	 srand(time(NULL));//随机数种子
	  switch (rand() % 5) { //此处暂随机选择以做测试,共五种选择
	     case 1: std::cout << "调用了中序遍历(迭代1)"; travIn_I1(this, visit);std::cout<<std::endl; break; //迭代版#1
	     case 2: std::cout << "调用了中序遍历(迭代2)"; travIn_I2(this, visit);std::cout<<std::endl; break; //迭代版#2
	     case 3: std::cout << "调用了中序遍历(迭代3)"; travIn_I3(this, visit);std::cout<<std::endl; break; //迭代版#3
	     case 4: std::cout << "调用了中序遍历(迭代4)"; travIn_I4(this, visit);std::cout<<std::endl; break; //迭代版#4
	     default:std::cout << "调用了中序遍历(递归)";  travIn_R(this, visit); std::cout<<std::endl; break; //递归版
	   }

 }

3.4 后序遍历算法(2种)


//二叉树后序遍历算法(递归版)
 template < typename T, typename VST> void travPost_R(BinNodePosi(T) x, VST & visit) {
	   if (!x) return;
	   travPost_R(x->lc, visit);
	   travPost_R(x->rc, visit);
	   visits(x->data);
	
 }
//在以S栈顶节点为根的子树中,找到最高左侧可见叶节点
template < typename T> static void gotoLeftmostLeaf(Stack<BinNodePosi(T)>&S) { 
	//沿途所遇节点依次入栈
	    while (BinNodePosi(T) x = S.top()) //自顶而下,反复检查当前节点(即栈顶)
		        if (HasLChild(*x)) { //尽可能向左
		           if (HasRChild(*x)) S.push(x->rc); //若有右孩子,优先入栈
		           S.push(x->lc); //然后才转至左孩子
	 }
		 else //实不得已
	        S.push(x->rc); //才向右
	       S.pop(); //返回之前,弹出栈顶的空节点
 } 
//二叉树的后序遍历(迭代版)
template < typename T, typename VST> void travPost_I(BinNodePosi(T) x, VST & visit) {
	     Stack<BinNodePosi(T)> S; //辅助栈
	     if (x) S.push(x); //根节点入栈
	     while (!S.empty()) { //x始终为当前节点
		    if (S.top() != x->parent) 若栈顶非x之父(而为右兄)
		          gotoLeftmostLeaf(S); //则在其右兄子树中找到HLVFL(相当于递归深入)
		    x = S.pop(); visits(x->data); //弹出栈顶(即前一节点之后继),并访问之

	 }
 }

 //二叉树后序遍历算法统一入口
template <typename T> template < typename VST> void BinNode<T>::travPost(VST & visit) { 
	 srand(time(NULL));//随机数种子
	     switch (rand() % 2) { //此处暂随机选择以做测试,共两种选择
	        case 1: std::cout<<"调用了后序遍历(迭代)";travPost_I(this, visit); std::cout<<std::endl;
				break; //迭代版
			default:std::cout<<"调用了后序遍历(递归)"; travPost_R(this, visit);std::cout<<std::endl;
				break; //递归版	
	 }
 }

4. 完整代码

#include<iostream>
#include "BinTree.h"
using namespace std;


int main() {
	
	BinTree<int> b;
	BinNodePosi(int) root = b.insertAsRoot(0);
	b.insertAsLC(root,20);
	b.insertAsRC(root, 5);
	b.travPre(root);//前序遍历
	b.travIn(root);//中序遍历
	b.travPost(root);//后序遍历
	b.travLevel(root);//层次遍历
	
	
	cout << b.size()<<endl;
	BinTree<int>* k = b.secede(root->lc);
	cout << b.size() << endl;
	b.insertAsLC(root,4);
	b.attachAsLC(root->rc,k);
	cout << b.size() << endl;
	b.travIn(root);	
	cout << endl;
	system("pause");
	return 0;
}

5. 运行结果截图

猜你喜欢

转载自blog.csdn.net/qq_58240448/article/details/127480205
今日推荐