二叉树遍历(前序中序后序递归迭代共6种)

参考博客

总体把握:

层次遍历使用 BFS 实现,利用的就是 BFS 一层一层遍历的特性;
而前序、中序、后序遍历利用了 DFS 实现。

前序、中序、后序遍只是在对节点访问的顺序有一点不同,其它都相同。

递归版本的实现都很简洁
为了把一个递归过程改为非递归过程,就需要自己维护一个辅助栈结构,记录遍历时的回退路径。非递归的快速排序的设计依据也是这个。


1.前序遍历

#递归版本
void PreOrder(BinaryTreeNode* pRoot)
{
	if (pRoot!=NULL)
	{
		visit(*pRoot);                                //visit只是代表处理,关键的是结构
		PreOrder(pRoot->m_pLeft);
		PreOrder(pRoot->m_pRight);
	}	
}
#非递归版本
#include
void PreOrder(BinaryTreeNode* pRoot)
{
	if (pRoot==NULL)
		return;
	std::stack S;
	BinaryTreeNode *p=pRoot;            //二叉树分左右,所以光有栈不行,合理的运用遍历指针是关键之一
	while(p!=NULL)
	{
		visit(p);
		if (p->m_pRight!=NULL)
			S.push(p->m_pRight);
		if (p->m_pLeft!=NULL)
			p=p->m_pLeft;
		else
		{
			if (S.empty())
				break;
			p=S.top();
			S.pop();
		}
	}
}

2.中序遍历

典例:二叉树转双向链表

#递归版本
void InOrder(BinaryTreeNode *pRoot)
{
	if(pRoot !=null)
	{
		InOrder(pRoot->m_pLeft);
		visit(*pRoot);
		InOrder(pRoot->m_pRight);
	}
}
#非递归版本
#include
void InOrder(BinaryTreeNode* pRoot)
{
	if (pRoot==NULL)
		return;
	std::stack S;
	BinaryTreeNode *p=pRoot;
	do 
	{
		while(p!=NULL)
		{
			S.push(p);
			p=p->m_pLeft;
		}
		//若进行到这里左子树为空
		if (!S.empty())               //Stack不空时退栈,然后访问该元素
		{
			p=S.top();
			S.pop();
			visit(p);
			p=p->m_pRight;
		}
	} while (p!=NULL||!S.empty());
	//这里的p==NULL表示右子树为空,然后堆栈如果也空的话,才是处理完毕
}

3.后序遍历

#递归版本
void PostOrder(BinaryTreeNode *pRoot)
{
	if(pRoot !=null)
	{
		PostOrder(pRoot->m_pLeft);
		PostOrder(pRoot->m_pRight);
		visit(*pRoot);
	}
}
#非递归版本
/* 迭代后序遍历比较复杂。在遍历完左子树时还不能访问根节点,需要再遍历右子树。待右子树遍历完后才访问根节点。
所以在辅助栈工作记录中必须注明节点是在左子树还是在右子树。
记住:二叉树后序遍历的非递归调用需要用 pair 类型,因为需要记录是左进还是右进,所以每个节点必然会进栈两次!!! */

using namespace std;

void PostOrder(BinaryTreeNode* pRoot)
{
	if (pRoot==NULL)
		return;
		
	pair w;
	stack > S;
	BinaryTreeNode *p=pRoot;
	      
	do 
	{
		while(p!=NULL)           //左子树经过节点加L进栈
		{
			w.first=p;
			w.second='L';
			S.push(w);
			p=p->m_pLeft;
		}
		bool continuel=true;                   //继续循环标志,用于L改为R的时候就开始向右遍历
		while (continuel && !S.empty())        //用一个break语句也能实现循环标志continuel的功能
		{
			w=S.top();
			S.pop();
			p=w.first;
			if (w.second=='L')      //标记为L表示左子树遍历完
			{
				w.second='R';
				S.push(w);
				continuel=false;
				p=p->m_pRight;
			}
			else
				visit(p);                  //如果标记为R,表示右子树遍历完
		}
	}while (!S.empty());
}

附:对后续遍历非递归版本的理解:

【1】考虑到每个节点都会访问两次,并且在第二次访问时候才会进行操作(如打印操作),因此需要一个标志位来进行访问标记,所有使用了pair.second来存放访问标志;
【2】当该标志位为L时,表示该节点是第一次访问,并且其左子树已经遍历完,此时就应该把标志位换为R,然后将其再次入栈;
【3】当再次访问到这个节点的时候,此时标志位为R,因此其右子树已经遍历完,此时就可以多该节点进行操作(如打印操作);
因此从理解上还是很好理解的,无非是在引入标志位后,各个节点多了一次入栈和出栈的过程而已;

你可能感兴趣的:(基本算法)