1. 程式人生 > >【LeetCode題解】二叉樹的遍歷

【LeetCode題解】二叉樹的遍歷

【LeetCode題解】二叉樹的遍歷

我準備開始一個新系列【LeetCode題解】,用來記錄刷題,順便複習一下資料結構與演算法。

1. 二叉樹

二叉樹(binary tree)是一種極為普遍的資料結構,樹的每一個節點最多隻有兩個節點——左孩子結點與右孩子結點。C實現的二叉樹:

struct TreeNode {
    int val;
    struct TreeNode *left; // left child
    struct TreeNode *right; // right child
};

DFS

DFS的思想非常樸素:根據結點的連線關係,依次訪問每一個節點,直至遍歷完整棵樹。根據根節點的訪問次序的不同——前、中、後,可分為先序、中序、後序遍歷。先序遍歷是指先訪問根節點,再依次訪問左孩子節點、右孩子節點。下圖給出一個二叉樹的先序、中序、後序遍歷示例:

遞迴實現:遞迴呼叫,列印根節點。C實現如下:

// preorder binary tree traversal
void preorder(struct TreeNode *root) {
    if(root) {
        printf("%d", root->val);
        preorder(root -> left);
        preorder(root -> right);
    }
}

void inorder(struct TreeNode *root) {
    if(root) {
        inorder(root -> left);
        printf("%d", root->val);
        inorder(root -> right);
    }
}

void postorder(struct TreeNode *root) {
    if(root) {
        postorder(root -> left);
        postorder(root -> right);
        printf("%d", root->val);
    }
}

任何遞迴實現的程式都可以改用棧實現。比如,先序遍歷時用棧維護待訪問結點;先將根結點入棧,再將右孩子結點入棧、左孩子結點入棧。Java實現如下:

public List<Integer> preorderTraversal(TreeNode root) {
  List<Integer> result = new ArrayList<>();
  if (root == null) return result;
  Stack<TreeNode> stack = new Stack<>();
  stack.push(root);
  while (!stack.isEmpty()) {
    TreeNode node = stack.pop();
    result.add(node.val);
    if (node.right != null) stack.push(node.right);
    if (node.left != null) stack.push(node.left);
  }
  return result;
}

中序遍歷,棧維護已訪問結點。一個結點只有當其左孩子結點已訪問時,才能出棧,然後入棧右孩子結點;否則,則入棧左孩子結點。

public List<Integer> inorderTraversal(TreeNode root) {
  List<Integer> result = new ArrayList<>();
  if (root == null) return result;
  Stack<TreeNode> stack = new Stack<>();
  stack.push(root);
  while (!stack.isEmpty()) {
    TreeNode node = stack.peek();
    if (node.left != null) {
      stack.push(node.left);
      node.left = null; // its left child has been visited
    } else {
      result.add(node.val);
      stack.pop();
      if (node.right != null)
        stack.push(node.right);
    }
  }
  return result;
}

後序遍歷,棧也維護已訪問結點。一個結點只有當其左右孩子結點均已訪問時,才能出棧;否則,則依次入棧右孩子結點、左孩子結點。

public List<Integer> postorderTraversal(TreeNode root) {
  List<Integer> result = new ArrayList<>();
  if (root == null) return result;
  Stack<TreeNode> stack = new Stack<>();
  stack.push(root);
  while (!stack.isEmpty()) {
    TreeNode node = stack.peek();
    if (node.left == null && node.right == null) {
      result.add(node.val);
      stack.pop();
    } else {
      if (node.right != null) {
        stack.push(node.right);
        node.right = null; // its right child has been visited
      }
      if (node.left != null) {
        stack.push(node.left);
        node.left = null; // its left child has been visited
      }
    }
  }
  return result;
}

層次遍歷

層次遍歷為二叉樹的BFS,是指按照結點的層級依次從左至右訪問。實現層序遍歷需要藉助於佇列,用以維護待訪問的結點。

public List<List<Integer>> levelOrder(TreeNode root) {
  List<List<Integer>> result = new LinkedList<>();
  if (root == null) return result;
  Queue<TreeNode> queue = new LinkedList<>();
  queue.offer(root);
  while (!queue.isEmpty()) {
    int levelSize = queue.size();
    List<Integer> levelList = new LinkedList<>();
    for (int i = 0; i < levelSize; i++) {
      TreeNode node = queue.poll();
      levelList.add(node.val);
      if (node.left != null) queue.offer(node.left);
      if (node.right != null) queue.offer(node.right);
    }
    result.add(levelList);
  }
  return result;
}

2. 題解

LeetCode題目 歸類
100. Same Tree  
101. Symmetric Tree  
104. Maximum Depth of Binary Tree  
111. Minimum Depth of Binary Tree  
110. Balanced Binary Tree  
112. Path Sum  
113. Path Sum II  
129. Sum Root to Leaf Numbers  
144. Binary Tree Preorder Traversal 先序
94. Binary Tree Inorder Traversal 中序
145. Binary Tree Postorder Traversal 後序
102. Binary Tree Level Order Traversal 層次
107. Binary Tree Level Order Traversal II 層次
103. Binary Tree Zigzag Level Order Traversal 層次
114. Flatten Binary Tree to Linked List  

