No. 114 - Flatten Binary Tree to Linked List ==== ![Problem](https://img.shields.io/badge/problem-tree-blue) ![Difficulty](https://img.shields.io/badge/difficulty-medium-yellow) --- ## [LeetCode 114 -- Flatten Binary Tree to Linked List](https://leetcode.com/problems/flatten-binary-tree-to-linked-list/) ### 題目描述 Given the root of a binary tree, flatten the tree into a "linked list": - The "linked list" should use the same TreeNode class where the right child pointer points to the next node in the list and the left child pointer is always null. - The "linked list" should be in the same order as a pre-order traversal of the binary tree. Example. ![](https://assets.leetcode.com/uploads/2021/01/14/flaten.jpg) ``` Input: root = [1,2,5,3,4,null,6] Output: [1,null,2,null,3,null,4,null,5,null,6] ``` --- 本題的目的是要將 binary tree 轉成 linked list,而 linked list 的順序是要照 preorder traversal 走訪的順序。 我們將講解兩種做法,分別是 postorder traversal 跟 morris traversal。 ### 解法 1. Postorder Traversal 我們先以下面這個簡單的例子來說明 ![](https://i.imgur.com/4hQhjo3.jpg) 可以看到我們要轉成 linked list 其實就是把 left child 移到 right child 並將原本的 right child 接到後面。 我們在示範稍微複雜的 binary tree,可以看到當走訪到 2 的時候它就是把左邊 4 跟右邊 5 合併,然後再回到 1 把左子樹跟右子樹合併。 ![](https://i.imgur.com/pvAVE4L.jpg) 我們可以看到上面的流程它是先把 2 的左子樹右子樹合併才回到 1 去合併,但其實回去把 1 的左子樹跟右子樹合併前除了先把其左子樹合併,也要先確認右子樹延伸下去的左子樹跟右子樹也合併了。 而要確認這件事,我們可以用 postorder traversal 來做到,也就是先把左子樹跟右子樹都走訪了(合併)再回到父 node 來將其左子樹跟右子樹合併。 ```cpp= /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: void postorderTraversal(TreeNode *node) { if (!node) return; postorderTraversal(node->left); postorderTraversal(node->right); // Merge left subtree with right subtree. TreeNode *tmp = node->right; node->right = node->left; node->left = nullptr; while (node->right) node = node->right; node->right = tmp; } void flatten(TreeNode* root) { postorderTraversal(root); } }; ``` ### 解法 2. Morris Traversal 這個演算法有三個步驟: - Step 1. 若當前 node 存在左子樹則走訪當前 node 的左子樹,把左子樹會走訪的最後一個 node 的 right pointer 指回這棵左子樹的 parent 的 right child,並接續 step 2。若不存在左子樹,則直接跳到 step 3。 - Step 2. 把左子樹改成接到右子樹。 - Step 3. 走訪 right child,並回到 step 1 重複動作。 以下面的例子為例,step 1 先走訪 node 1 的左子樹,並把左子樹會走訪的最後一個 node 5 的 right pointer 指向 node 1 的 right child node 3。step 2 把左子樹的 root,node 2 改接到 node 1 的 right child。接著 step 3 走訪 right child,node 2,然後繼續重複前面的步驟。 ![](https://i.imgur.com/U9QbqPC.jpg) ```cpp= /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: void flatten(TreeNode* root) { TreeNode *cur = root; while (cur) { TreeNode *prev = cur->left; if (prev) { while (prev->right) prev = prev->right; prev->right = cur->right; cur->right = cur->left; cur->left = nullptr; } cur = cur->right; } } }; ``` ### 複雜度分析 在時間複雜度上,兩個方法的複雜度都是 $O(N)$。 空間複雜度上,postorder traversal 的複雜度為 $O(H)$。而 morris traversal 的複雜度為 $O(1)$。