【總結】二叉樹的前中後序遍歷(遞迴和非遞迴)
阿新 • • 發佈:2020-12-19
一、前中後序遞迴
1. 前序遞迴( 時間複雜度O(n)、空間複雜度O(n))
class Solution { public List<Integer> preorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); dfs(root, res); return res; } private void dfs(TreeNode root, List<Integer> res) {if (root == null) return; res.add(root.val); // 根 dfs(root.left, res); // 左 dfs(root.right, res); // 右 } }
2. 中序遞迴( 時間複雜度O(n)、空間複雜度O(n))
class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); dfs(root,res);return res; } private void dfs(TreeNode root, List<Integer> res) { if (root == null) return; dfs(root.left, res); // 左 res.add(root.val); // 根 dfs(root.right, res); // 右 } }
3. 後序遞迴( 時間複雜度O(n)、空間複雜度O(n))
class Solution { public List<Integer> postorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); dfs(root, res); return res; } private void dfs(TreeNode root, List<Integer> res) { if (root == null) return; dfs(root.left, res); // 左 dfs(root.right, res);// 右 res.add(root.val);// 根 } }
二、前中後序非遞迴
前中後序遍歷都屬於深度遍歷,所以需要使用棧。層次遍歷需要用到佇列。
1. 前序非遞迴(時間複雜度O(n)、空間複雜度O(n))
class Solution { public List<Integer> preorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); Stack<TreeNode> stack = new Stack<>(); while (!stack.isEmpty() || root != null) { while (root != null) { res.add(root.val); stack.push(root); root = root.left; } root = stack.pop(); root = root.right; } return res; } }
2. 中序非遞迴( 時間複雜度O(n)、空間複雜度O(n))
class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); Stack<TreeNode> stack = new Stack<>(); while (!stack.isEmpty() || root != null) { while (root != null) { stack.push(root); root = root.left; } root = stack.pop(); res.add(root.val); root = root.right; } return res; } }
3.後序非遞迴( 時間複雜度O(n)、空間複雜度O(n))
class Solution { public List<Integer> postorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); Stack<TreeNode> stack = new Stack<>(); TreeNode pre = null; //pre節點用於記錄前一次訪問的節點 while (!stack.isEmpty() || root != null) { while (root != null) { stack.push(root); root = root.left; } root = stack.peek(); // 若右節點為空 或右節點訪問過 if (root.right == null || root.right == pre) { res.add(root.val); //此時可以訪問根結點啦 stack.pop(); pre = root; root = null; //此時下一輪迴圈不要將左子樹壓棧,直接判斷棧頂元素 }else { root = root.right; //先不出棧 把它右節點入棧 } } return res; } }
後序非遞迴的另一種寫法,修改前序【根左右】的遍歷順序為【根右左】,前序遍歷是往後加元素,後序遍歷是往前加元素
class Solution { //修改前序遍歷,每次先檢視右節點再檢視左節點。同時,節點寫入結果將插入隊尾修改為插入隊首 public List<Integer> postorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); Stack<TreeNode> stack = new Stack<>(); while (!stack.isEmpty() || root != null) { while (root != null) { res.add(0,root.val); // 取根節點的值,插入list最後邊 stack.push(root); root = root.right; // 遍歷右子樹 } root = stack.pop(); root = root.left; // 遍歷左子樹 } return res; } }
三、層序遍歷
class Solution { public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> res = new ArrayList<>(); if (root == null) return res; Queue<TreeNode> queue = new LinkedList<>(); // 層序需要藉助佇列 queue.offer(root); while (!queue.isEmpty()) { int level = queue.size(); List<Integer> list = new ArrayList<>(); for (int i = 0; i < level; i++) { TreeNode cur = queue.poll(); list.add(cur.val); if (cur.left != null) queue.offer(cur.left); if (cur.right != null) queue.offer(cur.right); } res.add(list); } return res; } }
四、相關習題
LeetCode144. 二叉樹的前序遍歷、LeetCode94. 二叉樹的中序遍歷、LeetCode145. 二叉樹的後序遍歷、LeetCode102. 二叉樹的層序遍歷
參考: