【C++】二叉树的遍历与生成

一、二叉树的广度优先遍历

广度优先遍历也叫层次遍历,从最高层开始,向下逐层访问每个结点,在每一层上,从左向右访问每个结点。

原理:开辟一个队列,创建一个指向当前结点的pointer指针保存根结点,并使其入队(此时判断根结点是否为空)。在访问完pointer指向的结点后,pointer指向的结点出队,让其左孩子和右孩子依次入队,然后使pointer指向队头结点,并重复以上步骤,直到队空为止。此过程可以保证第n层元素在第n+1层元素之前访问。

template<class T>
void BinaryTree<T>::levelOrder ( BinaryTreeNode<T> *root )
{
	using std::queue;
	queue<BinaryTreeNode<T> *> nodeQueue;
	BinaryTreeNode<T> *pointer = root;
	if ( pointer ) //判断根结点是否为空
	{
		nodeQueue.push ( pointer );
	}
	while ( !nodeQueue.empty() )
	{
		pointer = nodeQueue.front();  //每次循环把指针置为队头元素
		visit ( pointer );
		nodeQueue.pop();//访问后当前结点出队
		if ( pointer->getLeftChild() )
		{
			nodeQueue.push ( pointer->getLeftChild() );//左孩子入队
		}
		if ( pointer->getRightChild() )
		{
			nodeQueue.push ( pointer->getRightChild() );//右孩子入队
		}
	}
}

注:使用STL的队列需要调用头文件#include<queue>

二、二叉树的深度优先遍历

1、前序遍历(先序遍历)

遍历次序:根-左子树-右子树

(1)、递归算法

template<class T>
void BinaryTree<T>::preOrder ( BinaryTreeNode<T> *root )
{
	if ( root )
	{
		visit ( root );
		preOrder ( root->getLeftChild() );
		preOrder ( root->getRightChild() );
	}
}

(2)、非递归算法

原理:先用pointer指针保存根结点,然后进入循环。先访问当前结点,然后判断右孩子是否为空,非空则进栈。再判断左孩子是否为空,并且把pointer指针置为当前结点的左孩子。如果当前结点为空,则栈顶元素赋给当前结点pointer并且弹出栈顶元素。重复以上步骤直到栈空为止。

扫描二维码关注公众号,回复: 5023885 查看本文章
template<class T>
void BinaryTree<T>::preOrderWithoutRecusion ( BinaryTreeNode<T>* root )
{
	using std::stack;
	stack<BinaryTreeNode<T> *> nodeStack;
	BinaryTreeNode<T> *pointer = root;//根结点置为当前结点
	while ( !nodeStack.empty() || pointer )//第一次栈空且根结点非空
	{
		if ( pointer )
		{
			visit ( pointer );//先访问子树根结点
			if ( root->getRightChild() )//右孩子非空则进栈
			{
				nodeStack.push ( pointer->getRightChild() );
			}
			pointer = pointer->getLeftChild();//指针置为左孩子
		}
		else//如果当前结点的左孩子为空
		{
			pointer = nodeStack.top();//指针置为栈顶元素
			nodeStack.pop();//弹出栈顶元素
		}
	}
}

2、中序遍历

遍历次序:左子树-根结点-右子树

小规律:

(1)、递归算法

template<class T>
void BinaryTree<T>::inOrder ( BinaryTreeNode<T> *root )
{
	if ( root )
	{
		inOrder ( root->getLeftChild() );
		visit ( root );
		inOrder ( root->getRightChild() );
	}
}

(2)、非递归算法

原理:pointer指针置为根结点并开始循环,pointer每遇到一个结点就直接进栈,然后把pointer置为其左孩子,如果左孩子为空,访问栈顶元素并弹出,再把pointer置为右孩子,直到栈空。

