国产gaysexchina男同gay,japanrcep老熟妇乱子伦视频,吃奶呻吟打开双腿做受动态图,成人色网站,国产av一区二区三区最新精品

C++構建二叉樹問題

2023-09-20 09:23 更新

Question

給定一個二叉樹的前序遍歷 preorder 和中序遍歷 inorder ,請從中構建二叉樹,返回二叉樹的根節(jié)點。

構建二叉樹的示例數據

圖 12-5   構建二叉樹的示例數據

判斷是否為分治問題

原問題定義為從 preorderinorder 構建二叉樹,其是一個典型的分治問題。

  • 問題可以被分解:從分治的角度切入,我們可以將原問題劃分為兩個子問題:構建左子樹、構建右子樹,加上一步操作:初始化根節(jié)點。而對于每個子樹(子問題),我們仍然可以復用以上劃分方法,將其劃分為更小的子樹(子問題),直至達到最小子問題(空子樹)時終止。
  • 子問題是獨立的:左子樹和右子樹是相互獨立的,它們之間沒有交集。在構建左子樹時,我們只需要關注中序遍歷和前序遍歷中與左子樹對應的部分。右子樹同理。
  • 子問題的解可以合并:一旦得到了左子樹和右子樹(子問題的解),我們就可以將它們鏈接到根節(jié)點上,得到原問題的解。

如何劃分子樹

根據以上分析,這道題是可以使用分治來求解的,但如何通過前序遍歷 preorder 和中序遍歷 inorder 來劃分左子樹和右子樹呢?

根據定義,preorderinorder 都可以被劃分為三個部分。

  • 前序遍歷:[ 根節(jié)點 | 左子樹 | 右子樹 ] ,例如圖 12-5 的樹對應 [ 3 | 9 | 2 1 7 ] 。
  • 中序遍歷:[ 左子樹 | 根節(jié)點 | 右子樹 ] ,例如圖 12-5 的樹對應 [ 9 | 3 | 1 2 7 ]

以上圖數據為例,我們可以通過圖 12-6 所示的步驟得到劃分結果。

  1. 前序遍歷的首元素 3 是根節(jié)點的值。
  2. 查找根節(jié)點 3 在 inorder 中的索引,利用該索引可將 inorder 劃分為 [ 9 | 3 | 1 2 7 ] 。
  3. 根據 inorder 劃分結果,易得左子樹和右子樹的節(jié)點數量分別為 1 和 3 ,從而可將 preorder 劃分為 [ 3 | 9 | 2 1 7 ] 。

在前序和中序遍歷中劃分子樹

圖 12-6   在前序和中序遍歷中劃分子樹

基于變量描述子樹區(qū)間

根據以上劃分方法,我們已經得到根節(jié)點、左子樹、右子樹在 preorderinorder 中的索引區(qū)間。而為了描述這些索引區(qū)間,我們需要借助幾個指針變量。

  • 將當前樹的根節(jié)點在 preorder 中的索引記為 i 。
  • 將當前樹的根節(jié)點在 inorder 中的索引記為 m 。
  • 將當前樹在 inorder 中的索引區(qū)間記為 [l,r] 。

如表 12-1 所示,通過以上變量即可表示根節(jié)點在 preorder 中的索引,以及子樹在 inorder 中的索引區(qū)間。

表 12-1   根節(jié)點和子樹在前序和中序遍歷中的索引

根節(jié)點在 preorder 中的索引 子樹在 inorder 中的索引區(qū)間
當前樹 i [l,r]
左子樹 i+1 [l,m?1]
右子樹 i+1+(m?l) [m+1,r]

請注意,右子樹根節(jié)點索引中的 (m?l) 的含義是“左子樹的節(jié)點數量”,建議配合圖 12-7 理解。

根節(jié)點和左右子樹的索引區(qū)間表示

圖 12-7   根節(jié)點和左右子樹的索引區(qū)間表示

4.   代碼實現?

為了提升查詢 m 的效率,我們借助一個哈希表 hmap 來存儲數組 inorder 中元素到索引的映射。

build_tree.cpp

/* 構建二叉樹:分治 */
TreeNode *dfs(vector<int> &preorder, unordered_map<int, int> &inorderMap, int i, int l, int r) {
    // 子樹區(qū)間為空時終止
    if (r - l < 0)
        return NULL;
    // 初始化根節(jié)點
    TreeNode *root = new TreeNode(preorder[i]);
    // 查詢 m ,從而劃分左右子樹
    int m = inorderMap[preorder[i]];
    // 子問題:構建左子樹
    root->left = dfs(preorder, inorderMap, i + 1, l, m - 1);
    // 子問題:構建右子樹
    root->right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r);
    // 返回根節(jié)點
    return root;
}

/* 構建二叉樹 */
TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) {
    // 初始化哈希表,存儲 inorder 元素到索引的映射
    unordered_map<int, int> inorderMap;
    for (int i = 0; i < inorder.size(); i++) {
        inorderMap[inorder[i]] = i;
    }
    TreeNode *root = dfs(preorder, inorderMap, 0, 0, inorder.size() - 1);
    return root;
}

圖 12-8 展示了構建二叉樹的遞歸過程,各個節(jié)點是在向下“遞”的過程中建立的,而各條邊(即引用)是在向上“歸”的過程中建立的。

構建二叉樹的遞歸過程

built_tree_step2

built_tree_step3

built_tree_step4

built_tree_step5

built_tree_step6

built_tree_step7

built_tree_step8

built_tree_step9


圖 12-8   構建二叉樹的遞歸過程

每個遞歸函數內的前序遍歷 preorder 和中序遍歷 inorder 的劃分結果如圖 12-9 所示。

每個遞歸函數中的劃分結果

圖 12-9   每個遞歸函數中的劃分結果

設樹的節(jié)點數量為 n ,初始化每一個節(jié)點(執(zhí)行一個遞歸函數 dfs() )使用 O(1) 時間。因此總體時間復雜度為 O(n)

哈希表存儲 inorder 元素到索引的映射,空間復雜度為 O(n) 。最差情況下,即二叉樹退化為鏈表時,遞歸深度達到 n ,使用 O(n) 的棧幀空間。因此總體空間復雜度為 O(n) 。

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號