2015-03-15---二叉树递归(非递归)实现先序、中序、后序遍历(附代码)

今天说好的不碰代码的,后来还是没忍住,学了学数据结构和算法,就先讲讲先序中序和后序遍历吧,我还写了代码,一套递归方式实现遍历二叉树,还有两套非递归方式遍历二叉树,

从简单的开始,因为二叉树的所有操作都是要求在能够遍历的基础上啊。

学过数据结构的肯定都明白遍历顺序,

先序遍历就是先自己,然后左子树,然后右子树,

中序遍历就是先左子树,然后自己,然后右子树

后序遍历就是先左子树,然后右子树,然后自己

比如上图这个很简单的二叉树:

先序遍历:1 2 4 5 3 6 7

中序遍历:4 2 5 1 6 3 7

后序遍历:4 5 2 6 7 3 1

具体的遍历方法就是我们上面说的啊。

不说了,我们用代码实现:

先说简单的递归:

class Node  //树的节点,没有用c++的模板,我们只说算法,模板慢慢来
{
public:
	int data;
	Node *pLeft = NULL;
	Node *pRight = NULL;
};
Node *pRoot;//简单粗暴的方法初始化一棵树,重点不在这,大家自己进行脑补
	Node s1;
	Node s2;
	Node s3;
	Node s4;
	Node s5;
	Node s6;
	Node s7;

	s1.data = 1;
	s2.data = 2;
	s3.data = 3;
	s4.data = 4;
	s5.data = 5;
	s6.data = 6;
	s7.data = 7;

	pRoot = &s1;
	s1.pLeft = &s2;
	s1.pRight = &s3;
	s2.pLeft = &s4;
	s2.pRight = &s5;
	s3.pLeft = &s6;
	s3.pRight = &s7;

接下来才是重点:

void qiandigui(Node *pRoot)	//递归方法,先序遍历二叉树
{
	if (pRoot == nullptr)
	{
		return;
	}
	cout << pRoot->data << " ";
	if (pRoot->pLeft != nullptr)
	{
		qiandigui(pRoot->pLeft);
	}
	if (pRoot->pRight != nullptr)
	{
		qiandigui(pRoot->pRight);
	}
}
void zhongdigui(Node *pRoot)<span style="white-space:pre">	</span><span style="font-family: Arial, Helvetica, sans-serif;">//递归方法,中序遍历二叉树</span>
{
	if (pRoot == nullptr)
	{
		return;
	}
	if (pRoot->pLeft != nullptr)
	{
		zhongdigui(pRoot->pLeft);
	}
	cout << pRoot->data << " ";
	if (pRoot->pRight != nullptr)
	{
		zhongdigui(pRoot->pRight);
	}
}
void houdigui(Node *pRoot)<span style="white-space:pre">	</span>//递归方法,后序遍历二叉树
{
	if (pRoot == nullptr)
	{
		return;
	}
	if (pRoot->pLeft != nullptr)
	{
		houdigui(pRoot->pLeft);
	}
	if (pRoot->pRight != nullptr)
	{
		houdigui(pRoot->pRight);
	}
	cout << pRoot->data << " ";
}

下面开始非递归算法:

注意:和别人的博客不太一样的是,非递归算法,我这里有两套,一套是我自己开始写的,感觉还好,大家可以看看,也可以给我提提意见,还有一套是网上很流行的。

我们先从网上流行的开始:

void stackxianpopulance(Node *pRoot)<span style="white-space:pre">	</span>//非递归方法,先序遍历二叉树

{
	stack<Node *> mystack;
	Node *pnow = pRoot;
	while (pnow != nullptr || false == mystack.empty())
	{

		while (pnow)
		{
			cout << pnow->data << " ";
			mystack.push(pnow);
			pnow = pnow->pLeft;
		}
		pnow = mystack.top();
		mystack.pop();
		pnow = pnow->pRight;
	}
}
void stackzhongpopulance(Node *pRoot)<span style="white-space:pre">	</span>//非递归方法,中序遍历二叉树
{
	stack<Node *> mystack;
	Node *pnow = pRoot;
	while (pnow != nullptr || false == mystack.empty())
	{
		while (pnow)
		{
			mystack.push(pnow);
			pnow = pnow->pLeft;
		}
		pnow = mystack.top();
		mystack.pop();
		cout << pnow->data << " ";
		pnow = pnow->pRight;
	}
}

由于这个后序二叉树,在遍历完左子树后要找到右子树,所以网上很流行的版本今天很晚了,就偷了懒,没有实现,因为博主明天还要考试啊,实现完了估计又到夜里了,

