递归解法:常规
public List<Integer> preorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();preOrder(root, list);return list;}private void preOrder(TreeNode root, List<Integer> list) {if (root == null) {return;}list.add(root.val);preOrder(root.left, list);preOrder(root.right, list);}public List<Integer> inorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();inOrder(root, list);return list;}private void inOrder(TreeNode root, List<Integer> list) {if (root == null) {return;}inOrder(root.left, list);list.add(root.val);inOrder(root.right, list);}public List<Integer> postTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();postOrder(root, list);return list;}private void postOrder(TreeNode root, List<Integer> list) {if (root == null) {return;}postOrder(root.left, list);postOrder(root.right, list);list.add(root.val);}
学习迭代解法:
前序遍历迭代
class Solution {public List<Integer> preorderTraversal(TreeNode root) {// 首先,要明确。前序遍历的迭代写法是用栈存放,然后按照中左右的顺序来弹出// 迭代过程中要始终保持这个顺序(循环迭代中自动帮忙保持)// Start!// 首先构造一个栈,用于遍历存放Stack<TreeNode> stack = new Stack<>();List<Integer> list = new ArrayList<>();if (root == null) {return list;}// 首先将根节点放入stack.push(root);// 循环迭代,因为迭代中是不停的弹出,然后入栈操作,只要不为空,就不结束while (!stack.isEmpty()) {// 每次迭代先取出栈顶元素TreeNode curNode = stack.pop();// 2个操作,第一,将这个值输出;第二,将它的右左子树入栈(如果有)list.add(curNode.val);if (curNode.right != null) {stack.push(curNode.right);}if (curNode.left != null) {stack.push(curNode.left);}}return list;}}
中序遍历迭代
class Solution {public List<Integer> inorderTraversal(TreeNode root) {// 首先,还是通过栈来遍历迭代。// 一个特点是:对任何当前节点,都要一直先去找它的左节点。并不断加入栈(压在下面)// 直到左节点为空,才退出这个小迭代List<Integer> list = new ArrayList();if (root == null) {return list;}Stack<TreeNode> stack = new Stack();// 首先定义当前节点,并加入栈TreeNode curNode = root;// 进行大的迭代:1. 栈不为空 2. cur结点仍有指向// 因为用例证明[1,null,2,3]证明:1先入栈,然后弹出,没有新节点入栈。// 但是curNode指向了root的右节点,需要下一轮迭代。// 仅仅满足栈不为空不符合实际。while (!stack.isEmpty() || curNode != null) {// 一直找左节点!并加入while (curNode != null) {stack.push(curNode);curNode = curNode.left;}// 结束了就先POP一个出来,这个也就是最左侧完整左子树的左节点,第一个TreeNode node = stack.pop();list.add(node.val);// 如果当前节点有右子树,继续这样的迭代。类似模拟递归,保证右子树正常压入// 如果没有的话,下一伦迭代将弹出下一个应该弹出的值,直到结束if (node.right != null) {curNode = node.right;}}return list;}}
后序遍历迭代
class Solution {public List<Integer> postorderTraversal(TreeNode root) {// 总体思路:使用2个栈,一个栈暂时保持,一个栈输出结果。// 因为输出结果栈必然要是左右中的输出,所以压入的时候要中右左压入。// 那么中间栈需要弹出按照中右左弹出// 因为我们在一开始时已经压入根节点,然后迭代开始时弹出这个根,再压入左右子树// 还是有一种模拟递归的感觉,每次迭代的时候都是先弹出最上层,也就是将根节点先压入结果栈// 然后再压入其左右子树。// 初始化操作Stack<TreeNode> tempStack = new Stack<>();Stack<TreeNode> resStack = new Stack<>();List<Integer> resList = new ArrayList<>();if (root == null) {return resList;}// 先压入根节点tempStack.push(root);// 开始迭代,临时栈为空退出while (!tempStack.isEmpty()) {// 压入结果栈,按照递归的思想就相当于:在结果栈中先压根,再右,再左。TreeNode curNode = tempStack.pop();resStack.push(curNode);// 在临时栈中压入左和右,弹出并压入结果栈的操作交由上面2行if (curNode.left != null) {// 不用担心节点是否需要移动,因为栈能准确弹出目标节点并指向tempStack.push(curNode.left);}if (curNode.right != null) {tempStack.push(curNode.right);}}while (!resStack.isEmpty()) {TreeNode resNode = resStack.pop();resList.add(resNode.val);}return resList;}}
