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

3 篇文章 0 订阅
3 篇文章 0 订阅

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


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


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

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

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

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




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

先序遍历: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进栈; 
                    } 
         } 




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

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


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


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值