所以就网上找一份给大家看看,为了临时的顶上来:

前序、中序、后序的非递归遍历中,要数后序最为麻烦,如果只在栈中保留指向结点的指针,那是不够的,必须有一些额外的信息存放在栈中。
方法有很多,这里只举一种,先定义栈结点的数据结构
typedef struct{Node * p; int rvisited;}SNode //Node 是二叉树的结点结构,rvisited==1代表p所指向的结点的右结点已被访问过。

lastOrderTraverse(BiTree bt){
  //首先,从根节点开始,往左下方走,一直走到头,将路径上的每一个结点入栈。
  p = bt;
  while(bt){
    push(bt, 0); //push到栈中两个信息,一是结点指针,一是其右结点是否被访问过
    bt = bt.lchild;
  }

  //然后进入循环体
  while(!Stack.empty()){ //只要栈非空
    sn = Stack.getTop(); // sn是栈顶结点

    //注意,任意一个结点N,只要他有左孩子,则在N入栈之后,N的左孩子必然也跟着入栈了(这个体现在算法的后半部分),所以当我们拿到栈顶元素的时候,可以确信这个元素要么没有左孩子,要么其左孩子已经被访问过,所以此时我们就不关心它的左孩子了,我们只关心其右孩子。

    //若其右孩子已经被访问过,或是该元素没有右孩子,则由后序遍历的定义,此时可以visit这个结点了。
    if(!sn.p.rchild || sn.rvisited){
      p = pop();
      visit(p);
    }
    else //若它的右孩子存在且rvisited为0,说明以前还没有动过它的右孩子,于是就去处理一下其右孩子。
    {
      //此时我们要从其右孩子结点开始一直往左下方走,直至走到尽头,将这条路径上的所有结点都入栈。

      //当然,入栈之前要先将该结点的rvisited设成1,因为其右孩子的入栈意味着它的右孩子必将先于它被访问(这很好理解,因为我们总是从栈顶取出元素来进行visit)。由此可知,下一次该元素再处于栈顶时,其右孩子必然已被visit过了,所以此处可以将rvisited设置为1。
      sn.rvisited = 1;

      //往左下方走到尽头,将路径上所有元素入栈
      p = sn.p.rchild;
      while(p != 0){
        push(p, 0);
        p = p.lchild;
      }
    }//这一轮循环已结束,刚刚入栈的那些结点我们不必管它了,下一轮循环会将这些结点照顾的很好。
  }
}

上面这位大哥的代码我们就这么ctrl+c,ctrl+v粘贴过来了,还请那位,如果看见了就算了啊,没准你也是这么来的呢,哈哈。。。

接下来呢,这一套是我自己写的,不知道合不合大家胃口,大家看一看吧,感觉这个改动起来要比上面的populance版本改动起来要好的多啊,比如前序改中序,后序改前序,等等。

废话少说,贴代码:

class StackNode<span style="white-space:pre">	</span>//这个是栈里面装的类型,
{
public:
	bool flag = false;//flag表示的意思也就是可不可以打印吧,这么理解就好了
	Node *p = nullptr;
	StackNode(bool flag, Node *p) :flag(flag), p(p)
	{

	}
};
void xianstackmy(Node *pRoot)<span style="white-space:pre">	</span>//非递归实现,先序遍历二叉树
{
	stack<StackNode> mystack;
	if (pRoot == nullptr)
	{
		return;
	}
	mystack.push(StackNode(false, pRoot));
	while (false == mystack.empty())
	{
		StackNode now = mystack.top();
		mystack.pop();
		if (now.p != nullptr)
		{
			if (now.flag)//如果可以打印,就打印,flag的意思
			{
				cout << now.p->data << " ";
			}
			else
			{
				mystack.push(StackNode(false, now.p->pRight));//先把右子树压打栈底
				mystack.push(StackNode(false, now.p->pLeft));//再把左子树压栈
				now.flag = true;//当前节点设置为可以打印
				mystack.push(now);//将当前可以打印的节点压栈
			}
		}
	}

	cout << endl;
}
void zhongstackmy(Node *pRoot)<span style="white-space:pre">	</span>//非递归实现,中序遍历二叉树
{
	stack<StackNode> mystack;
	if (pRoot == nullptr)
	{
		return;
	}
	mystack.push(StackNode(false, pRoot));
	while (false == mystack.empty())
	{
		StackNode now = mystack.top();
		mystack.pop();
		if (now.p == nullptr)
		{
			continue;
		}
		if (now.flag)//我们之前设置过可以打印的啊,这里可以打印的就直接打印了,不符合的继续执行else
		{
			cout << now.p->data << " ";
		}
		else
		{
			mystack.push(StackNode(false, now.p->pRight));//先将右子树压到栈底
			now.flag = true;//当前节点设置为可以打印
			mystack.push(now);
			mystack.push(StackNode(false, now.p->pLeft));//将左子树压到栈底
		}
	}

	cout << endl;
}
void houstackmy(Node *pRoot)<span style="white-space:pre">	</span>//非递归方式实现,后序遍历二叉树
{
	stack<StackNode> mystack;
	if (pRoot == nullptr)
	{
		return;
	}
	mystack.push(StackNode(false, pRoot));
	while (false == mystack.empty())
	{
		StackNode now = mystack.top();
		mystack.pop();
		if (now.p == nullptr)
		{
			continue;
		}
		if (now.flag)
		{
			cout << now.p->data << " ";
		}
		else
		{
			now.flag = true;//先设置当前节点为可以打印
			mystack.push(now);
			mystack.push(StackNode(false, now.p->pRight));//压栈右子树
			mystack.push(StackNode(false, now.p->pLeft));//压栈左子树
		}
	}

	cout << endl;
}

