1

配列内のバイナリ ツリーの次の実装があります。

   32
  /  \
 2    -5
     /  \
   -331   399

データは一度に 3 つのインデックスでグループ化されます。 index%3==0はノードindex%3==1の値、 は左側のノードのindex%3==2値のインデックス、 は右側のノードの値のインデックスです。左または右のインデックス参照が 0 の場合、その方向のノードはありません。

私はこの木の深さ (高さ) を見つけようとしています。再帰的に書きました

height(node): 
   if node == null:
        return 0
   else:
        return max(height(node.L), height(node.R)) + 1

ただし、非再帰的な解決策を見つけたいです。

ツリーが空ではないと仮定して、私が持っている疑似コードを次に示します

int i = 0; int left = 0; int right = 0;
while (i != n ){
if ( a[i+1] != 0 ){
  left++;
}
else if ( a[i+2] != 0 ){
  right++;
}
 i = i + 3;
 }

return max ( left, right ) + 1;

これは正しくないと思います。これを正しく行う方法を理解するのに助けが必要です。

4

1 に答える 1

1

改善したい動作を理解するために、再帰の問題が何であるかを述べていません。

これには多くの解決策がありますが、ほとんどすべての解決策で、再帰的な解決策と同じか、パフォーマンスが低下します。実際、最善の解決策は、ツリーを作成するときに行う必要があることです。たとえば、各ノードの高さをノードごとに 4 番目の配列インデックスに格納できます。次に、最大の高さを見つけるために、4 つおきのインデックスを簡単にスキャンします。また、高さチェック中に計算する必要がないように、ノードに親参照が保存されていると簡単になります。

1 つの解決策は、スタックを使用して再帰をシミュレートすることですが、それは実際には再帰と変わりません。

別の解決策は、各ノードを調べて、その親に基づいてその高さを決定することですが、特定のトラバーサルの順序ではありません。ただし、これをどのように構成したかにより、階層を格納するためのセカンダリ データ構造がないため、O(n^2) の効率が低下します。問題は、配列全体をスキャンしないと、子から親に到達できないことです。次に、線形時間で実行できます(ただし、再帰も線形時間であるため、改善されているかどうかはわかりません。メモリの観点からも、それほど改善されるわけではありません)。

改善したい効率の種類を定義できますか?

それぞれの疑似コードは次のとおりですが、簡単には存在しないいくつかのデータ構造に依存しています。

「再帰なしの再帰」ソリューション:

int get_height(int * tree, int length) {

    Stack stack;

    int max_height = 0;

    if (length == 0) {
        return 0;
    }

    // push an "array" of the node index to process and the height of its parent.  
    //   make this a struct and use that for real c code
    stack.push(0,0);

    while(!stack.empty()) {
        int node_index, parent_height = stack.pop();

        int height = parent_height + 1;
        if (height > max_height) {
            max_height=height;
        }
        if (tree[node_index+1] != 0 )
            stack.push(tree[node_index+1], height);
        if (tree[node_index+2] != 0 )
            stack.push(tree[node_index+2], height);

    }

    return max_height;
}

現在、追加のメモリを使用しない非常に遅いソリューションに取り組んでいますが、それは本当に悪いです。フィボナッチを再帰的に悪いと書くようなものです。元のアルゴリズムは各ノードを通過し、O(n^2) の実行時間に対して O(n) チェックを実行しました (実際には、当初考えていたほど悪くはありません)。

編集:ずっと後、子を持つすべてのノードをスキップする最適化を追加しています。これは、多くの通話をカットするので、非常に重要です。最良のケースは、ツリーが実際にリンクされたリストである場合です。この場合、O(n) 時間で実行されます。最悪の場合は、完全にバランスの取れたツリーです。それぞれの logn リーフ ノードが O((log(n)^2) のルートに戻って logn チェックを行います。これはそれほど悪くはありません。以下の行はそのようにマークされます。

「本当に遅いが余分なメモリがない」ソリューション(ただし、それほど遅くないように更新されました):

int get_height(int * tree, int length) {
    int max_height = 0;
    for (int i = 0; i < length; i+=3) {

        // Optimization I added later
        // if the node has children, it can't be the tallest node, so don't
        //   bother checking from here, as the child will be checked
        if (tree[i+1] != 0 || tree[i+2] != 0)
            continue;

        int height = 0;
        int index_pointing_at_me;

        // while we haven't gotten back to the head of the tree, keep working up
        while (index_pointing_at_me != 0) {
            height += 1; 
            for (int j = 0; j < length; j+=3) {
                if (tree[j+1] == tree[i] ||
                    tree[j+2] == tree[i]) {
                    index_pointing_at_me = j;
                    break;
                }
            }

        }
        if (height > max_height) {
            max_height = height;
        }

    }

    return max_height;
}

以前のソリューションを改善しましたが、O(n) メモリを使用します。これは、配列内で親が常に子の前にあることを前提としています (技術的には必要ないと思います)。

int get_height(int * tree, int length) {

    if (length == 0) 
        return 0;

    // two more nodes per node - one for which node is its parent, the other for its height
    int * reverse_mapping = malloc((sizeof(int) * length / 3) * 2) 
    reverse_mapping[1] = 1; // set height to 1 for first node



    // make a mapping from each node to the node that points TO it.
    // for example, for the first node
    //    a[0] = 32
    //    a[1] = 3
    //    a[2] = 6
    //  store that the node at 3 and 6 are both pointed to by node 0 (divide by 3 just saves space since only one value is needed) and that each child node is one taller than its parent
    int max_height = 0;
    for (int i = 0; i < length; i+=3) {

        int current_height = reverse_mapping[(i/3)*2+1];
        if (current_height > max_height)
            max_height = current_height;

        reverse_mapping[(tree[i+1]/3)*2] = i;
        reverse_mapping[(tree[i+1]/3)*2 + 1] = current_height + 1;

        reverse_mapping[(tree[i+2]/3)*2] = i;
        reverse_mapping[(tree[i+2]/3)*2 + 1] = current_height + 1;

    }
    return max_height
}
于 2013-05-19T04:46:18.613 に答える