二叉树三种遍历递归及非递归实现(Java)

import java.util.Stack;

//二叉树三种遍历递归及非递归实现(Java)
public class Traverse {

	/******************定义二叉树**************************/
	private final int MAX_SIZE = 10;
	//链式存储
	public static class BinaryTreeNode
	{
		int mValue;
		BinaryTreeNode mLeft;
		BinaryTreeNode mRight;

		public BinaryTreeNode(int mValue) {
			this.mValue = mValue;
		}
	}

	//顺序存储
	class BinaryTreeNode2
	{
		int[] data = new int[MAX_SIZE];
		int length;
	}

	//用以实现后续遍历的辅助结构
	private class HelpNode
	{
		BinaryTreeNode treeNode;
		boolean isFirst;
	}

	/******************递归实现***************************/
	//先序遍历
	public int PreOrderTreeWalk(BinaryTreeNode pNode)
	{
		if(pNode == null)
			return 0;
		visitNode(pNode);
		PreOrderTreeWalk(pNode.mLeft);
		PreOrderTreeWalk(pNode.mRight);
		return 1;
	}

	//中序遍历
	public int InOrderTreeWalk(BinaryTreeNode pNode)
	{
		if(pNode == null)
			return 0;
		InOrderTreeWalk(pNode.mLeft);
		visitNode(pNode);
		InOrderTreeWalk(pNode.mRight);
		return 1;
	}

	//后序遍历
	public int PostOrderTreeWalk(BinaryTreeNode pNode)
	{
		if(pNode == null)
			return 0;
		PostOrderTreeWalk(pNode.mLeft);
		PostOrderTreeWalk(pNode.mRight);
		visitNode(pNode);
		return 1;
	}

	/*****************非递归实现***********************/
	//先序遍历
	public int PreOrderTraverse(BinaryTreeNode pNode)
	{
		Stack<BinaryTreeNode> stack  = new Stack<>();
		if(pNode == null)
			return 0;

		while(!stack.isEmpty()||pNode != null)
		{
			while(pNode != null)
			{
				//先访问
				visitNode(pNode);
				stack.push(pNode);
				//遍历左节点
				pNode = pNode.mLeft;
			}
			//返回顶层元素
			pNode = stack.peek();
			stack.pop();
			//遍历右节点
			pNode = pNode.mRight;
		}
		return 1;
	}

	//先序遍历实现方法二
	public int PreOrderTraverse2(BinaryTreeNode pNode)
	{
		if(pNode == null)
			return 0;
		Stack<BinaryTreeNode> stack = new Stack<>();
		stack.push(pNode);

		while(!stack.isEmpty())
		{
			pNode = stack.pop();
			visitNode(pNode);

			if(pNode.mRight != null)
				stack.push(pNode.mRight);
			if(pNode.mLeft != null)
				stack.push(pNode.mLeft);
		}
		return 1;
	}

	//中序遍历
	public int InOrderTraverse(BinaryTreeNode pNode)
	{
		Stack<BinaryTreeNode> stack = new Stack<>();
		if(pNode == null)
			return 0;

		while(!stack.isEmpty()||pNode != null)
		{
			while(pNode!=null)
			{
				stack.push(pNode);
				pNode = pNode.mLeft;
			}
			pNode = stack.pop();
			visitNode(pNode);
			pNode = pNode.mRight;
		}
		return 1;
	}

	//后序遍历,用一个标记标记右子树是否访问过
	/*
	 *    第一种思路:对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,
	 *    此时该结点出现在栈顶,但是此时不能将其出栈并访问,因此其右孩子还为被访问。所以接下来按照相同
	 *    的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在栈顶,此时可以将其出栈并访
	 *    问。这样就保证了正确的访问顺序。可以看出,在这个过程中,每个结点都两次出现在栈顶,只有在第二
	 *    次出现在栈顶时,才能访问它。因此需要多设置一个变量标识该结点是否是第一次出现在栈顶。
	 * */
	public int PostOrderTraverse(BinaryTreeNode pNode)
	{
		if(pNode == null)
			return 0;
		Stack<HelpNode> stack = new Stack<>();
		HelpNode helpNode;
		while(!stack.isEmpty() || pNode != null)
		{
			//一直循环至最左节点
			while(pNode != null)
			{
				HelpNode temp = new HelpNode();
				temp.treeNode = pNode;
				temp.isFirst = true;
				stack.push(temp);
				pNode = pNode.mLeft;
			}

			if(!stack.isEmpty())
			{
				helpNode = stack.pop();

				if(helpNode.isFirst)//表示第一次,即每一个要被访问的根节点要被push两次
				{
					helpNode.isFirst = false;
					stack.push(helpNode);
					pNode = helpNode.treeNode.mRight;//右节点的是否有效则移至循环的开始出进行判断
				}
				else
				{
					visitNode(helpNode.treeNode);
					pNode = null;
				}
			}
		}
		return 1;
	}

