Day 13 二叉樹part01
二叉樹的遞迴遍歷
這個用遞迴就好,現在寫起來基本沒問題
二叉樹的迭代遍歷
這個是重點,今天寫的時候居然一次寫出來了,多刷還是有用的。前中後三種遍歷方式,其迭代版本的難度排序 前 < 中 < 後。所以寫的時候也是按這個順序去做的。
144. 二叉樹的前序遍歷
使用一個棧來儲存需要遍歷的節點,注意,當遍歷一個節點後,我們需要先將其右節點壓棧再壓棧左節點,這樣可以保證先遍歷左子樹再右子樹。
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null) return res;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode tmp = stack.pop();
res.add(tmp.val);
if(tmp.right != null) stack.push(tmp.right);
if(tmp.left != null) stack.push(tmp.left);
}
return res;
}
}
94. 二叉樹的中序遍歷
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null) return res;
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null || !stack.isEmpty()){ //cur代表要遍歷的節點
while(cur != null){ //一直向左走,一直到null
stack.push(cur);
cur = cur.left;
}
cur = stack.pop(); //此時從棧中彈出的節點一定是最左的,因此可以遍歷它了
res.add(cur.val);
cur = cur.right; //當該節點遍歷完,就應該去遍歷其右子樹
}
return res;
}
}
145. 二叉樹的後序遍歷
與中序遍歷非常相似,一定要比較著學。
與中序的不同之處在於:
- 中序遍歷中,從棧中彈出的節點,其左子樹是訪問完了,可以直接訪問該節點,然後接下來訪問右子樹。
- 後序遍歷中,從棧中彈出的節點,我們只能確定其左子樹肯定訪問完了,但是無法確定右子樹是否訪問過。
因此,我們在後序遍歷中,引入了一個pre來記錄歷史訪問記錄。
- 當訪問完一棵子樹的時候,我們用pre指向該節點。
- 這樣,在回溯到父節點的時候,我們可以依據pre是指向左子節點,還是右子節點,來判斷父節點的訪問情況。
while(!stack.isEmpty() || cur != null)對於中序遍歷和後序遍歷判斷條件的理解:
- 對於左子樹訪問和右子樹訪問是不同的,當訪問一個節點的時候,我們會從該節點開始,壓棧並一直向左走,直到沒有左子樹才去彈棧,然後轉向去訪問右子樹
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root, pre = null;
while(cur != null || !stack.isEmpty()){
while(cur != null){ //和中序遍歷完全一致,走到最左即可
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
if(cur.right == null || cur.right == pre) {
//如果一個節點沒有右子樹,或者右子樹已經遍歷過,那麼這個節點就可以被訪問了
res.add(cur.val);
pre = cur;
cur = null;
}else{ //右子樹還沒遍歷,先將cur還原回棧中,轉而遍歷右子樹
stack.push(cur);
cur = cur.right;
}
}
return res;
}
}