【C++】二叉树的创建方法及其遍历的递归与非递归方法总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Chengzi_comm/article/details/48464279

    在计算机科学中,二叉树是每个节点最多只有两个子树的结构。通常字数被称作“左子树”(left subChild)和“右子树”(right subChild).

例如下面的二叉树:


二叉树性质

(1) 在非空二叉树中,第i层的结点总数不超过
, i>=1;
(2) 深度为h的二叉树最多有
个结点(h>=1),最少有h个结点;
(3) 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;
(4) 具有n个结点的完全二叉树的深度为
(5)有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:
若k为结点编号则 如果k>1,则其父结点的编号为k/2;
如果2*k<=N,则其左孩子(即左子树的根结点)的编号为2*k;若2*k>N,则无左孩子;
如果2*k+1<=N,则其右孩子的结点编号为2*k+1;若2*k+1>N,则无右孩子。

下面给出二叉树的创建方法及其遍历的递归与非递归方法:
下面是BinTree.h 实现文件:
#pragma once

#include<iostream>
#include<assert.h>
#include<stack>
#include<queue>
using namespace std;

template<class Type>
class BinTree;

template<class Type>
class BinTreeNode    // 树中的节点类
{
	friend class BinTree<Type>;
public:
	/* 树节点的构造函数*/
	BinTreeNode():value(Type()), leftChild(NULL), rightChild(NULL)
	{}               
	BinTreeNode(Type data):value(data), leftChild(NULL), rightChild(NULL)
	{}
	~BinTreeNode()
	{}
private:					// 树中节点的结构 
	Type value;                       //值类型
	BinTreeNode<Type> *leftChild;     // 指向左孩子
	BinTreeNode<Type> *rightChild;	  // 指向右孩子
};

/////////////////////////////////////////////////////
typedef enum{L,R}Tag_Type;

/* 这个结构用来实现树的非递归后序遍历,后面再讲*/
template<class Type>
struct stkNode        
{
	BinTreeNode<Type> *str;
	Tag_Type           Tag;
};
/////////////////////////////////////////////////////

/* 二叉树类*/
template<class Type>
class BinTree
{
public:
	/* '#'是自定义的一个标记,表示该节点为空*/
	BinTree():root(NULL), refvalue('#')
	{}
	
        /* 第一种构造树的方法,直接按先序遍历写出树中所有节点,
	包括叶子节点的两个空左右孩子【用'#'代替,例如 ABC##DE##F##G#H##】*/
	void CreateBinTree_w()
	{
		CreateBinTree(root);  // CreateBinTree_w为树对外的接口,CreateBinTree为具体实现,为私有方法
	}
	
	/* 第二种构造方法,把先序遍历的字符串以参数形式穿进去*/
	void CreateBinTree_str(const char *str)
	{
		CreateBinTree(root, str);
	}
	
	/* 根据先序遍历和中序遍历的结果唯一构造二叉树,
	三个参数分别为先序遍历字符串、中序遍历字符串、树的节点个数*/
	void CreateBinTree_Pre(char *VLR, char *LVR, int n)    
	{           
		CreateBinTree_Pre(root, VLR, LVR, n); 
	}
	
	/* 根据后序遍历和中序遍历的结果唯一构造二叉树,
	三个参数分别为后序遍历字符串、中序遍历字符串、树的节点个数*/
	void CreateBinTree_Post(char *LRV, char *LVR, int n)
	{
		CreateBinTree_Post(root, LRV, LVR, n);
	}
	
	/* 求树中的节点个数*/
	int Size()const
	{		
		return Size(root);
	}
	
	/* 求树的高度*/
	int Height()const
	{
		return Height(root);
	}
	
	/* 求树的根节点*/
	BinTreeNode<Type>* Root()const
	{
		return root;
	}
	
	/* 求指针p所指节点的左孩子*/
	BinTreeNode<Type>* LeftChild(BinTreeNode<Type>* p)const
	{
		if(NULL == p)
			return p;
		return p->leftChild;
	}
	