100. Same Tree
判斷兩棵樹是否一樣,遞迴解決:

public boolean isSameTree(TreeNode p, TreeNode q) {
  if (p == null && q == null) return true;
  if (p == null || q == null) return false;
  return p.val == q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}

101. Symmetric Tree
同上一題較為類似,判斷一棵樹是否中心對稱:

public boolean isSymmetric(TreeNode root) {
  return root == null || helper(root.left, root.right);
}

private boolean helper(TreeNode p, TreeNode q) {
  if (p == null && q == null) return true;
  if (p == null || q == null) return false;
  return p.val == q.val && helper(p.left, q.right) && helper(p.right, q.left);
}

104. Maximum Depth of Binary Tree
二叉樹的最大深度,遞迴實現之:

public int maxDepth(TreeNode root) {
  if (root == null) return 0;
  return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}

111. Minimum Depth of Binary Tree
二叉樹的最小深度,解決思路與上類似,不過有個special case——根不能視作為葉子節點:

public int minDepth(TreeNode root) {
  if (root == null) return 0;
  if (root.left == null || root.right == null)
    return 1 + Math.max(minDepth(root.left), minDepth(root.right));
  return 1 + Math.min(minDepth(root.left), minDepth(root.right));
}

110. Balanced Binary Tree
判斷一棵樹是否平衡,即每一個節點的左右子樹的深度不大於1,正好可用到上面求樹深度的程式碼:

public boolean isBalanced(TreeNode root) {
  return root == null || Math.abs(maxDepth(root.left) - maxDepth(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
}

112. Path Sum
判斷root-to-leaf路徑的結點之和是否存在,遞迴實現之:

public boolean hasPathSum(TreeNode root, int sum) {
  if (root == null) return false;
  if (root.val == sum && root.left == null && root.right == null) return true; // root is leaf node
  return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
}

113. Path Sum II
上一個問題的變種,要將所有滿足sum的情況儲存下來;解決辦法採用兩個list儲存中間結果,要注意list適當remove:

public List<List<Integer>> pathSum(TreeNode root, int sum) {
  List<List<Integer>> result = new LinkedList<>();
  List<Integer> pathList = new LinkedList<>(); // store a path which meets required sum
  helper(root, sum, result, pathList);
  return result;
}

private void helper(TreeNode root, int sum, List<List<Integer>> result, List<Integer> pathList) {
  if (root != null) {
    pathList.add(root.val);
    if (root.val == sum && root.left == null && root.right == null) {
      result.add(new LinkedList<>(pathList)); // deep copy
    } else {
      helper(root.left, sum - root.val, result, pathList);
      helper(root.right, sum - root.val, result, pathList);
    }
    pathList.remove(pathList.size() - 1); // remove the last element
  }
}

129. Sum Root to Leaf Numbers

二叉樹的root-to-leaf路徑表示一個十進位制數,求所有路徑所代表的數之和,遞迴實現:

public int sumNumbers(TreeNode root) {
  return helper(root, 0);
}

private int helper(TreeNode root, int sum) {
  if (root == null) return 0; // special case
  if (root.left == null && root.right == null) return sum * 10 + root.val;
  return helper(root.left, sum * 10 + root.val) + helper(root.right, sum * 10 + root.val);
}

144. Binary Tree Preorder Traversal
先序遍歷,參看前面的棧實現。

94. Binary Tree Inorder Traversal
中序遍歷,同上。

145. Binary Tree Postorder Traversal
後序遍歷,同上。

102. Binary Tree Level Order Traversal
層次遍歷,同上。

107. Binary Tree Level Order Traversal II
層次遍歷變種,不同的是從bottom開始往上遍歷;只需在層次遍歷的程式碼中修改從頭入result連結串列而不是從尾入:

result.addFirst(levelList);

103. Binary Tree Zigzag Level Order Traversa

“之”字形層次遍歷,在層次遍歷的基礎上,將層級分為奇數、偶數,分別按順序、逆序出隊。

public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
  List<List<Integer>> result = new LinkedList<>();
  if (root == null) return result;
  Queue<TreeNode> queue = new LinkedList<>();
  queue.add(root);
  int level = 1;
  while (!queue.isEmpty()) {
    int levelSize = queue.size();
    List<Integer> levelList = new LinkedList<>();
    while (levelSize-- > 0) { // to deal with level nodes
      TreeNode node = queue.poll();
      levelList.add(node.val);
      if (node.left != null) queue.add(node.left);
      if (node.right != null) queue.add(node.right);
    }
    if (level % 2 == 0) // be stack order when level is even
      Collections.reverse(levelList);
    result.add(levelList);
    level++;
  }
  return result;
}

114. Flatten Binary Tree to Linked List
將二叉樹打散成長鏈條樹,從遞迴的角度來看,可分解為三個步驟:打散左子樹,打散右子樹,然後將右子樹拼接到左子樹尾部。

public void flatten(TreeNode root) {
  helper(root, null);
}

// flatten sub-tree, return its root node
private TreeNode helper(TreeNode root, TreeNode last) {
  if (root == null) return last;
  root.right = helper(root.left, helper(root.right, last));
  root.left = null;
  return root;
}

如需轉載,請註明作者及出處.

作者:Treant

出處:http://www.cnblogs.com/en-heng/