	//后序遍历实现方法二:双栈法
	public int PostOrderTraverse2(BinaryTreeNode pNode)
	{
		if(pNode == null)
			return 0;
		Stack<BinaryTreeNode> stack1 = new Stack<>();
		Stack<BinaryTreeNode> stack2 = new Stack<>();//辅助栈
		//存入根节点,初始化
		stack1.push(pNode);
		//stack1弹出的元素,压入stack2,在将该元素的左右节点压入stack1
		while(!stack1.isEmpty())
		{
			pNode = stack1.pop();
			stack2.push(pNode);
			if(pNode.mLeft != null)
			{
				stack1.push(pNode.mLeft);
			}
			if(pNode.mRight != null)
			{
				stack1.push(pNode.mRight);
			}
		}

		//stack弹出的即是后序遍历的顺序
		while(!stack2.isEmpty())
		{
			visitNode(stack2.pop());
		}
		return 1;
	}

	//后序遍历实现方法三
	/*
	 * 第二种思路:要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。
	 * 如果P不存在左孩子和右孩子,则可以直接访问它;或者P存在左孩子或者右孩子,但是其左孩子和右
	 * 孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次
	 * 入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点
	 * 前面被访问。
	 * */
	//后序遍历实现方法二:双栈法
	public int PostOrderTraverse3(BinaryTreeNode pNode)
	{
		if(pNode == null)
			return 0;

		BinaryTreeNode preVisitedNode = null;
		Stack<BinaryTreeNode> stack = new Stack<>();
		stack.push(pNode);

		while(!stack.isEmpty())
		{
			pNode = stack.peek();
			if((pNode.mLeft == null && pNode.mLeft == null)//左右子树均为空的情况,即叶子节点
				||(preVisitedNode != null &&
				(preVisitedNode == pNode.mLeft || preVisitedNode == pNode.mRight)))//左右子树已经被访问的情况
			{
				visitNode(pNode);
				preVisitedNode = stack.pop();
			}
			else
			{
				if(pNode.mRight != null)
					stack.push(pNode.mRight);//注意push的顺序,先访问右子树
				if(pNode.mLeft != null)
					stack.push(pNode.mLeft);
			}
		}

		return 1;
	}

	/*********辅助函数**********/
	//访问节点数据
	private void visitNode(BinaryTreeNode treeNode)
	{
		System.out.print(treeNode.mValue);
		System.out.print("、");
	}

	public static void main(String[] args)
	{
		/************************构造测试二叉树链表组***********************/
		int[] data = {1,2,3,4,5,6,7,8,9,10};
		BinaryTreeNode[] treeNodes = new BinaryTreeNode[data.length];

		for(int i = 0; i < data.length; i++)
		{
			treeNodes[i] = new BinaryTreeNode(data[i]);
		}

		for(int i = 0; i < (data.length/2); i ++)
		{
			treeNodes[i].mLeft = treeNodes[(i + 1)*2 - 1];

			if(((i + 1) *2) < data.length)
			{
				treeNodes[i].mRight = treeNodes[(i + 1)*2];
			}
		}
		Traverse traverse = new Traverse();
		BinaryTreeNode root = treeNodes[0];
		System.out.print("先序遍历递归:    ");
		traverse.PreOrderTreeWalk(root);
		System.out.println();

		System.out.print("先序遍历非递归一:");
		traverse.PreOrderTraverse(root);
		System.out.println();

		System.out.print("先序遍历非递归二:");
		traverse.PreOrderTraverse2(root);
		System.out.println();

		System.out.print("中序遍历递归:  ");
		traverse.InOrderTreeWalk(root);
		System.out.println();

		System.out.print("中序遍历递归:  ");
		traverse.InOrderTraverse(root);
		System.out.println();

		System.out.print("后序遍历递归:  ");
		traverse.PostOrderTreeWalk(root);
		System.out.println();

		System.out.print("后序遍历非递归一:");
		traverse.PostOrderTraverse(root);
		System.out.println();

		System.out.print("后序遍历非递归二:");
		traverse.PostOrderTraverse2(root);
		System.out.println();

		System.out.print("后序遍历非递归三:");
		traverse.PostOrderTraverse3(root);
		System.out.println();
		/******************验证输出***********************/
//		for(BinaryTreeNode treeNode : treeNodes)
//		{
//			System.out.print("根节点");
//			System.out.println(treeNode.mValue);
//			System.out.print("左节点");
//			if(treeNode.mLeft != null)
//			{
//				System.out.println(treeNode.mLeft.mValue);
//			}
//			else
//			{
//				System.out.println("null");
//			}
//
//			System.out.print("右节点");
//			if(treeNode.mRight != null)
//			{
//				System.out.println(treeNode.mRight.mValue);
//			}
//			else
//			{
//				System.out.println("null");
//			}
//			System.out.println();
//		}
	}

}

时间: 2024-10-23 10:22:42

