二叉树的深度优先dfs遍历(前序、中序和后序;递归与非递归)

二叉树本身就是用递归定义的,因此采用递归方法实现dfs的三种遍历不仅容易理解而且代码简洁。

若用非递归实现,则要采用栈去模拟:保存计算的上下文,使得计算在返回上一级时有路可循。注意每一个结点都要当作根结点来看待。

//前序遍历
//递归实现:根左右
void preOrder1(BinTree *root)
{
    if (root != NULL)
    {
        cout<<root->data<<endl;
        preOrder1(root->lchild);
        preOrder1(root->rchild);
    }
}

//非递归实现
/*注意,如前所述,我们将二叉树的每一个结点都看作根结点。因此,从整个二叉树的根结点root出发,一路向左,遇到的每一个结点都立即访问(它是根,同时也是其父亲的左子树的根,所以这个过程访问了“根和左”)并入栈,直到不能再左,转向右(这个“右”上哪找呢?当然是父亲的右儿子。父亲去哪里找呢?当然是栈里),将这个右儿子当成新的根结点重复上述过程,直到栈为空且当前根结点也为空。*/
void preOrder2(BinTree *root)
{
	stack<BinTree *> s;
	BinTree *p=root;
 	while (p!=NULL || !s.empty())
 	{
  		//一路向左
  		while (p!=NULL)
		{      
			cout<<p->data<<endl;	 //访问根结点
			s.push(p);
   			p=p->lchild;
		}
 
		//当不能再左时,开始向右
  		if (!s.empty())
  		{
   			p=s.top();//从栈里面取出根结点
   			s.pop();
   			p=p->rchild;            //作为新的根结点
  		}
 	}
 }

//中序遍历
//递归实现:左根右
void inOrder1(BinTree *root)
{
	if (root != NULL)
	{
  		inOrder1(root->lchild);
  		cout<<root->data<<endl;
  		inOrder1(root->rchild);
 	}
}

//中序遍历 
//非递归实现
/*与前序遍历类似,但是,根结点进栈时不访问(否则就成了前序遍历),根结点弹栈时才访问(左根右)。*/
void inOrder2(BinTree *root)
{
	stack<BinTree *> s;
	BinTree *p=root;
 
	while (p!=NULL || !s.empty())
	{
  		//一路向左
 		while (p!=NULL)
 		{
 			s.push(p);
 			p=p->lchild;
 		}
 
  		//当不能再左时,访问根结点,向右
  		if (!s.empty())
  		{
   			p=s.top();
   			cout<<p->data<<endl;	//在中间访问根结点
   			s.pop();
   			p=p->rchild;
  		}
	}
}

//后序遍历
//递归实现:左右根
void postOrder1(BinTree *root)
{
	if (root != NULL)
	{
		postOrder1(root->lchild);
		postOrder1(root->rchild);
		cout<<p->data<<endl;
	}
}
//顺便说一句,删除一棵二叉树,即释放一棵二叉树的内存,用后续遍历即可实现(这里的“访问”变成了delete 结点)。

//后序遍历
//非递归实现
/*对任一结点p,边一路向左边进栈,直到其左儿子为空,这时,各个根结点在栈里存放。然后依次出栈,但是此时还不能访问(否则就是中序遍历了),出栈之后以当前根节点的右儿子设置为新的根节点,重复前述过程;直到当前根节点第二次出栈(说明它的左右儿子都已被访问),然后访问此根结点(左右根)。*/
typedef struct _poNode
{
	BinTree *btnode;
	bool isFirst;
} poNode;

void postOrder2(BinTree *root)
{
	stack<poNode *> s;
	BinTree *p=root;
 
	poNode *temp;
 
	while (p!=NULL || !s.empty())
	{
 		//一路向左直到不能再左
		while (p!=NULL)
		{
			temp = (poNode *)malloc(sizeof(poNode));
			temp->btnode = p;
			temp->isFirst = True;	 //第一次进栈标记
			s.push(temp);
 
			p=p->lchild;
		}
 
		if (!s.empty())
		{
			temp = s.top();	 //此时还不能访问,否则就是中序遍历了
			s.pop();
 
			//如果是第一次进栈,那还需要再进栈一次,之后以它的右儿子为新的根结点
			if (temp->isFirst == true)
			{
				temp->isFirst = false;
				s.push(temp);
				p = temp->btnode->rchild;
 			}
			else
			{
				cout<<temp->btnode->data<<endl;	//后序访问根结点
				p = NULL;	//不要忘了这一句,因为访问过根结点之后应该直接弹栈考察上一个父结点
			}
 		}
 	}
 }

总结 :所有结点都看作根结点,关键在于何时访问。前序:入栈时访问;中序:第一次退栈时访问;后序:第二次退栈时访问。

你可能感兴趣的:(非递归,DFS,中序遍历,后序遍历,前序遍历)