	/* 求指针p所指节点的右孩子*/
	BinTreeNode<Type>* RightChild(BinTreeNode<Type>* p)const
	{	
		if(NULL == p)
			return p;
		return p->rightChild;
	}	
	
	/* 求指针p所指节点的双亲节点*/
	BinTreeNode<Type>* Parent(BinTreeNode<Type>* p)
	{
		return Parent(root, p);
	}
	
	/* 求值为key的节点的双亲节点*/
	BinTreeNode<Type>* Parent(const Type &key)
	{
		return Parent(Search(key));
	}
	
	/* 查找值为key的节点,返回其地址*/
	BinTreeNode<Type>* Search(const Type &key)
	{
		return Search(root, key);
	}
	
	/* 按层遍历二叉树*/
	void LevelOrder()
	{
		LevelOrder(root);
	}
	
	/* 判断二叉树是否为空*/
	bool Empty()const
	{
		return (root == NULL);
	}
	
	/* 判断两个二叉树是否相等*/
	bool Equal(BinTree<Type> &bt)
	{
		return Equal(root, bt.root);
	}
	
	/* 把一个树拷贝到另一个非空树中,(首先释放要拷贝的树)*/
	void Copy(const BinTree<Type> &bt)const
	{
		DistroyBinTree();
		Copy(root, (BinTreeNode<Type>*)bt.root);
	}
	
	/* 摧毁二叉树*/
	void DistroyBinTree()
	{
		DistroyBinTree(root);
	}
	///////////////////////////////////////////
	/* 递归形式 先序遍历二叉树*/
	void PreOrder()      
	{
		PreOrder(root);
	}
	
	/* 中序遍历*/
	void InOrder()   
	{
		InOrder(root);
	}
	
	/* 后序遍历*/
	void PostOrder()
	{
		PostOrder(root);
	}
	///////////////////////////////////////////
	/* 非递归形式 先序遍历二叉树(以‘_’结束) */
	void PreOrder_()   //
	{
		PreOrder_(root);
	}	
	
	void InOrder_()
	{
		InOrder_(root);
	}	
	
	void PostOrder_()
	{
		PostOrder_(root);
	}
	////////////////////////////////////////////
	
	/* 下面是各类函数的具体实现*/
private:
	
	/* 手写先序遍历字符串构造二叉树*/
	void CreateBinTree(BinTreeNode<Type> *&t)
	{
		Type Item;
		cin>>Item;
		if(Item == refvalue)
			t = NULL;
		else
		{
			t = new BinTreeNode<Type>(Item);  // 首先构造根节点
			CreateBinTree(t->leftChild);      // 构造左孩子
			CreateBinTree(t->rightChild);     // 构造右孩子
		}
	}
	
	/* 以参数形式传入先序遍历字符串,构造二叉树*/
	void CreateBinTree(BinTreeNode<Type> *&t, const char *&str)
	{
		 if(*str=='#' || *str=='\0')
			t = NULL;
		else
		{
			t = new BinTreeNode<Type>(*str);
			CreateBinTree(t->leftChild, ++str);
			CreateBinTree(t->rightChild,++str);
		}
	}
	
	/* 根据先序遍历和中序遍历的结果唯一构造二叉树,
	三个参数分别为先序遍历字符串、中序遍历字符串、树的节点个数*/
	void CreateBinTree_Pre(BinTreeNode<Type> *&t, char *VLR, char *LVR, int n)
	{
		if(0 == n)
			return ;
		else
		{
			int k = 0;
			while(VLR[0] != LVR[k])
			{
				k++;
			}
			
			t = new BinTreeNode<Type>(LVR[k]);
			CreateBinTree_Pre(t->leftChild,VLR+1,LVR,k);
			CreateBinTree_Pre(t->rightChild,VLR+k+1,LVR+k+1,n-k-1);
		}
	}
	