以上的就是我写的二叉树的遍历方式,有什么问题大家请多指教,

我写的应该算是,递归转栈的间接转换法吧,

模板如下:

间接转换法 :

    该方法使用栈保存中间结果,一般需根据递归函数在执行过程中栈的变化得到。其一般过程如下:

<1>将初始状态s0进栈

    <2>while (栈不为空)

         {

             退栈,将栈顶元素赋给s;

             if (s是要找的结果) {

返回;

}

             else {

                   寻找到s的相关状态s1;

                   将s1进栈;

                    }

         }

你看,我的模板大概也如此,递归的参数还有逻辑很重要,

递归转栈,如何用栈模拟递归,还有参数很重要,弄明白这些就好了。

这个问题,我过几天还会继续研究,有成果和大家一起分享,

明天还要考试啊,今天赶紧去刷牙了,然后睡觉,讲的不够仔细还望大家见谅

时间: 2024-08-05 07:09:35

2015-03-15---二叉树递归(非递归)实现先序、中序、后序遍历(附代码)的相关文章

二叉树的非递归遍历--京东2015笔试回忆

题目回忆: C/C++研发试卷:偏重于数据结构的考察,编程题有2题+1题附加题: 1.输入整数n,求m,m>9,m中各个数位的乘积=n的最小整数;如n=36,m=49; 2.二叉树前序遍历的非递归实现(本文的总结) 3.求第n个数,这个序列满足(2^i)*(3^j)*(5^k),前7个为:2,3,4,5,6,8,10 .... 小题有基本的数据结构.程序运行结果.SQL题目. 4.删除表格用DROP命令,死锁产生的条件: 4.1互斥使用(资源独占) 一个资源每次只能给一个进程使用 4.2.不可强

二叉树基础(创建方法,遍历方法(前序/中序/后序/层序、递归/非递归)

二叉树的创建及遍历是很多二叉树问题的基础,递归遍历逻辑清晰,代码简约漂亮,然则效率低下(所有递归方案的通病,非不得已不用递归): 非递归遍历高效,却不是能信手写出来的,特别是后续非递归遍历,相信很多资深码工也有这样的经历: 5年前学习了二叉树的非递归遍历,一个月前复习了并达到能熟练写出的程度,在不参考任何资料的情况下,今天却怎样也写不出来. 如果你也有过这种经历,恭喜你,这说明你是一个正常人…… 另一方面市面上有些国人写的教材,各种语法.逻辑错误层出不起,不知祸害了多少未来的码工,深感痛心. 印

二叉树的非递归遍历(先序、中序、后序和层序遍历)

[前文] 二叉树的非递归遍历有 先序遍历.中序遍历 .后续遍历 和 层序遍历. 非递归算法实现的基本思路:使用堆栈.而层序遍历的实现:使用队列. 如下图所示的二叉树: 前序遍历顺序为:ABCDE (先访问根节点,然后先序遍历其左子树,最后先序遍历其右子树) 中序遍历顺序为:CBDAE (先中序遍历其左子树,然后访问很节点,最后中序遍历其右子树) 后续遍历顺序为:CDBEA (先后序遍历其左子树,然后后续其右子树,最后访问根节点) 层序遍历顺序为:ABECD (由上至下.从左到右遍历二叉树) [准

[算法]二叉树的非递归遍历算法

1.二叉树的非递归中序遍历算法 二叉树的中序遍历方法是:左中右,因此一开始会顺着根节点的左孩子一直往下(这点和先序遍历一样,这也是二者前面部分代码很相似的原因),到最后一个左孩子时尝试把它的右孩子塞进栈内,然后顺着它的的左孩子而下,直到不能访问为止.利用的栈FILO的特性,对每个节点都进行顺左孩子而下即可. 上代码: 1 void inOrder(TreeNode* root,vector<int>& inOrder) 2 { 3 stack<TreeNode*>st; 4

二叉树总结—建树和4种遍历方式(递归&amp;&amp;非递归)

今天总结一下二叉树,要考离散了,求不挂!二叉树最重要的就是 建立.4种遍历方式,简单应用,如何判断两颗二叉树是否相似 二叉树分为 :1.完全二叉树  2.满二叉树 结构性质: 1).满二叉树 高度为h ,节点数则为 2^h - 1,且叶子节点全在最下层,且叶子节点数为2^(n-1)个{n代表二叉树层数,也叫深度} 2).n个节点的 完全二叉树 深度为 int(log2n)(以2为底n的对数)+ 1: 3).非空二叉树 叶子节点个数==双分支节点数+1 4).非空二叉树 某节点编号 n  若有左孩

