今日のアルゴリズム 04 - 二分木の再構成

1. トピックの説明

問題のリンク: leetcode.cn/problems/zh…

難易度: 中程度

バイナリツリーのpreorder traversalとinorder traversalの結果を入力し、バイナリツリーを構築し、そのルートノードを返します。

入力の事前順序トラバーサルと順序トラバーサルの結果のどちらにも重複する数値が含まれていないと仮定します。

2. 問題解決のアイデア

分割統治

事前順序トラバーサル プロパティ: ノードは[ 根节点 | 左子树 | 右子树 ]で並べ替えられます。

順序トラバーサルのプロパティ: ノード[ 左子树 | 根节点 | 右子树 ]は で並べ替えられます。

上記の特性に基づいて、次の推論を引き出すことができます。

事前順序トラバーサルの最初の値は、ルート ノードの値です。この値を使用して、順序トラバーサルの結果を 2 つの部分に分割します。左側の部分は、ツリーの左側のサブツリーの順序トラバーサルの結果であり、右側の部分は、ツリーの右側のサブツリーの順序トラバーサル結果。次に、左と右のサブツリーをそれぞれ再帰的に解決します。

この種の問題を解決するには、分割統治の考え方を使用するのが適しています。分割統治のコードは次の構造のように要約できます。

 def divide_conquer(problem, param1, param2, ...):
     # 1.终止条件
     if problem is None:
         print_result
         return
     # 2.准备数据
     data = prepare_data(problem)
     subproblems = split_problem(problem, data)
     # 3.处理子问题
     subresult1 = self.divide_conquer(subproblems[0], p1, ...)
     subresult2 = self.divide_conquer(subproblems[1], p1, ...)
     subresult3 = self.divide_conquer(subproblems[1], p1, ...)
     # ...
     
     result = process_result(subresult1, subresult2, subresult3, ...)

分割統治アルゴリズム分析:

  1. 終了条件: 事前順序トラバーサルのルート ノードのインデックスroot、順序トラバーサルのサブツリーの左境界preL、および順序トラバーサルのサブツリーの右境界preRpreL > preRリーフ ノードが終了することを意味する場合交差した場合、この時点では null が返されます。

  2. データを準備します:

    • ルート ノードを作成しますnode。ノード値は次のとおりですpreorder[root]
    • 左と右のサブツリーを分割します。順序トラバーサルでルート ノードinorderのインデックスを見つけますi
  3. サブ問題の処理: 左と右のサブツリー再帰を有効にします。

    ルートノードインデックス インオーダートラバーサルの左境界 インオーダートラバーサルの右境界
    左のサブツリー ルート+1 私 - 1
    右サブツリー i - 左 + ルート + 1 私+1

複雑さの分析

時間計算量: O(N)、N はツリー内のノードの数です。HashMap の初期化には順番にトラバースする必要があり、これには O(N) かかります。合計 N 個のノードが再帰的に確立され、再帰の各層でのノードの確立と検索操作には O(1) かかるため、O(N) 時間が使用されます。

スペースの複雑さ: O(N)、HashMap は O(N) の追加スペースを使用します。最悪の場合 (入力バイナリ ツリーがリンク リストの場合)、再帰の深さは N に達し、O(N) のスタック フレーム スペースを占有します。合計 O(N) 個が N) 個のスペースとして使用されます。

3. コードの実装

 // 缓存中序遍历数组每个值对应的索引
 private Map<Integer, Integer> indexForInOrders = new HashMap<>();
 ​
 public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
     for (int i = 0; i < in.length; i++)
         indexForInOrders.put(in[i], i);
     return reConstructBinaryTree(pre, 0, pre.length - 1, 0);
 }
 ​
 private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int inL) {
     if (preL > preR)
         return null;
     TreeNode root = new TreeNode(pre[preL]);
     int inIndex = indexForInOrders.get(root.val);
     int leftTreeSize = inIndex - inL;
     root.left = reConstructBinaryTree(pre, preL + 1, preL + leftTreeSize, inL);
     root.right = reConstructBinaryTree(pre, preL + leftTreeSize + 1, preR, inL + leftTreeSize + 1);
     return root;
 }

推奨読書

カバー

今日のアルゴリズムシリーズ、ソリューション更新アドレス:studeyang.tech/2023/0718.h…

おすすめ

転載: juejin.im/post/7257077672362246203