二叉树三种遍历递归及非递归实现(Java)的相关文章

二叉树几种遍历算法的非递归实现

二叉树遍历的非递归实现 相对于递归遍历二叉树,非递归遍历显得复杂了许多,但换来的好处是算法的时间效率有了提高.下面对于我学习非递归遍历二叉树算法的过程进行总结 为了便于理解,这里以下图的二叉树为例,分析二叉树的三种遍历方式的实现过程. 一.非递归实现二叉树的前序遍历 不借助递归,要实现二叉树的前序遍历,我们需要用到前面学过的栈这种数据结构.根据前序遍历的定义,先访问根节点,再访问左子树,最后访问右子树.声明指向节点的指针pCur,我们可以先访问根节点,之后让根节点进栈,并让pCur在左子树上移动

二叉树三种遍历(递归以及非递归实现)

package com.shiyeqiang.tree; import java.util.Stack; public class BiTree { public static void main(String[] args) { // 首先构造叶子节点 BiTree leafA1 = new BiTree(4); BiTree leafA2 = new BiTree(5); BiTree leafB1 = new BiTree(6); BiTree leafB2 = new BiTree(7)

二叉树三种遍历非递归算法

http://blog.csdn.net/pipisorry/article/details/37353037 c实现: 1.先序遍历非递归算法 #define maxsize 100 typedef struct { Bitree Elem[maxsize]; int top; } SqStack; void PreOrderUnrec(Bitree t) { SqStack s; StackInit(s); p=t; while (p!=null || !StackEmpty(s)) { w

数据结构 《22》---- 二叉树三种遍历的迭代器算法

二叉树的三种遍历有递归版本,和迭代版本.本文介绍一种新的思路. 参考了 http://coolshell.cn/articles/9886.html 在许多应用中,我们还需要对遍历本身进行抽象.假如有一个求和的函数sum,我们希望它能应用于链表,数组,二叉树等等不同的数据结构.这时,我们可以抽象出迭代器(Iterator)的概念,通过迭代器把算法和数据结构解耦了,使得通用算法能应用于不同类型的数据结构. 以下给出了三种遍历的迭代器算法. class Iterator { public: virt

二叉树三种遍历算法的递归和非递归实现(C++)

struct BinaryTreeNode { int m_nValue; BinaryTreeNode* m_pLeft; BinaryTreeNode* m_pRight; }; //递归前序遍历 void PreOrder(BinaryTreeNode* pNode) { if(pNode!=NULL) { cout<<pNode->m_nValue<<endl; PreOrder(pNode->m_pLeft); PreOrder(pNode->m_pRi

对二叉树三种遍历的理解

二叉树普通的遍历分为三种,分别是前序遍历(先序遍历).中序遍历.后序遍历. 这是从别处拷来的一张图,以此图为例说明: 前序遍历的顺序是:根节点.左节点.右节点. 从第一个根节点A开始为ABE,接下来是B开始,由于B没有左节点,所以遍历为BC:然后是E作为开始遍历为EF,C作为开始遍历为CD,F作为开始遍历为FG,G作为开始遍历为GHK. 将上面的一次关联起来,整个前序遍历即为ABCDEFGHK. 中序遍历的顺序是:左节点.根节点.右节点. 从第一个根节点A作为参照遍历为BAE,B作为参照遍历为B

关于二叉树三种遍历的相互推导

嗯..跟着陈越姥姥上数据结构,期末考试遇到一道从后序遍历和中序遍历推前序遍历的题,然后硬是不会做,今天突然有了思路,遂记下来: 原题是这样的:一颗二叉树的后序遍历序列是FDEBGCA,中序遍历序列是FDBEACG,那么前序遍历序列是? 思路如下: 根据后序遍历的性质,最后访问的元素一定是根节点,可知该二叉树根节点为A: 根据中序遍历的性质,出现在A前面的一定是A的左子树里面的节点,出现在A后面的一定是A右子树里面的节点.那么我们现在可以确定FDBE一定是A的左子树,CG一定是A的右子树: 根据树

HDU 1710 二叉树三种遍历

Binary Tree Traversals Problem Description A binary tree is a finite set of vertices that is either empty or consists of a root r and two disjoint binary trees called the left and right subtrees. There are three most important ways in which the verti

二叉树的前序、中序、后序遍历(递归、非递归)实现

本文部分来源于CSDN兰亭风雨大牛的原创.链接为http://blog.csdn.net/ns_code/article/details/12977901 二叉树是一种非常重要的数据结构,很多其他数据机构都是基于二叉树的基础演变过来的.二叉树有前.中.后三种遍历方式,因为树的本身就是用递归定义的,因此采用递归的方法实现三种遍历,不仅代码简洁且容易理解,但其开销也比较大,而若采用非递归方法实现三种遍历,则要用栈来模拟实现(递归也是用栈实现的).下面先简要介绍三种遍历方式的递归实现,再详细介绍三种遍