リンク: http://www.geeksforgeeks.org/segment-tree-set-1-sum-of-given-range/ 。これは引用されたテキストです:
セグメントarr [0から始めます。 。 。 n-1]。そして、現在のセグメントを2つに分割するたびに(まだ長さ1のセグメントになっていない場合)、両方の半分で同じプロシージャを呼び出し、そのようなセグメントごとに、対応するノードに合計を格納します。構築されたセグメントツリーのすべてのレベルは、最後のレベルを除いて埋められます。また、すべてのレベルでセグメントを常に2つに分割するため、ツリーはフルバイナリツリーになります。構築されたツリーは常にn個の葉を持つ完全な二分木であるため、n-1個の内部ノードが存在します。したがって、ノードの総数は2n –1になります。セグメントツリーの高さはceil [log(n)]になります。ツリーは配列を使用して表され、親インデックスと子インデックスの関係を維持する必要があるため、セグメントツリーに割り当てられるメモリのサイズは次のようになります。 。
メモリはどのように割り当てられますか(上記のパラグラフの最後の行)?正しい場合、親と子のインデックスはコードにどのように格納されますか?この背後にある理由を教えてください。これがfalseの場合、正しい値は何ですか?
ここで起こっていることは、n個の要素の配列がある場合、セグメントツリーにはこれらのn個のエントリごとにリーフノードがあります。したがって、(n)個のリーフノードと(n-1)個の内部ノードがあります。
ノードの総数= n +(n-1)= 2n-1これで、完全な二分木がわかったので、高さは次のようになります。ceil(Log2(n))+ 1
総数ノードの数= 2 ^ 0 + 2 ^ 1 + 2 ^ 2 +…+ 2 ^ ceil(Log2(n))//これは等比数列であり、2 ^ iはレベルiのノードの数を示します。
総和の公式G.P. = a *(r ^ size-1)/(r-1)ここで、a = 2 ^ 0
総数ノードの数= 1 *(2 ^(ceil(Log2(n))+ 1)-1)/(2-1)
= 2 * [2 ^ ceil(Log2(n))] -1(この数の内部ノードとリーフノードのそれぞれについて、配列内にスペースが必要です)。したがって、これはサイズの配列です。
= O(4 * n)約。
このように考えることもできます。以下をセグメントツリーとします。
10
/ \
3 7
/\ /\
1 2 3 4
上記がセグメントツリーの場合、セグメントツリーの配列は次のようになります。10,3,7,1,2,3,4つまり、0番目の要素は1番目と2番目のエントリの合計を格納し、1番目のエントリはの合計を格納します。 3位と4位と2位は5位と6位のエントリーの合計を格納します!!
また、より適切な説明は次のとおりです。配列サイズnが2の累乗である場合、正確にn-1内部があります。ノード、合計で最大2n-1ノード。ただし、常にではありませんが、2の累乗としてnがあるため、基本的に2の最小の累乗が必要です。 n。つまり、これは、
int s=1;
for(; s<n; s<<=1);
あなたは私の同じ答えを見るかもしれません ここ
奇妙なことに、私はこれに出くわしたときの質問と同じ情報源から読んでいました。頑張ってお答えします。
ツリー表現の基本的な違いから始めましょう(contextのみ):
ほぼ「最悪の場合」のシナリオ。これは完全にバランスが取れていないので、トラバースするのは本当に楽しいものではありません。どうして?入力が異なると、異なるツリーが生成される可能性があるため、トラバースにかかる時間はあまり予測できません。
私たちの「ベストケース」シナリオ。これは完全にバランスが取れているか完全であり、常にトラバースするのに予測可能な時間がかかります。さらに、このツリーも優れています「ハッキング」。
では、質問に戻りましょう。 [最初の画像を参照]すべてのn-input配列(greenの数値))、n-1個の内部ノード(blueの数字)があります。したがって、最大2n-1ノードスペースを割り当てる必要があります。
しかし、コード ここ は逆に何かをします。なぜ、どのように?
期待すること:メモリが2n-1に割り当てられることを期待しますノードで十分です。言い換えれば、これは行われるべきです:
int *st = new int[2*n - 1];
コードの残りの部分がうまく機能すると仮定すると、これはあまり良い考えではありません。これは、最初の場合と同様に、不均衡なツリーが作成されるためです。このようなツリーは、トラバースするのも、問題解決に適用するのも簡単ではありません。
実際に何が起こるか:null
または0
の値でメモリを追加/パディングします。私たちはこれを行います:
int x = (int)(ceil(log2(n))); //Height of segment tree
int max_size = 2*(int)pow(2, x) - 1; //Maximum size of segment tree
int *st = new int[max_size];
つまり、バランスの取れた完全なツリーを生成するのに十分なスペースを割り当てます。このようなツリーは(いくつかの特別な変更を使用して)簡単にトラバースでき、問題に直接適用できます。
ケース2に十分なメモリをどのように割り当てましたか?方法は次のとおりです。
バランスの取れたセグメントツリーには、少なくとも3つのコンポーネントがあることがわかっています。
また、kの葉を持つ平衡木は次のようになることもわかっています。
2つを組み合わせると、望ましい結果が得られます。
int x = (int)(ceil(log2(n))); //Height of segment tree
int max_size = 2*(int)pow(2, x) - 1; //Maximum size of segment tree
int *st = new int[max_size];
雑学クイズ!2を上記のx
の累乗にすると、最も近い上限整数が確実に得られます。
n
(入力配列内の要素の数)以上。入力配列のサイズをnとします。
すべての入力配列要素はセグメントツリーのリーフノードになるため、リーフノードの数= n
セグメントツリーは 完全なツリー なので、セグメントツリーの高さ h =⌈ログ2n⌉+ 1
高さ「h」の二分木のノードの最大数は 2h-1
したがって、セグメントツリー内のノードの数= 2⌈ログ2n⌉+ 1 -1
に等しい 2 * 2⌈ログ2n⌉ -1
セグメントツリーは、すべての葉が入力配列の要素を示すフルバイナリツリーになります。そして言及として ここ
完全な二分木におけるノードの数nは、少なくともn = 2h + 1そして最大でn = 2 ^ {h + 1} -1、ここでhは木の高さです。そしてh = log_2n.
Note - log_2n indicates log base 2
セグメントツリー内のノードの最大数を見つけるためのpythonコード-
from math import pow, log, ceil
def initialize_seg_tree(input_arr):
n = len(input_arr)
height = ceil(log(n, 2))
# max_nodes = 2^(h+1) - 1, where h = log(n) // base 2
seg_tree_size = int(pow(2, height + 1) - 1)
seg_tree_arr = empty_1d_array(seg_tree_size)
return seg_tree_arr
ここにいくつかのリンクがあります..n(任意の数)の長さの配列からサイズ2 * n-1のセグメントツリーを構築するための反復実装 https://www.geeksforgeeks.org/segment-tree-efficient-implementation/ n(任意の数)の長さの配列からサイズ2 * n-1のセグメントツリーを構築するための再帰的な実装 https://www.hackerearth.com/practice/notes/segment-tree-and-lazy -propagation /#c191521
n(任意の数)の配列から4 * n未満のサイズのセグメントツリーを構築するための反復実装 https://codeforces.com/blog/entry/18051