	/* 根据后序遍历和中序遍历的结果唯一构造二叉树,
	三个参数分别为后序遍历字符串、中序遍历字符串、树的节点个数*/
	void CreateBinTree_Post(BinTreeNode<Type> *&t, char *LRV, char *LVR, int n)
	{
		if(0 == n)
			return ;
		else
		{
			int k = n-1;
			while(LRV[n-1] != LVR[k])
				k--;
			t = new BinTreeNode<Type>(LVR[k]);
			CreateBinTree_Post(t->leftChild, LRV, LVR, k);
			CreateBinTree_Post(t->rightChild, LRV+k,LVR+k+1,n-k-1); 
		}
	}
	/* 递归形式遍历*/
	void PreOrder(BinTreeNode<Type> *t)
	{
		if(NULL != t)
		{
			cout<<t->value<<" ";
			PreOrder(t->leftChild);
			PreOrder(t->rightChild);
		}
	}
	
	void InOrder(BinTreeNode<Type> *t)
	{
		if(NULL != t)
		{
			InOrder(t->leftChild);
			cout<<t->value<<" ";
			InOrder(t->rightChild);
		}
	}
	
	void PostOrder(BinTreeNode<Type> *t)
	{
		if(NULL != t)
		{
			PostOrder(t->leftChild);
			PostOrder(t->rightChild);
			cout<<t->value<<" ";
		}
	}
	
	/* 求树中节点个数*/
	int Size(BinTreeNode<Type> *t)const
	{
		if(NULL == t)
			return 0;
		else
			return Size(t->leftChild) + Size(t->rightChild) + 1;
	}	
	
	/* 求树的高度*/
	int Height(BinTreeNode<Type> *t)const
	{
		int Height = 0;
		int lcHeight = 0;
		int rcHeight = 0;
		if(NULL != t)
		{
			lcHeight = Height(t->leftChild);
			rcHeight = Height(t->rightChild);
			Height = lcHeight>rcHeight ? lcHeight+1 : rcHeight+1;
		}
		return Height;
	}
	
	
	BinTreeNode<Type> *Parent(BinTreeNode<Type> *t, BinTreeNode<Type> *pkey)
	{
		assert(NULL != pkey);
		
		if(NULL == t)
			return NULL;
		if(t->leftChild==pkey || t->rightChild==pkey)
			return t;
		BinTreeNode<Type> *p = Parent(t->leftChild, pkey);
		if(NULL != p)
			return p;
		else 
			return Parent(t->rightChild, pkey);
	}
	
	/* 查找节点key,返回该节点的指针*/
	BinTreeNode<Type> *Search(BinTreeNode<Type> *t, const Type &key)
	{
		if(NULL == t)
			return NULL;
		else
		{
			if(t->value == key)
				return t;
			BinTreeNode<Type> *p = Search(t->leftChild, key);
			if(NULL != p)
				return p;
			return Search(t->rightChild, key);
		}		
	}	
	
	/* 判断两棵树是否相等*/
	bool Equal(const BinTreeNode<Type> *t, const BinTreeNode<Type> *p)
	{
		if(NULL==t && NULL==p)
			return true;
		if(t!=NULL && p!=NULL && t->value==p->value && Equal(t->leftChild, p->leftChild) && Equal(t->rightChild, p->rightChild))
			return true;
		return false;
	}	
	
	/* 拷贝一棵树到另一颗非空树中*/
	void Copy(BinTreeNode<Type> *&t, BinTreeNode<Type> *&p)
	{
		if(NULL == p)
			return ;
		t = new BinTreeNode<Type>(p->value);
		Copy(t->leftChild, p->leftChild);
		Copy(t->rightChild, p->rightChild);
	}
	
	/* 摧毁二叉树*/
	void DistroyBinTree(BinTreeNode<Type> *&t)
	{
		if(NULL != t)
		{
			DistroyBinTree(t->leftChild);
			DistroyBinTree(t->rightChild);
			delete t;
			t = NULL;
		}
	}
	