template<class T>
void BinaryTree<T>::inOrderWithoutRecusion ( BinaryTreeNode<T>* root )
{
	using std::stack;
	stack<BinaryTreeNode<T> *> nodeStack;
	BinaryTreeNode<T> *pointer = root;
	while ( !nodeStack.empty() || pointer )
	{
		if ( pointer )//碰到一个结点就进栈
		{
			nodeStack.push ( pointer );
			pointer = pointer->getLeftChild();//指针指向左孩子
		}
		else
		{
			pointer = nodeStack.top();//左右孩子空就出栈
			visit ( pointer );//访问栈顶结点
			pointer = pointer->getRightChild();//指针指向右孩子
			nodeStack.pop();//删除栈顶结点
		}
	}
}

3、后序遍历

遍历次序:左子树-右子树-根结点

(1)、递归算法

template<class T>
void BinaryTree<T>::postOrder ( BinaryTreeNode<T> *root )
{
	if ( root )
	{
		postOrder ( root->getLeftChild() );
		postOrder ( root->getRightChild() );
		visit ( root );
	}
}

(2)、非递归算法

原理:创建一个指向当前结点的指针pointer和一个指向前一个被访问的结点的指针pre,如果当前结点非空,则进入循环。

若左孩子非空则进栈直到左孩子为空,转而访问右孩子,若右孩子非空则进栈,右孩子空或已经访问过则访问当前结点,访问后把pre指针置为当前结点表示已经访问过,然后弹出栈顶元素并赋给pointer指针。此时有两种情况,第一是当前结点的右孩子为空或者访问过,则访问当前结点;第二是当前结点的右孩子没有访问过,则把刚弹出的当前结点再压进栈中,然后去访问其右孩子并重复以上步骤直到栈空为止。(对栈顶元素的判断是为了保证访问完其左子树和右子树之后才能访问该结点)

template <class T>
void BinaryTree<T>::PostOrderWithoutRecusion ( BinaryTreeNode<T> * root )
{
	stack<BinaryTreeNode<T> * > nodeStack;      //存储待访问结点
	BinaryTreeNode<T> * pointer = root;             //保存根结点
	BinaryTreeNode<T> * pre = root;         //保存前一个被访问的结点

	while ( pointer )
	{
		for ( ; pointer->getLeftChild() != NULL; pointer = pointer->getLeftChild() )
			nodeStack.push ( pointer );     //向左搜索

		while ( pointer != NULL && ( pointer->getRightChild() == NULL || pointer->getRightChild() == pre ) )
		{
			//当前结点没有右孩子或者右孩子刚被访问过,则访问该结点
			visit ( pointer );
			pre = pointer;                          //记录刚被访问过的结点
			if ( nodeStack.empty() )
				return;
			pointer = nodeStack.top();      //取栈顶结点
			nodeStack.pop();
		}
		nodeStack.push ( pointer );
		pointer = pointer->getRightChild();     //转向当前结点的右子树
	}
}

三、二叉树的生成

每次运行函数都生成一个树或者子树的根结点

必须给定中序序列,前序序列和后序序列的作用都是找根结点:前序序列的第一个元素是根结点,后序序列的最后一个元素是根结点。

1、任何n(n≥0)个不同结点的二叉树都可以由它的前序序列和中序序列唯一确定。

