簡介:中等,題意為給予中序 (inorder)與後序(postorder)走訪二元樹 (binary tree)的順序 (traversal),建構出原本的二元樹。
一、概念
中序 (inorder)與後序 (postorder)走訪二元樹的示意圖 橘色背景為子樹的根 (root of subtree),藍箭頭為左子樹,粉箭頭為右子樹 |
(一)前中後走訪特性
以上圖右上角二元樹為例,其前中後序為:
- 前序:3, 9, 20, 15, 7
- 中序:9, 3, 15, 20, 7
- 後序:9, 15, 7, 20, 3
可以發現根與子樹位置是有規則的。
- 前序根為最左元素,中序根分開左右子樹,後序根為最右元素
- 前中後序左子樹位在右子樹左方,右子樹也在左子樹右方
綜合以上兩規則,只要給予前序與中序,即可透過根的位置知道左子樹大小,進而推敲出右子樹大小;同理如果給予後序與中序,也可透過根知道左右子樹大小。
(二)分治法 (divide and conquer)
題目是給中序與後序要我們建構二元樹,在綜合上述特性,我們可以使用分治法:
- 找根元素 (conquer):後序最右元素必是根
- 找子樹大小 (divide):得知根元素後,定位出中序的根元素,即可得知左右子樹大小
二、實作
(一)使用Map存中序的value-index
static TreeNode* buildTree(vector<int>&inorder, vector<int>& postorder) { // 1. Store the index of inorder. unordered_map index; for (int i = 0; i < inorder.size(); ++i) {index[inorder[i]] = i;} // 2. Build a tree by inorder and postorder. return buildTreeHelper(inorder, postorder, 0, inorder.size() - 1, 0, postorder.size() - 1, index); }
- 第一步為了快速在中序中定位出根元素的位置,我們使用unorder_map (hash table)去紀錄value與index的關係,定位時間複雜度為O(1)。
- 第二步直接使用遞迴函數,去建構二元樹。
(二)分治法建構二元樹
static TreeNode* buildTreeHelper(vector<int>&inorder, vector<int>& postorder, const int inorderStart, const int inorderEnd, const int postorderStart, const int postorderEnd, unordered_map<int>& index) { // 1. If Start > End, return NULL if (inorderStart > inorderEnd || postorderStart > postorderEnd) {return NULL;} // 2. Find root and the size of left subtree const int rootValue = postorder[postorderEnd]; TreeNode* root = new TreeNode(rootValue); const int inorderRootIndex = index[rootValue]; const int leftSubtreeSize = inorderRootIndex - inorderStart; // 3. Build the left subtree root->left = buildTreeHelper(inorder, postorder, inorderStart, inorderRootIndex - 1, postorderStart, postorderStart + leftSubtreeSize - 1, index); // 4. Build the right subtree root->right = buildTreeHelper(inorder, postorder, inorderRootIndex + 1, inorderEnd, postorderStart + leftSubtreeSize, postorderEnd - 1, index); return root; }
主要有四部分:
- 檢查邊界:如果中序或後序頭與尾交錯,則代表此樹只剩下一個元素,無法在分割,故回傳NULL
- 找根元素與左右子樹大小
- 建構左子樹,左子樹邊界請參照上圖藍箭頭
- 建構右子樹,右子樹邊界請參照上圖粉箭頭
三、相關文章
請詳見LeetCode相關文章
留言
張貼留言