二叉樹的遍歷方式包括前序遍歷、中序遍歷和後序遍歷,其實現方式包括遞歸實現和非遞歸實現。java
前序遍歷:根節點 | 左子樹 | 右子樹node
中序遍歷:左子樹 | 根節點 | 右子樹post
後序遍歷:左子樹 | 右子樹 | 根節點指針
遞歸方式實現代碼十分簡潔,三種遍歷方式的遞歸實現代碼結構相同,只是執行順序有所區別。code
前序遍歷:遞歸
public class preOrderRecur { List<Integer> res = new ArrayList<>(); public List<Integer> preOrderTraversal(TreeNode root) { if (root != null) { res.add(root.val); // 根節點 preOrderTraversal(root.left); // 左子樹 preOrderTraversal(root.right); // 右子樹 } return res; } }
中序遍歷:class
public class inOrderRecur { List<Integer> res = new ArrayList<>(); public List<Integer> inOrderTraversal(TreeNode root) { if (root != null) { inOrderTraversal(root.left); // 左子樹 res.add(root.val); // 根節點 inOrderTraversal(root.right); // 右子樹 } } return res; }
後序遍歷:List
public class inOrderRecur { List<Integer> res = new ArrayList<>(); public List<Integer> inOrderTraversal(TreeNode root) { if (root != null) { inOrderTraversal(root.left); // 左子樹 inOrderTraversal(root.right); // 右子樹 res.add(root.val); // 根節點 } } return res; }
public class inOrderIterator { List<Integer> res = new ArrayList<>(); public List<Integer> inOrderTraversal(TreeNode root) { Stack<TreeNode> stack = new Stack<>(); while (root != null || !stack.isEmpty()) { if (root != null) { stack.push(root); root = root.left; } else { TreeNode node = stack.pop(); res.add(node.val); root = node.right; } } return res; } }
方法1:由於前序遍歷訪問順序是「中-左-右」,因此能夠先將根結點壓棧,而後按照下列步驟執行。二叉樹
public class preOrderIterator { List<Integer> res = new ArrayList<>(); public List<Integer> inOrderTraversal(TreeNode root) { if (root == null) return res; Stack<TreeNode> stack = new Stack<>(); stack.push(root); while (!stack.isEmpty()) { root = stack.pop(); res.add(root.val); // 右孩子壓棧 if (root.right != null) stack.push(root.right); // 左孩子壓棧 if (root.left != null) stack.push(root.left); } return res; } }
方法2:根據中序遍歷進行微調:搜索
public class preOrderIterator { List<Integer> res = new ArrayList<>(); public List<Integer> inOrderTraversal(TreeNode root) { Stack<TreeNode> stack = new Stack<>(); while (root != null || !stack.isEmpty()) { if (root != null) { res.add(root.val); stack.push(root); root = root.left; } else { TreeNode node = stack.pop(); root = node.right; } } return res; } }
由於前序遍歷的順序是「左-中-右」,然後序遍歷順序是「左-右-中」,不考慮左結點,區別只是在於中結點和右結點的順序進行了反向而已,所以可使用前序遍歷的代碼進行調整,只須要將前序遍歷對左右孩子壓棧的順序反向便可,即先壓入左孩子,再壓入右孩子。除此以外,由於按照這種方法調整獲得的遍歷順序爲「中-右-左」,正好是後序遍歷的反向順序,所以在得到遍歷序列後還需進行逆序操做。
public class postOrderIterator { List<Integer> res = new LinkedList<>(); public List<Integer> postOrderTraversal(TreeNode root) { if (root == null) return res; Stack<TreeNode> stack = new Stack<>(); stack.push(root); while (!stack.isEmpty()) { root = stack.pop(); // 頭插法 res.add(0, root.val); // 左孩子壓棧 if (root.left != null) stack.push(root.left); // 右孩子壓棧 if (root.right != null) stack.push(root.right); } return res; } }
該方法的思路簡單說就是,對於每個結點,找到它左孩子的最右子結點,由於按照正常訪問順序,其左孩子的最有子節點訪問完後就應該訪問其自己了,所以將其左孩子最右子節點的右指針指向它。基本步驟以下:
該方法雖然保證了O(1)的空間複雜度,但在遍歷過程當中改變了部分結點的指向,破壞了樹的結構。
public class inOrderMorris { List<Integer> res = new ArrayList<>(); public List<Integer> inOrderTraversal(TreeNode root) { TreeNode pre = null; TreeNode cur = root; while (cur != null) { if (cur.left == null) { res.add(cur.val); cur = cur.right; } else { pre = cur.left; while (pre.right != null && pre.right != cur) pre = pre.right; if (pre.right == null) { pre.right = cur; cur = cur.left; } else { res.add(cur.val); pre.right = null; cur = cur.right; } } } return res; } }
public class preOrderMorris { List<Integer> res = new ArrayList<>(); public List<Integer> preOrderTraversal(TreeNode root) { TreeNode pre = null; TreeNode cur = root; while (cur != null) { if (cur.left == null) { res.add(cur.val); cur = cur.right; } else { pre = cur.left; while (pre.right != null && pre.right != cur) pre = pre.right; if (pre.right == null) { res.add(cur.val); pre.right = cur; cur = cur.left; } else { pre.right = null; cur = cur.right; } } } return res; } }
前序遍歷反向的思想
public class postOrderMorris { List<Integer> res = new LinkedList<>(); public List<Integer> postOrderTraversal(TreeNode root) { TreeNode pre = null; TreeNode cur = root; while (cur != null) { if (cur.right == null) { res.add(0, cur.val); cur = cur.left; } else { pre = cur.right; while (pre.left != null && pre.left != cur) pre = pre.left; if (pre.left == null) { res.add(0, cur.val); pre.left = cur; cur = cur.right; } else { pre.left = null; cur = cur.left; } } } return res; } }