二叉树的遍历(递归,非递归实现)

二叉树的遍历(递归,非递归实现)

二叉树的遍历是一些动态查找的基础,也是数据结构中的重要知识点。最近有时间重新回顾一个这几种常见的遍历方式。若有不对之处,请批评指正。

0.什么是二叉树

二叉树其实不难理解,有这官方的定义。要是你对普通的树型结构有所了解的话,二叉树其实是一颗特殊的树,它有着自己的特别。

  • 每个结点最多有两颗子树,所以二叉树中不存在度大于2的结点。
  • 左右子树都是有顺序的,且次序不能颠倒。

1.二叉树的遍历(递归)

最基本的二叉树的遍历,有前序,中序,后序遍历。而递归的实现方式也是大家最常见的一种实现方式。面对递归对于一些初学者来说还是有一些困难的。但是只要你宏观的逻辑是上走同即可。

下面看看前序遍历。所谓的前序遍历则是,根,左节点,右节点。

mark

像上图上的二叉树,前序遍历的结果是 A B D E C

这样写一个递归的遍历则就是,先访问根节点,再访问左节点,再试右节点

public void perOrder(BtNode p){
	//判断二叉树是否空	
    if(p!=null){
			visit(p);
			perOrder(p->lchild);
			perOrder(p->rchild);
		}
	}

剩下的中序和后序遍历则就比较好理解了。

下面直接附代码:

//中序遍历
public void inOrder(BtNode p){
	//判断二叉树是否空	
    if(p!=null){	
		inOrder(p->lchild);
		visit(p);	
        inOrder(p->rchild);
		}
	}
//后序遍历
public void postOrder(BtNode p){
	//判断二叉树是否空	
    if(p!=null){	
		postOrder(p->lchild);
        postOrder(p->rchild);
        visit(p);	
		}
	}

2.二叉树的遍历(非递归)

非递归的遍历呢,则就是自己实现系统栈所做的工作。上面讲的递归遍历其实还是有问题的,要是二叉树比较深,在递归的时候就容易出现栈的溢出。非递归的算法相比于递归的算法来讲,效率上也是好一些的。

先说一个前序遍历吧。

再来回顾一个啥是前序遍历呀,就是根,左节点,右节点。现在做的是非递归,显然,我们需要一个其他的数据结构那就是栈。该如何利用栈呢。

是这么操作的。先将根节点入栈。当栈不空的时候,栈顶节点出栈,若存在右节点,让右节点入栈。若左节点存在,让左节点入栈。往复循环这个过程。这样就可以。代码如下:

	public void preOrderNorRecun(BtNode p){
		BTNode* stack[maxSize] ;
		int top  = -1;
		BtNode q;
		if(P!=null){
			stack[++top] = p;
			while(top!=-1){
				q = stack[top--];
				if(p->rchild != null){
					stack[++top] = p->rchild;
				}
				if(p->lchild != null){
					stack[++top] = p->lchild;
				}
			}
		}

	}

在就是中序遍历。其实操作上都是通过栈来进行的,只是在步骤上有所不同罢了。

步骤是这样子的。先将根节点入栈,然后若左节点一直不为空的话,就一直入栈。当没有左节点就让这个节点指向右节点。继续进行上面的步骤。

	public void inOrderNorRecu(BtNode p){
        
		BtNode* stack[maxSize];
		int top = -1;

		while(top!= -1 || p != null){

			while(p->lchild != null){
				stack[++top] = p;
				p = p->lchild;
			}
			if(top != -1){
				q = stack[top--];
				visit(q);
				p = p->rchild;

			}
		}
	}

最后就是后序遍历的非递归了,其实后序遍历的非递归还是比较复杂的,这里采用一种双栈的方式简化一下这个算法。

要是你比较细心就会发现这么一个规律。后序遍历的倒序和把左右子树颠倒的前序遍历是一致的

mark

上面的二叉树的后序遍历是 D E B C A。倒序为 A C B E D。

把上面的二叉树左右子树颠倒,进行前序遍历为,A C B E D。这样就和后序遍历的倒序是一致的。这样我们就是借鉴前序非递归的代码,进行编写了。

思路是这样子的,定义两个栈。一个栈做遍历顺序,一个栈放出第一个栈的结点。这个和前序非递归的非常相似的。先将根节点入栈。当栈不为空的时候,将栈顶结点出栈,并入第二个栈。然后若左节点不是为空,入栈。右节点不为空,入栈。最后将第二个栈的结点依次出栈则为 后序遍历的结果。代码如下:


	public void postOrderNorRecu(BtNode p){
		BtNode* stack1[maxSize];int top1 = -1;
		BtNode* stack2[maxSize];int top2 = -1;

		BtNode *q;
		if(p!=null){
			stack1[++top1] = p;
			while(top1 != -1){
				q = stack1[top1--];
				stack2[++top2] = q;
				if(p->lchild != null){ 
					stack1[++top1] = p->lchild;
				}
				if(p->rchild != null){
					stack1[++top1] = p->rchild;
				}
			}

			while(top2 != -1){
				q = stack2[top--];
				visit(q);
			}

		}
	}

if(p->rchild != null){
					stack1[++top1] = p->rchild;
				}
			}

			while(top2 != -1){
				q = stack2[top--];
				visit(q);
			}

		}
	}

posted @ 2019-08-14 15:19  ghsticker  阅读(62)  评论(0)    收藏  举报