中序遍歷 前序遍歷 後序遍歷 程式設計題_力扣(LeetCode)二叉樹的前序、中序、後序遍歷...
94. 二叉樹的中序遍歷
144. 二叉樹的前序遍歷
145. 二叉樹的後序遍歷
給定一個二叉樹,返回它的前序、中序、後序 遍歷。
示例:
輸入: [1,null,2,3]
前序輸出: [1,2,3]
中序輸出: [1,3,2]
後序輸出: [3,2,1]
進階: 遞迴演算法很簡單,你可以通過迭代演算法完成嗎?
來源:力扣(LeetCode)
連結:二叉樹的中序遍歷 - 力扣(LeetCode)
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。
題目分析:
樹的遍歷方式主要有2種,深度優先遍歷和廣度優先遍歷,廣度遍歷主要為層級遍歷,而深度遍歷主要分前序遍歷、中序遍歷、後序遍歷,這3種遍歷說的前序、中序和後序是指父節點,順序如下,切莫理解為前序先遍歷左節點,後序先遍歷右節點。
前序遍歷,父節點先遍歷,遍歷順序為:父節點-》左節點-》右節點
中序遍歷,父節點中間遍歷,遍歷順序為:左節點-》父節點-》右節點
後序遍歷,父節點後遍歷,遍歷順序為:左節點-》右節點-》父節點
具體的解法相信只要學習過資料結構,都會的遞迴遍歷,程式碼非常簡單。
解法一:遞迴遍歷
由於給的案例模板裡面的方法帶出參,所以我們需要自己寫一個遞迴函式,把list作為入參傳到遞迴函式中。
public List<Integer> preorderTraversal(TreeNode root); public List<Integer> inorderTraversal(TreeNode root); public List<Integer> postorderTraversal(TreeNode root);
定義樹節點:
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
遞迴程式碼如下:
1、前序遍歷
/** * 二叉樹的前序遍歷 * @param root * @return */ public List<Integer> preorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); preorderTraversal(root,res); return res; } /** * 前序遍歷遞迴函式 * @param root * @param res */ private void preorderTraversal(TreeNode root, List<Integer> res) { if(root != null){ res.add(root.val); preorderTraversal(root.left, res); preorderTraversal(root.right,res); } }
2、中序遍歷
/**
* 中序遍歷,先遍歷左節點,後中節點,後右節點
*
* @param root
* @return
*/
public static List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
inorderTraversal(root, list);
return list;
}
/**
* 遞迴函式
*
* @param root
* @param list
*/
private static void inorderTraversal(TreeNode root, List<Integer> list) {
if (root != null) {
inorderTraversal(root.left, list);
list.add(root.val);
inorderTraversal(root.right, list);
}
}
3、後序遍歷
/**
* 二叉樹的後序遍歷
* @param root
* @return
*/
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
// 遞迴遍歷
postorderTraversal(root,res);
return res;
}
/**
* 後序遍歷遞迴函式
* @param root
* @param res
*/
private void postorderTraversal(TreeNode root, List<Integer> res) {
if(root!=null){
// 先遍歷左節點
postorderTraversal(root.left,res);
// 遍歷右節點
postorderTraversal(root.right,res);
// 遍歷父節點
res.add(root.val);
}
}
複雜度分析:
時間複雜度:O(n)
空間複雜度:O(n)
解法二:棧遍歷
學過jvm的同學都知道,沒學過也沒關係,記住結論就行,java中所有方法呼叫都儲存在棧中,而遞迴就是不停的呼叫方法,所以遞迴方法呼叫多少次,每次入參是什麼等都儲存在棧中,只不過是系統幫我們實現了。當然,我們自己也可以用棧去遍歷樹而不適用遞迴方法。所有的遞迴方法都可以用棧實現哦。
1、前序遍歷
/**
* 二叉樹的前序遍歷-棧
* @param root
* @return
*/
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
// 申請一個棧空間,儲存節點
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
while(curr != null || !stack.isEmpty()){
// 當前節點的左節點深度遍歷
while(curr!=null){
res.add(curr.val);
stack.push(curr);
curr = curr.left;
}
// 當前節點的左節點訪問完畢,訪問右節點,對右節點的左節點繼續深度遍歷
curr = stack.pop();
curr = curr.right;
}
return res;
}
2、中序遍歷
public List<Integer> inorderTraversal(TreeNode root) {
// 申請一個棧空間,儲存節點
Stack<TreeNode> stack = new Stack<>();
List<Integer> res = new ArrayList<>();
// 定義一個指標,指向根節點
TreeNode curr = root;
while (curr != null || !stack.isEmpty()) {
// 每指向一個節點,把該節點push到棧中,且將左節點push到棧中
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
// 左節點不存在,因為是中序遍歷,棧中最後一個元素出棧並訪問
curr = stack.pop();
res.add(curr.val);
// 指標指向右節點,開始下一個迴圈,訪問該右節點的左節點
curr = curr.right;
}
return res;
}
3、後序遍歷,在leetcode中,後序遍歷被定義為困難級別,當然,遞迴演算法不可能是困難級別,定義困難的主要原因是利用棧的迭代演算法。
從前面的演算法中我們可以看出:前序的演算法是 父-》左-》右,後序的演算法順序是 左-》右-》父,而在迭代演算法中最後訪問父節點實現起來比較困難,此時我們可以轉換一下思路,我們將後序演算法的順序反向計算,即 左《-右《-父,先遍歷父節點,再遍歷右節點,再遍歷左節點,將遍歷結果反向輸出,或者儲存的時候在連結串列的最前端儲存,同樣可以達到後序遍歷的效果,總結就是:用前序遍歷的演算法實現後序遍歷。
具體的程式碼在前序遍歷的技術上略作修改即可,具體程式碼如下:
/**
* 二叉樹的後序遍歷-棧
* @param root
* @return
*/
public List<Integer> postorderTraversal3(TreeNode root) {
LinkedList<Integer> res = new LinkedList<>();
// 定義一個棧
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
while(curr != null || !stack.isEmpty()){
// 當前節點的右節點深度遍歷
while(curr!=null){
res.addFirst(curr.val);
stack.push(curr);
curr = curr.right;
}
// 當前節點的左節點訪問完畢,訪問右節點,對右節點的左節點繼續深度遍歷
curr = stack.pop();
curr = curr.left;
}
return res;
}
複雜度分析:
時間複雜度:O(n)
空間複雜度:O(n)