Java数据结构:二叉树的前序,中序,后序遍历(递归和非递归)

嘤嘤嘤,两个月没写博客了,由于有点忙,今天开始日更博客。

今天总结一下学习树的先根,中根,后根。每种两种方法,递归和非递归。

先根:

递归:

思路:先根遍历,即第一次遇到的结点就开始打印。先一直遍历左子树,直到未空,然后右子树,直到为空。递归下去。

过程:先将1进入方法,2,3执行方法,2先执行,在2中将4,5执行方法,直到发现4无子树,然后轮到5执行方法。直到发现5没有子树,此时2方法结束,轮到3执行方法。。。。。。懂我的意思吧。

private void preorder(BinaryNode<T> p) {        //根节点
		if (p != null) {
			System.out.print(p.data.toString() + " ");//打印结点数值
			preorder(p.left);        //打印当前结点的左子树
			preorder(p.right);       //打印当前结点的右子树
		}
	}

非递归:

思路:还是先根遍历,遇到结点就压入栈中,每当发现某个结点无子树,将结点设置为从栈中弹出元素。如果不为空就打印。

过程:先将1压入栈中,然后让结点=1.left,1的左子树=2,发现2不为空压入栈中,继续。。。当发现4无子树时,从栈中弹出2,使得p=2.right=5.此时栈中只有1,等发现5没有子树时,p=弹栈=1.。。。。。你懂我意思吧。

上代码:

public void preorderTraverse() {
		LinkedStack<BinaryNode<T>> stack = new LinkedStack<BinaryNode<T>>();
		BinaryNode<T> p = this.root;
		while (p != null || !stack.isEmpty()) {
			if (p != null) {
				System.out.print(p.data + " "); // 先根遍历
				stack.push(p);
				p = p.left;
			} else {
				System.out.print("^ ");
				p = stack.pop();
				p = p.right;

			}

		}
	}​​

中根:

递归:

思路:开始从跟结点遍历,当第二次遍历到该结点时打印。如何区分是第二次了,在递归中只需要在左右子树中间打印即可。因为但遍历完左子树时,回到父母结点时为第二次遍历到父母结点,第一次为通过父母接到遍历到孩子结点时。

过程:从1开始进入遍历方法,发现1不为空,将2,3进入遍历方法。由于2先开始,此时3一直在等待2的结束。2发现4.5不是空,将4,5进入遍历方法,此时没有打印2.当4结束时在通过2进入5之前打印此时结点,即为中根遍历。

上代码:

protected void inorder(BinaryNode<T> p) {     //p为根结点
		if (p != null) {
			inorder(p.left);
			System.out.print(p.data.toString() + " ");
			inorder(p.right);
		}
	}

非递归:

思路:参考先根遍历中的非递归方法。先根遍历是在第一次遇到非空的时候打印。中根稍有区别,在第一次非空时入栈,不打印,当发现子树为空时,弹栈时回到空子树的父母结点时,打印,此时为第二次访问,即中根遍历。

过程:1开始进入while循环,发现1.left=2不为空入栈,此时不要打印,然后4,进入循环,入栈,发现4左子树为空,此时弹栈,弹出4,立即打印,达到中根遍历。其余相似。

上代码:

public void inorderTraverse() {
		LinkedStack<BinaryNode<T>> stack = new LinkedStack<BinaryNode<T>>();
		BinaryNode<T> p = this.root;
		BinaryNode<T> q1 = null;
		BinaryNode<T> q2;
		boolean flag = true;
		while (p != null || !stack.isEmpty()) {
			if (p != null) {
				stack.push(p);
				p = p.left;
			} else {
				System.out.print("^ ");
				p = stack.pop();
				System.out.print(p.data + " "); // 中跟遍历
				p = p.right;

			}

		}
	}

后跟:这是又臭又硬的一根

递归:

思路:现遍历子树,等左右子树都遍历完后,在打印结点。跟之前的类比一下很容易想到。

过程:1进入递归,发现2,3非空,然后2进入递归,3等待,2又发现4,5非空,此时4进入递归,5等待,然后4发现左右子树都为空,此时不再进行递归,打印4结点的值。懂我意思吧。

上代码:

public void inorderTraverse() {
		LinkedStack<BinaryNode<T>> stack = new LinkedStack<BinaryNode<T>>();
		BinaryNode<T> p = this.root;
		BinaryNode<T> q1 = null;
		BinaryNode<T> q2;
		boolean flag = true;
		while (p != null || !stack.isEmpty()) {
			if (p != null) {
				stack.push(p);
				p = p.left;
			} else {
				System.out.print("^ ");
				p = stack.pop();
				System.out.print(p.data + " "); // 中跟遍历
				p = p.right;

			}

		}
	}

非递归:

思路:由于需要确定当前结点被访问过两次,此时需要额外一个结点来储存之前访问的结点。符合打印条件的就一共两种情况,一是无子结点,直接打印。二是当前访问的结点其右子树已被访问,或者右子树不存子,左子树被访问过。

过程:懒得写了,一定要注意,先将右子树入栈,再将左子树入栈,因为访问时是现访问左子树,在访问右子树,栈为后进现出结构!!!!!

上代码:

public void postorderTraverse() {
		LinkedStack<BinaryNode> stack = new LinkedStack<BinaryNode>();
		BinaryNode cur, pre = null;       //pre为额外结点,记录前一个结点,用于打印
		stack.push(this.root);
		while (!stack.isEmpty()) {
			cur = stack.peek();
			if ((cur.left == null && cur.right == null)
					|| (pre != null && (cur.left == pre || cur.right == pre))) {            //判断是否为上述条件决定是否打印
				BinaryNode temp = stack.pop();
				System.out.print(temp.data + " ");
				pre = temp;
			} else {
				if (cur.right != null) {        //右结点入栈
					stack.push(cur.right);
				}

				if (cur.left != null) {
					stack.push(cur.left);
				}
			}
		}
	}

猜你喜欢

转载自blog.csdn.net/qq_42192693/article/details/84644193