寫之前先看作用:
先序:中 左 右的順序(先訪問根節點,先序遍歷左子樹,先序遍歷右子樹)
中序:左 中 右的順序
後序:左 右 中的順序
遍歷過程相同,只是訪問各個節點的時機不同
先序遍歷
遞歸實現:
public static void preOrderRecur(TreeNode treeNode) { if (treeNode==null) { return; } System.out.println(treeNode.val); preOrderRecur(treeNode.left); preOrderRecur(treeNode.right); }
夠簡單,需要注意終止條件。否則會空指針異常。
非遞歸實現(自己維護一個棧):
public static void preOrder(TreeNode treeNode) { if(treeNode==null) { return; } Stack<TreeNode> stack = new Stack<TreeNode>();//注意泛型 stack.push(treeNode); while (stack.size()!=0) { treeNode=stack.pop(); System.out.println(treeNode.val + " "); if(treeNode.right!=null)//注意不爲null的條件,否則出棧會空指針 { stack.push(treeNode.right);//由於先處理左子樹,後處理右子樹,所以壓棧順序反過來就行 } if(treeNode.left!=null) { stack.push(treeNode.left); } } }
這個迭代寫的有點像遞歸了。。。
中序遍歷
遞歸寫法,真的爽,不用考慮具體的壓棧過程,關注什麼時候結束和遞歸邏輯就行
class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> list=new ArrayList<>(); parse(root,list); return list; } public void parse(TreeNode root,List<Integer> list) { if(root==null) { return; } parse(root.left,list); list.add(root.val); parse(root.right,list); } }
非遞歸寫法:
class Solution { public List<Integer> inorderTraversal(TreeNode root) { Stack<TreeNode> stack=new Stack<>(); List<Integer> list=new ArrayList<>(); while(stack.size()>0||root!=null) { while(root!=null)//處理root節點,去往最左側,出循環後棧頂爲優先級最高節點 { stack.push(root); root=root.left; } TreeNode outNode=stack.pop();//到這裏保證左側已經沒有東西了,outnode代表目前全樹優先級最高的節點 list.add(outNode.val);//處理“中” if(outNode.right!=null)//處理“右”,這一步加上去往最左能保證右子樹被處理完 { root=outNode.right; } } return list; } }
- root是要處理的節點,當root不爲null時,還沒有找到最高優先級的節點,當root爲null時,最高優先級節點在棧頂,且這個最高優先級節點的父親在棧的第二位
- 處理最高優先級節點的辦法是,彈出棧,輸出最高優先級節點,處理右子樹(即把root設置爲其右孩子)
- 還是有點繞的。。。
後序遍歷
遞歸寫法,差不多
class Solution { public List<Integer> postorderTraversal(TreeNode root) { List<Integer> list=new ArrayList<>(); postOrder(root,list); return list; } public 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> postorderTraversal(TreeNode root) { List<Integer> list=new ArrayList<>(); if(root==null) {return list;} Stack<TreeNode> stackReverse=new Stack<TreeNode>(); Stack<TreeNode> stackResult=new Stack<TreeNode>(); stackReverse.push(root); while(stackReverse.size()>0) { root=stackReverse.pop(); stackResult.add(root); if(root.left!=null) {stackReverse.push(root.left);} if(root.right!=null) {stackReverse.push(root.right);} } while(stackResult.size()>0) { list.add(stackResult.pop().val); } return list; } }
第二種,硬寫唄
和中序遍歷有點像,先要找優先級最高的節點root,往左探
當root爲null時,cur=stack.pop,但是此時優先級更高的不是cur而是cur.right
所以加入cur.right不爲null,肯定要先處理右側節點,cur要先放回棧
爲了到時右側節點被處理完了,cur再出棧時能知道,所以加了一個pre(因爲cur.right處理完肯定下一個就是cur)
class Solution { public List<Integer> postorderTraversal(TreeNode root) { List<Integer> list=new ArrayList<>(); if(root==null) {return list;} Stack<TreeNode> stack=new Stack<TreeNode>(); TreeNode pre=null; while(stack.size()>0||root!=null) { while(root!=null) { stack.push(root); root=root.left; } TreeNode cur=stack.pop(); if(cur.right==null||pre==cur.right) { list.add(cur.val); pre=cur; } else { stack.push(cur); root=cur.right; } } return list; } }