Description

給定一棵二元樹,用前序遍歷的方式將其轉換成字串,使用括號表示子樹。需要省略不影響一對一映射關係的空括號,但若左子樹為空而右子樹存在,左子樹的空括號不能省略。

Example:

Input: root = [1,2,3,4] Output: “1(2(4))(3)”

Intuition#

前序遍歷加上括號表示子樹結構,關鍵是判斷何時需要保留空括號。

  • 若左右子樹都為空,不需要括號
  • 若左子樹為空但右子樹存在,左邊需要空括號 () 來保持結構明確
  • 若左子樹存在但右子樹為空,右邊的括號可以省略

Approaches#

⭐ 1: Recursive DFS#

  • 概念: 前序遍歷,根據左右子樹是否存在決定括號的添加
  • 時間複雜度: O(n),每個節點訪問一次
  • 空間複雜度: O(h),遞迴堆疊深度
class Solution {
    fun tree2str(root: TreeNode?): String {
        if (root == null) return ""
        val sb = StringBuilder()
        fun dfs(node: TreeNode?) {
            if (node == null) return
            sb.append(node.`val`)
            if (node.left != null || node.right != null) {
                sb.append("(")
                dfs(node.left)
                sb.append(")")
            }
            if (node.right != null) {
                sb.append("(")
                dfs(node.right)
                sb.append(")")
            }
        }
        dfs(root)
        return sb.toString()
    }
}

2: Iterative with Stack#

  • 概念: 用 Stack 模擬前序遍歷,使用 Set 記錄已處理的節點來決定何時加右括號
  • 時間複雜度: O(n)
  • 空間複雜度: O(n)
class Solution {
    fun tree2str(root: TreeNode?): String {
        if (root == null) return ""
        val sb = StringBuilder()
        val stack = ArrayDeque<TreeNode>()
        val visited = HashSet<TreeNode>()
        stack.addLast(root)
        while (stack.isNotEmpty()) {
            val node = stack.last()
            if (node in visited) {
                stack.removeLast()
                sb.append(")")
            } else {
                visited.add(node)
                sb.append("(${node.`val`}")
                if (node.left == null && node.right != null) {
                    sb.append("()")
                }
                node.right?.let { stack.addLast(it) }
                node.left?.let { stack.addLast(it) }
            }
        }
        // 移除最外層的括號
        return sb.substring(1, sb.length - 1)
    }
}

🔑 Takeaways#

  • Pattern: 樹的序列化 — 用括號表示子樹結構
  • 關鍵技巧: 省略括號的規則:右子樹為空可省略,但左子樹為空且右子樹存在時必須保留空括號 ()