	/* 按层遍历二叉树,借助一个队列*/
	void LevelOrder(BinTreeNode<Type> *t)
	{
		if(NULL != t)
		{
			queue<BinTreeNode<Type>* > que;
			BinTreeNode<Type> *p;
			que.push(t);          // 根节点压入队列
			while(!que.empty())
			{
				p = que.front();
				que.pop();
				cout<<p->value<<" ";      // 出栈访问
				if(NULL != p->leftChild)
					que.push(p->leftChild);   //左孩子 先进先出【保证了左在右之前】
				if(NULL != p->rightChild)
					 que.push(p->rightChild);
			 }
		}
	}	
	
	/* 二叉树的先序遍历【非递归】,需要借助一个栈结构*/
	void PreOrder_(BinTreeNode<Type> *t)
	{
		if(NULL != t)
		{
			stack<BinTreeNode<Type>* > st;
			BinTreeNode<Type> *p;
			st.push(t);       // 把根节点入栈
			while(!st.empty())
			{
				p = st.top();   // p指向栈顶元素 
				st.pop(); 
				cout<<p->value<<" ";      // 出栈访问该节点
				if(NULL != p->rightChild)     // 该节点的右孩子压栈,保证左孩子先出栈
					st.push(p->rightChild);
				if(NULL != p->leftChild)      // 左孩子入栈
					st.push(p->leftChild);
			}
		}                       // 循环结束,栈空,二叉树访问完
		cout<<endl;
	}	
	
	/* 二叉树的中序遍历【非递归】,需要借助一个栈结构*/
	void InOrder_(BinTreeNode<Type> *t)  
	{  
		if(t==NULL)  
			return;  
		stack<BinTreeNode<Type>* > st;  
		BinTreeNode<Type>*p = t;  
		
		while (p!=NULL||!st.empty())
		{  
			while(p!=NULL)  
			{  
				st.push(p);        // 首先一直左孩子压栈
				p = p->leftChild;
			}
			p=st.top();            // p 接收栈顶元素
			st.pop();              // 出栈访问
			cout<<p->value<<" "; 
			p=p->rightChild;  	   // 指向右孩子,下一个循环 右孩子压栈
		}   
	}  
	
	/* 二叉树的后序遍历【非递归】,需要借助一个栈结构和一个结构sn(把一个节点指针和一个标记封装)*/
	void PostOrder_(BinTreeNode<Type> *t)
	{
		if(NULL != t)
		{
			stkNode<Type> sn;
			stack< stkNode<Type> > st;
			do
			{
				while(NULL != t)    // 把节点指针和标记封装
				{
					sn.str = t;
					sn.Tag = L;     // 标记为L 表示是第一次访问该节点,出栈的时候先不能访问
					st.push(sn);
					t = t->leftChild;
				}
				
				while(!st.empty())    
				{
					sn = st.top();       // sn接收栈顶元素
					t = sn.str->rightChild;    // 先记下栈顶元素的右孩子,下一个do while循环要把右孩子压栈
					st.pop();
					if(sn.Tag == L)      // 为L 表示是第一次访问该节点,出栈的时候先不能访问 
					{
						sn.Tag = R;  
						st.push(sn);     // 把标记改为R后继续把sn压栈
						break;           // 结束本次循环
					}
					else
					{
						cout<<sn.str->value<<" "; // 为R 表示是第二次访问该节点,可以访问
					}
				}
			}while(!st.empty());
		}
	}
	
private:
	BinTreeNode<Type> *root;   // 树的根
	Type refvalue;             // 根据refvalue判断一个节点是否为空
};


下面是测试代码Main.cpp:
#include"BinTree.h"

/* 测试代码太多,这里只给出一个,*/
void main()
{
	//char *str = "ABC##DE##F##G#H##";
	BinTree<char> bt;
	char *VLR = "ABCDEFGH";
	char *LVR = "CBEDFAGH";
	char *LRV = "CEFDBHGA";

	bt.CreateBinTree_Post(LRV, LVR, 8);

	bt.PreOrder_();
}

下面是程序结果:


猜你喜欢

转载自blog.csdn.net/Chengzi_comm/article/details/48464279