LeetCode每日一题——二叉树的中序遍历

一、题目描述

LeetCode每日一题——二叉树的中序遍历_第1张图片

二、思路阐述

二叉树的遍历方式有四种,分别为:先序遍历、中序遍历、后序遍历、层次遍历。前三种为一类,“序”是指父结点的位置,先序即父->左->右,中序即左->父->右,后序即左->右->父;层次遍历单独为一类,指根据结点所在深度由浅及深、由左及右的顺序进行遍历。前三种遍历方式由于需要回溯,故需要依靠递归方式或者数据结构——栈来实现;层次遍历需要使用队列来实现。
中序遍历的具体处理思路:对于每一个结点来说,遍历顺序均为左->父->右,但是要想遍历到左结点和右节点需要依赖父结点,所以为了保证父结点遍历顺序不丢失以及右节点可遍历到,需要在沿左子树遍历的时候将中间结点入栈。当左结点数据输出后,再依次遍历父结点(栈顶结点)和右结点(栈顶结点右子树)即可。总的来说,因为最终遍历顺序为左->父->右,而栈的特性是先进后出,所以栈内顺序就为右->父->左。

三、代码讨论

在leetcode上面我总共提交了两次,第一次是我自己的初始想法,我认为代码逻辑没有问题,但是超时了,于是我便研究了一下如何进行优化。代码如下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        Stack<TreeNode> stack = new Stack<TreeNode>();
        List<Integer> result = new ArrayList<Integer>();
        stack.push(root);
        while (!stack.empty()) {
			while (stack.lastElement().left != null) {
				stack.push(stack.lastElement().left);
			}
			TreeNode temp = stack.pop();
			result.add(temp.val);
			if (temp.right != null) {
				stack.push(temp.right);
			}
		}
        return result;
}
  1. 一次改进:尽可能去掉函数调用。主要针对的是嵌套中的while,里面的while主要目的就是通过一个迭代将左结点通通压入栈中,可以设计一个临时变量去掉函数调用,代码如下:
public List<Integer> inorderTraversal(TreeNode root) {
	Stack<TreeNode> stack = new Stack<TreeNode>();
    List<Integer> result = new ArrayList<Integer>();
    stack.push(root);
    while (!stack.empty()) {
        TreeNode curNode = stack.lastElement();
		while (curNode.left != null) {
			stack.push(curNode.left);
			curNode = curNode.left;
		}
		TreeNode temp = stack.pop();
		result.add(temp.val);
		if (temp.right != null) {
			stack.push(temp.right);
		}
	}
       return result;
}
  1. 二次改进:进一步优化。通过上一步处理可以发现我们要想完全舍弃lastElement()方法,还需要将curNode的定义再向外提,令TreeNode curNode = root;并将代码作相应改变。代码如下:
 class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        Stack<TreeNode> stack = new Stack<TreeNode>();
        List<Integer> result = new ArrayList<Integer>();
       	TreeNode curNode = root;
        while (root != null || !stack.empty()) {
			while (curNode != null) {
				stack.push(curNode);
				curNode = curNode.left;
			}
			TreeNode temp = stack.pop();
			result.add(temp.val);
			if (temp.right != null) {
				stack.push(temp.right);
			}
		}
        return result;
    }
}
  1. 三次改进:去掉不必要的变量。经过上面两步我们对上半部分的优化完成了,接下来是对下半部分的优化。可以发现变量TreeNode temp其实可以用TreeNode curNode替代。并且最后的if判断可以不必要的,最终改进如下:
 public List<Integer> inorderTraversal(TreeNode root) {
 	Stack<TreeNode> stack = new Stack<TreeNode>();
    List<Integer> result = new ArrayList<Integer>();
    TreeNode curNode = root;
    while (curNode != null || !stack.empty()) {
		while (curNode != null) {
			stack.push(curNode);
			curNode = curNode.left;
		}
		curNode = stack.pop();
		result.add(curNode.val);
		curNode = curNode.right;
	}
    return result;
}

此版本最终通过了,这也就是我二次提交的结果。对比改动前后的代码我们可以发现两个代码所表示的思想是发生了微妙的变化的。前者每一次循环处理一个左子树序列,即针对栈顶结点,首先将其左子树序列全部入栈,然后对栈顶结点进行输出,之后将栈顶结点的右子树压入栈中当作下一次处理的开始;后者每一次循环处理进行一次输出,每一次输出的对象是未输出树的最左结点,所以首先要最左,然后将其输出最后再将curNode指向右结点作为下一轮循环的开始。curNode经历的路径就可看作是我们寻找最左子节点的过程。

四、知识扩展——java类Stack简介

  1. Stack类继承于Vector类,是java语言中栈的官方实现,其与父类均是线程安全的。
  2. 主要方法
方法名 返回类型 描述
empty boolean 判断stack是否为空
peek E 返回栈顶元素并不改变栈
pop E 弹出栈顶元素
push E 将元素压入栈
search int 返回最靠近顶端的目标元素到顶端的距离
  1. isEmpty()和empty()没有区别,前者是继承Vector类的方法,后者是Stack类自己的方法。
  2. lastElement()指栈顶元素,firstElement()指栈底元素,两个方法均继承自Vector类,而Vector类类似于数组。

你可能感兴趣的:(leetcode,java,二叉树,leetcode)