1. 程式人生 > 其它 >中序遍歷 前序遍歷 後序遍歷 程式設計題_力扣(LeetCode)二叉樹的前序、中序、後序遍歷...

中序遍歷 前序遍歷 後序遍歷 程式設計題_力扣(LeetCode)二叉樹的前序、中序、後序遍歷...

94. 二叉樹的中序遍歷

144. 二叉樹的前序遍歷

145. 二叉樹的後序遍歷

給定一個二叉樹,返回它的前序、中序、後序 遍歷。

示例:

輸入: [1,null,2,3]

b9e41a6f130d81e29f5ae09952b188c7.png

前序輸出: [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中,後序遍歷被定義為困難級別,當然,遞迴演算法不可能是困難級別,定義困難的主要原因是利用棧的迭代演算法。

從前面的演算法中我們可以看出:前序的演算法是 父-》左-》右,後序的演算法順序是 左-》右-》父,而在迭代演算法中最後訪問父節點實現起來比較困難,此時我們可以轉換一下思路,我們將後序演算法的順序反向計算,即 左《-右《-父,先遍歷父節點,再遍歷右節點,再遍歷左節點,將遍歷結果反向輸出,或者儲存的時候在連結串列的最前端儲存,同樣可以達到後序遍歷的效果,總結就是:用前序遍歷的演算法實現後序遍歷。

f77fcd428cedc3e1a5a08ef32d1d9266.png

具體的程式碼在前序遍歷的技術上略作修改即可,具體程式碼如下:

/**
 * 二叉樹的後序遍歷-棧
 * @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)