二叉树的非递归实现

之前一直觉得二叉树使用递归来实现就感觉有点绕,今天才发现二叉树使用非递归来实现更加的绕,但是考虑到我们得使用非递归来提高二叉树的遍历效率,使用非递归是一种比较好的方法. 三种递归遍历对遍历的描述,思路非常简洁,最重要的是三种方法完全统一,大大减轻了我们理解的负担.现在非递归使用栈来实现,利用了栈的先进后出的特点,可以解决. 二叉树的递归实现之前已经实现过了,我们直接实现非递归.并且都使用栈实现 (一)前序遍历 对于前序遍历,我们要解决的问题是何时压入左右子树?先压左子树还是右子树? 答案是显而易

二叉树,递归非递归遍历算法(全)

包含了所有的非递归和递归的算法: #include<iostream> #include<queue> #include<stack> using namespace std; //二叉树结点的描述 typedef struct BiTNode { char data; struct BiTNode *lchild, *rchild; //左右孩子 }BiTNode,*BiTree; //按先序遍历创建二叉树 //BiTree *CreateBiTree() //返回结

通过二叉树的中序和后序遍历序列构造二叉树(非递归)

题目:通过二叉树的中序和后序遍历序列构造二叉树 同样,使用分治法来实现是完全可以的,可是在LeetCode中运行这种方法的代码,总是会报错: Memory Limit Exceeded ,所以这里还是用栈来实现二叉树的构建. 与用先序和后序遍历构造二叉树的方法类似,但还是要做一些改变: 如果从后往前处理中序和后序的序列,则处理就为如下所示的情况: Reverse_Post: 根-右子树-左子树 Reverse_In: 右子树-根-左子树 这样处理方式和先序-中序就差不多了,只是将添加左孩子的情况

Java实现二叉树的创建、递归/非递归遍历

近期复习数据结构中的二叉树的相关问题,在这里整理一下 这里包含: 1.二叉树的先序创建 2.二叉树的递归先序遍历 3.二叉树的非递归先序遍历 4.二叉树的递归中序遍历 5.二叉树的非递归中序遍历 6.二叉树的递归后序遍历 7.二叉树的非递归后序遍历 8.二叉树的层次遍历 这里感谢博客http://blog.csdn.net/skylinesky/article/details/6611442的指导 /**二叉树的结点定义*/ class Node<T>{ private T value; pr

【C语言】【数据结构】菜鸟学习日志(四) 用二叉树实现非递归排序

唉,由于要备战考研,这篇博文可能是我这一年最后一次更新啦! 其实断断续续的也没有写很多,而且大多都是很初级.很简单的东西,没有和大家分享什么高阶的东西.这也正应了我们<菜鸟学习日志>的标题嘛! 不过说回来我还是很喜欢写博文的.一方面总结学到的知识,以后也可以自己看看别做了就忘了:另一方面,写博文也让我在学习的过程中更加认真,以免分享了错误的知识. 写的东西好不好呢是一说,好像有一些点击量,不过看的人估计也不多.只是我还算乐在其中吧! 大学生活说到底过得有点浪了,导致我苦逼地走向了考研的不归路-