【算法与数据结构复习】| 二叉树及其三序遍历的实现

        今天跟着左程云算法课0017和0018。复习了二叉树前序遍历(中左右)、中序遍历(左中右)、后序遍历的实现(左右中),分别用递归迭代实现。

        通用的树结点类代码:

	public static class TreeNode{
		public int val;
		public TreeNode left;
		public TreeNode right;
		public TreeNode(int n) {
			val=n;
			left=null;
			right=null;
		}
		
	}

一、递归实现三序遍历

        用递归迭代实现三序遍历的代码非常简单。我觉得核心的点在与跟左神过了一遍递归的流程,知道在具体的实现中递归的计算过程是怎样的,在子树操作结束后他还会再次回到父节点,在遍历过程中,一个非空结点需要经历三次。这是比较有意义的,比汉诺塔更适合入门者去理解递归。代码实现非常简单,代码如下:

(1)递归实现前序遍历
	//先序遍历
	public static void preOrder(TreeNode head) {
		if(head==null) {
			return;
		}
		System.out.print(head.val+" ");
		preOrder(head.left);
		preOrder(head.right);
	}
(2)递归实现中序遍历
	//中序遍历
	public static void inOrder(TreeNode head) {
		if(head==null) {
			return;
		}
		inOrder(head.left);
		System.out.print(head.val+" ");
		inOrder(head.right);
	}
(3)递归实现后序遍历
	//后序遍历
	public static void postOrder(TreeNode head) {
		if(head==null) {
			return;
		}
		postOrder(head.left);
		postOrder(head.right);
		System.out.print(head.val+" ");
	}

二、迭代实现三序遍历

        迭代法实现三序遍历基本上都用到栈结构。

(1)迭代实现前序遍历

        前序遍历的顺序是中左右,先打印自己,再顾及左树和右树。那么栈是一个先进后出的结构,因此将一个节点弹出后,要先push它的right节点,再push它的left节点,这样出栈的时候就是先左再右了。想清楚之后代码实现也是比较简单的(实在想不清楚就画个简单的树过一遍代码的流程),代码如下:

	//迭代实现树的先序遍历
	public static void preOrder(TreeNode head) {
		if(head!=null) {
			Stack<TreeNode> stack=new Stack<TreeNode>();
			stack.push(head);
			while(!stack.isEmpty()) {
				head=stack.pop();
				System.out.print(head.val+" ");
				//先把右树压进去
				if(head.right!=null) {
					stack.push(head.right);
				}
				//再把左树压进去
				if(head.left!=null) {
					stack.push(head.left);
				}
			}
			System.out.println();
		}
	}
(2)迭代实现中序遍历

        中序遍历的顺序是左中右,核心思想是先处理左树,再处理自己,最后再处理右树。具体步骤为:1)子树左边界进栈(左边界要进完);2)栈顶节点弹出,head指向当前弹出结点,打印,head指向head.right,重复步骤1;3)head为空且栈空时,结束。

        用迭代实现中序遍历比较不容易想到,过一遍流程就能想通了,依然是画一个简单的树然后照着代码过一遍流程,代码如下:

	//迭代实现树的中序遍历
	public static void inOrder(TreeNode head) {
		if(head!=null) {
			Stack<TreeNode> stack=new Stack<TreeNode>();
			while(!stack.isEmpty()||head!=null) {
				if(head!=null) {
					stack.push(head);
					head=head.left;
				}
				else {
					head=stack.pop();
					System.out.print(head.val+" ");
					head=head.right;
				}
			}
		}
		System.out.println();
	}
(3)迭代实现后序遍历

        我们在用迭代实现前序遍历时,专门先压right再压left为了实现中左右的顺序,那么如果先压left再压rigth,就实现了中右左的顺序;我们后序遍历顺序是左右中,是变形后的前序完全倒过来。因此设置了两个栈,在前序遍历打印的时候替换成入栈2,整个流程结束后将栈2的元素以此出栈。

        核心在于由前序遍历的中左右->中右左->左右中;代码如下:

	//迭代实现树的后序遍历(使用两个栈)
	public static void postOrder(TreeNode head) {
		if(head!=null) {
			Stack<TreeNode> stack =new Stack<>();
			Stack<TreeNode> collect=new Stack<>();
			stack.push(head);
			while(!stack.isEmpty()) {
				head=stack.pop();
				collect.push(head);
				if(head.left!=null) {
					stack.push(head.left);
				}
				if(head.right!=null) {
					stack.push(head.right);
				}
			}
			while(!collect.isEmpty()) {
				System.out.print(collect.pop()+" ");
			}
			
			System.out.println();
			
		}

	}

三、时空复杂度分析

        使用递归和迭代实现三序遍历的时间复杂度都是O(1),递归实现中每个结点会回到3次,迭代实现中每个结点进栈出栈的次数也是有限的,也就是说每个结点折腾的次数是有限的,所以递归和迭代实现三序遍历的时间复杂度都是O(1)。空间复杂度都是O(h),h是二叉树的高度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值