template<class T>
BinaryTreeNode<T>* BinaryTree<T>::createFunction1 ( string m, string n )
{
	root = new BinaryTreeNode<T>;
	preinCreateTree ( root, m, n );
	return root;
}
template<class T>
void BinaryTree<T>::preinCreateTree ( BinaryTreeNode<T>* t, string pre, string in )
{
	if( pre.size() != in.size())
	{
		t = NULL;
		cout << "数据有误,创建失败" << endl;
		return;
	}
	char tvalue = pre[0];//获得根结点的值
	int index = in.find ( tvalue );//获得根结点在中序序列中的下标
	string leftchild_in = in.substr ( 0, index );//获得左子树的中序序列
	string rightchild_in = in.substr ( index + 1 );//获得右子树的中序序列
	int leftchild_length = leftchild_in.size();//计算左子树的结点个数
	int rightchild_length = rightchild_in.size();//计算右子树的结点个数
	string leftchild_pre = pre.substr ( 1, leftchild_length );//获得左子树的前序序列
	string rightchild_pre = pre.substr ( leftchild_length + 1 );//获得右子树的前序序列
	if(leftchild_length == 0 && rightchild_length == 0)
	{
		//如果左右子树都不存在,插入当前根结点
		t->setValue(tvalue);
	}
	else if(leftchild_length == 0 && rightchild_length > 0)
	{
		//如果右子树存在,插入当前根结点后对其右子树进行相同操作
		t->setValue(tvalue);
		t->createRightChild();
		preinCreateTree(t->getRightChild(),rightchild_pre,rightchild_in);
	}
	else if(leftchild_length > 0 && rightchild_length == 0)
	{
		//如果左子树存在,插入当前根结点后对其左子树进行相同操作
		t->setValue(tvalue);
		t->createLeftChild();
		preinCreateTree(t->getLeftChild(),leftchild_pre,leftchild_in);
	}
	else if(leftchild_length > 0 && rightchild_length > 0)
	{
		//如果左右子树都存在,插入当前根结点后对其左右子树进行相同操作
		t->setValue(tvalue);
		t->createRightChild();
		t->createLeftChild();
		preinCreateTree(t->getLeftChild(),leftchild_pre,leftchild_in);
		preinCreateTree(t->getRightChild(),rightchild_pre,rightchild_in);
	}
}

2、任何n(n≥0)个不同结点的二叉树都可以由它的后序序列和中序序列唯一确定

template<class T>
BinaryTreeNode<T>* BinaryTree<T>::createFunction2(string m, string n)
{
	root = new BinaryTreeNode<T>;
	inpostCreateTree ( root, m, n );
	return root;
}
template<class T>
void BinaryTree<T>::inpostCreateTree(BinaryTreeNode<T>* t, string in, string post)
{
	if( post.size() != in.size())
	{
		t = NULL;
		cout << "数据有误,创建失败" << endl;
		return;
	}
	char tvalue = post[post.size() - 1];//获得根结点的值
	int index = in.find(tvalue);//获得根结点在中序序列中的下标
	string leftchild_in = in.substr(0,index);//获得左子树的中序序列
	string rightchild_in = in.substr(index+1);//获得右子树的中序序列
	int leftchild_length = leftchild_in.size();//计算左子树的结点个数
	int rightchild_length = rightchild_in.size();//计算右子树的结点个数
	string leftchild_post = post.substr(0,leftchild_length);//获得左子树的后序序列
	string rightchild_post = post.substr(leftchild_length,rightchild_length);//获得右子树的后序序列
	if(leftchild_length > 0 && rightchild_length > 0)
	{
		//如果左右子树都存在,插入当前根结点后对其左右子树进行相同操作
		t->setValue(tvalue);
		t->createLeftChild();
		t->createRightChild();
		inpostCreateTree(t->getLeftChild(),leftchild_in,leftchild_post);
		inpostCreateTree(t->getRightChild(),rightchild_in,rightchild_post);
	}
	else if (leftchild_length > 0 && rightchild_length == 0)
	{
		//如果左子树存在,插入当前根结点后对其左子树进行相同操作
		t->setValue(tvalue);
		t->createLeftChild();
		inpostCreateTree(t->getLeftChild(),leftchild_in,leftchild_post);
	}
	else if (leftchild_length == 0 && rightchild_length > 0)
	{
		//如果右子树存在,插入当前根结点后对其右子树进行相同操作
		t->setValue(tvalue);
		t->createRightChild();
		inpostCreateTree(t->getRightChild(),rightchild_in,rightchild_post);
	}
	else if (leftchild_length == 0 && rightchild_length == 0)
	{
		//如果左右子树都不存在,插入当前根结点
		t->setValue(tvalue);
	}
}

猜你喜欢

转载自blog.csdn.net/L____________/article/details/83859011