論文翻訳: PRESERVING ORDER IN A FOREST IN LESS THAN LOGARITHMIC TIME
P. van Emde Boas
Mathematical Centre, Amsterdam, Netherlands /
Mathematical Institute, University of Amsterdam
ABSTRACT
我々は階層化二分木 (stratified binary tree) に基づくデータ構造を提示する。このデータ構造により、区間
Table of Contents
- ABSTRACT
- 1. 導入
- 2. 一般的な背景
- 3.
の処理時間を持つ "愚直な" 優先度付きキュー - 4. 階層化木構造
- 5. 階層化木構造における操作
- 6. 階層化木の応用
- 7. 集合捜査問題間の帰着性
- ACKNOWLEDGEMENTS
- REFERENCES
- 翻訳抄
1. 導入
集合操作のための効率的なアルゴリズム設計における主要な問題は、同時に実行したい異なる操作がもたらす矛盾した要求から生じる。集合内の要素の挿入、削除、あるいは要素の所属判定 (membership) を行う命令は、ランダムアクセスをサポートするデータ構造を必要とする。一方、最小要素や最大要素の値、あるいは特定の要素の後続要素や先行要素の値を計算する命令は、順序付けられた表現を必要とする。最後に、2 つの集合を結合する命令は、これまでのところツリー構造を用いてのみ効率的に実装されてきた。
これらの矛盾の 1 つを解決する効率的なアルゴリズムの例として、よく知られている union-find アルゴリズムが挙げられる。
順序アクセスとランダムアクセスという競合する要求を解決するためにこれまでに発表されてきたアルゴリズムは、いずれもオンラインで実行されなければならない
順序アクセスとランダムアクセスの競合を解決するために用いられてきたデータ構造には (とりわけ) バイナリヒープ、AVL 木、2-3 木がある。AHO, HOPCROFT & ULLMAN [1] では、命令レパートリー INSERT, DELETE, UNION, MIN をサポートするために 2-3 木が使用されており、最悪ケースで命令あたり
優先度付きキューやマージ可能ヒープの操作にかかる
別の例として、PERL, GAREY & EVEN によって最近発表された最適なプレフィックスコードを生成するための効率的なアルゴリズム [5] が挙げられる。最悪ケースの実行時間に表れる
本論文では、ランダムアクセスマシン上で命令あたり
1.1 論文の構成
セクション 2 では、効率的な union-find アルゴリズムの説明を含む、いくつかの表記法と背景情報を述べる。セクション 3 では、命令あたり
セクション 4 では、我々の階層化木とそれらの標準的な部分木への分解について説明する。次に、これらの木を用いて命令あたり最悪ケースおよび平均ケースの処理時間が
セクション 4, 5, 6 を通して、この異なるフォントで記述された識別子は PASCAL 実装における同じ識別子の値と意味を表す。
2. 一般的な背景
2.1 命令
| MIN | |
|---|---|
| MAX | |
| INSERT(j) | |
| DELETE(j) | |
| MEMBER(j) | |
| EXTRACT MIN | |
| EXTRACT MAX | |
| PREDECESSOR(j) | |
| SUCCESSOR(j) | |
| NEIGHBOUR(j) | |
| ALL MIN(j) | |
| ALL MAX(j) | |
(命令が正規に実行できない場合、例えば
数
例:
2.2 優先度付きキュー
優先度付きキューは単一の集合
上記の完全な命令リストを以後拡張レパートリー (extended repertoire) と呼ぶ。
2.3 Union-Find 問題
- FIND(i)
-
を含んでいる集合を計算する - UNION(A,B,C)
- 集合
と の和集合を形成し、この和集合に という名前を付ける
これらの 2 つの命令をサポートするデータ構造に特定の名前はない。このような構造を操作する問題は Union-Find 問題として知られている。
よく知られている効率的な Union-Find アルゴリズムは、木構造による集合の表現を使用する。木構造の各ノードは集合の要素に対応し、そのノードがその木の根である場合は集合の名前を指すポインタを持ち、そうでない場合は木構造内の親ノードを指すポインタを持つ。UNION 命令は、より小さい木の根をより大きい木の直接の子ノードにすることで実行される (平衡化; balancing)。FIND 命令を実行するには、要求された要素に対応するノードに直接アクセスし、そのポインタをたどってその木の根が見つかるまで探索する。この過程で遭遇するすべてのノードは根の直接の子孫となるため、それにより皇族の検索での処理時間が削減される。
上記のアルゴリズムがいかに効率的であるかは最近になってようやく確立された。その平均処理時間は当初
2.4 マージ可能ヒープ
マージ可能ヒープとは、それ自体が結合および検索可能な集合で、INSERT, DELETE, MEMBER, MIN 命令をサポートするデータ構造である。すなわち UNION と FIND もサポートされている。マージ可能ヒープは、特定のノードの順序付けされていない子の集合を優先度付きキューで置き換えることによって Union-Find 構造から得ることができる。ここでノードの "値" は、そのノードとその子孫によって形成される集合内の最小要素に等しい。
このような表現では、命令は以下のように実行される:
- UNION
- 要素数が最も少ない構造の根優先度付きキューを、その最小要素に対応する位置でもう一方の構造の根優先度付きキューに挿入される。
- FIND
- まず要素自体から "上向き" に進み、その要素が属する構造の根優先度付きキューを見つける。次に、この根から要素まで戻りながら、経路に沿った優先度付きキューを削除操作によって切断する。その後、これらのキューは根優先度付きキューの (変更されている可能性のある) 最小要素の位置に挿入される。
- MIN
- 構造の根優先度付きキューで MIN 命令を実行するとその最小要素が判明する。この要素に対して FIND 命令を実行することで、その要素が格納されている場所にアクセスできるようになる。
- INSERT & DELETE
- これらの操作は、まず FIND 命令を実行することで優先度付きキューの挿入と削除に帰着される。MEMBER についても同様である。
このようにすることで、命令の平均処理時間は、使用されている優先度付きキュー命令の処理時間の
3. の処理時間を持つ "愚直な" 優先度付きキュー
3.1 構造
このセクションで述べるスキームは、主に次のセクションで説明するより複雑な構造に対して実行される操作の背後にある考えを説明するために設計されている。
このセクションでは
木内の各ノードに対して、その親ノードと、その左辺および右辺の子ノードをリンクする 3 つのポインタが関連付けられている。さらに、各ノードは 1 ビットのマークフィールドを持つ。
部分集合
この表現を用いることで、セクション 2 にリストされている操作は、処理される各項目につき
我々は以下にアルゴリズムの概略を提示する:
- INSERT(i)
- 葉
と、葉 から根への経路上のすべてのノードを、既にマークされたノードに遭遇するまでマークする。 - DELETE(i)
- 葉
と、葉 から根への経路上のノードを、マークされた 2 つの子を持つ経路上の最下位ノードまで (ただしそれは含まない) マークを削除する。 - MEMBER(i)
- 葉
がマークされているかどうかを検査する。 - MIN (MAX)
- 根から葉へ進み、常に最左端 (最右端) の存在する子を選択する。
- EXTRACT MIN (EXTRACT MAX)
- MIN (MAX) の後に DELETE を実行。
- ALLMIN(j)
- while MIN
do EXTRACTMIN od - ALLMAX は同様に定義される。
- PREDECESSOR(j)
- 葉
から根へ進み、 を右側子孫として持つノードで左側の子がマークされているものに遭遇するまで進む。この左側の子から葉へ進み、常に最右端の存在する子を取る。 - SUCCESSOR(j) は同様に定義される。
PREDECESSOR と SUCCESSOR 以外のすべての命令は、マークされていない葉から根への経路上の最も低いマークされたノード、またはマークされた葉から根への経路上の最も低い分岐点 (つまり両方の子がマークされているノード) を使用することに注意。最も低い "興味深い" ノードを超えて登らない類似の命令として以下の命令がある:
- HEIGHBOUR(j)
- 葉
から進んで、このノードの "もう一方" の子がマークされている最下位のノードまで進む。このもう一方の子が左側の子である場合、このノードから葉へ進み、常に最右端のマークされた葉を選択する。そうでなければ、常に最左端のマークされた葉を選択する。
さらに、この集合を双方向連結リストを用いて表現し、マークされた各葉にリスト内の対応するエントリ (へのポインタ) を格納すると、NEIGHBOUR 命令の呼び出しに続いてリストを 1~2 ステップ実行するだけで PREDECESSOR や SUCCESSOR 命令の呼び出しを実行するのに十分である。
NEIGHBOUR 命令の説明から、ある数の隣接要素がどのような意味で
3.2. 時間効率の改善
上記の記述から、我々の "愚直な" 構造が命令あたり
残りの命令は "複合的" である。この観察は効率を向上させる道筋を開く。木を高く上向きに登らないことで節約された時間は、単一ノードでより多くの作業を実行するために使用できる。例えば、各ノードが固定数の代わりに存在する子の線形リストを使用することになれば、
しかし改善の余地はまだ多い。単一ノードで実行したい操作自体が、優先付きキュー操作である。したがって、バイナリヒープを使用することで、分岐次数
どちらの変更においても空間要件は
"分割統治" 戦略によれば、各ノードで我々が説明しているのと同じ効率的な構造を使用すべきである。これは次のアプローチを示唆している。全体集合
ブロック内の最初の要素の挿入と最後の要素の削除がブロックのサイズと無関係に定数時間で実行できるような方法で上記のアイディアを実装できると仮定すると、上記の説明は実行時間に対して
この "愚直な" 表現を改善し、再び同じ効率をもたらす別の方法は以下のように考えられる。前述のように、"難しい" 命令は木を上向きに走査して最も低い "興味深い" ノード (例えば分岐点) まで到達し存在ノードの経路に沿って下向きに進むことで処理される。
これらの走査を "レベル上の二分探索" 戦略によって実行できれば、処理時間は
読者は、本論文の続きを読む際には両方のアプローチを念頭に置くべきである。
4. 階層化木構造
4.1. 清純部分木と静的情報
このセクションでは
完全木は明らかにランク
- UC(v)
-
を葉に持つランク の唯一の正準部分木。 - LC(v)
-
を根に持つランク の唯一の正準部分木。
ランク
ノード
ノードの静的情報には、その位置だけでなく、内部ノードの場合はそのランクとレベルも含まれる。静的情報の割り当てと初期化は
4.2. 動的情報
内部ノードの動的情報は 4 つのポインタ l_min, l_max, r_min, r_max と、plus, minus, undefined の値を取ることのできる指示フィールド ub を用いて格納される。葉における動的情報は 2 つのポインタ successor, predecessor, ブール値の present で構成される。
l_min = nil, l_max = nil, r_min = nil, r_max = nil, ub = undefined である。非アクティブな葉では、これらの値は predecessor = nil, successor = nil, present = false である。アクティブな葉
predecessor- 存在する場合は
内の に対応する番号の先行要素に対する葉を指す。そうでない場合は predecessor = nilとなる。 successo- 先行要素と同様
present-
= true
分岐点 (branchpoint) とは、2 つの present な子を持つ内部ノードであることを思い出されたい。
l_min-
の最左端の present 葉を指す (そのようなノードが存在する場合)。存在しなければ l_min = nil。 l_max-
の最右端の present 葉を指す (同様) r_min-
の最左端の present 葉を指す (同様) r_max-
の最右端の present 葉を指す (同様) ub-
t と の間に分岐点が存在する場合は plus、そうでない場合はminus。= plus
nil に等しいアクティブな内部ノードを持つことが不可能であることを示している。
前のセクションで示唆されたように、最初の要素を挿入したり最後の要素を削除したりするために必要な時間は木のサイズに依存しないはずである。これは、そのアクティブ性が必要でない限り、present ノードがアクティブになることを防ぐことで実現される。これは以下のように表現される。
適正性条件 (properness condition):
葉がアクティブであるのは、それが present である場合、かつその場合に限る。根がアクティブであるためには集合が空でなければならない。
(実際には
内部ノード
ある正準半木 (canonical half-tree) が 2 つの present 葉を持つ場合、その中心レベルにあるすべての present ノードはアクティブである。また、ランク
集合
(我々の実際のプログラムでは、この構造は
適正な情報のない例を Diagram 3 に示す (明らかな双方向連結リストデータは省略)。記号 nil または undefined を表す。
5. 階層化木構造における操作
階層化木内の特定のフィールドに値を割り当てることで集合
処理時間
が実現され; 表現の構造を維持、つまり適正性条件が有効なまま
であるように実行する方法を示す。
さらに、静的情報と動的情報の正当な初期状態が適切な時間と空間 (すなわちどちらも
このセクションでは、木の葉によって形成される双方向連結リスト構造を操作するために必要な自明な操作については考慮しない。さらに常に
5.1. 初期化
初期化は、事前順序による単一の木の横断中に行われる。ノードが処理されるとき、その親ノードへのポインタと位置、そして内部ノードの場合はランクとレベルが適切なフィールドに格納される。必要な計算は以下の関係に基づいている:
頂点の親は
nilである。 となるようなノード の直系の子 の親ノードは を満たす。ノード は、木横断処理を実行する再帰手続きのパラメータ fathを使用することで、 の処理中にアクセス可能である。 ノードのレベルは、その親ノードのレベルより 1 小さい。
レベル
における最左端ノードの位置は に等しく、レベル における他の任意のノードの位置は 前に処理されたレベル における最後のノードの位置に等しい。葉はその位置の昇順で処理される。 ノードのランクはレベルのみに依存し、サイズ
の事前計算されたテーブルを使用して格納できる。
必要な 2 のべき乗を反復加算によって事前計算しておくと、上記の関係は、乗算やビット操作などの "非正当な" (illegitimate) 命令を使用せずに、処理されるノードあたり
加算のみを使用して
5.2. 操作
拡張命令レパートリーは (双方向連結リスト操作を除き)、insert, delete, neighbour という 3 つの基本演算で表現できる。これらの演算はそれぞれ線形再帰手続きで記述される。手続きはランク
アルゴリズムの実行において、根
の子孫 が左側の子孫である場合、 の位置は 位置より大きくない場合のみである。
実際、ノードの位置は、子孫の方向性についてこの簡単なテストを容易にするために導入された。
挿入、削除、隣接は以下の基本操作を使用する。
myfields(v,t)-
方向で のフィールドへのポインタを返す。このポインタは fieldptr型である。 mymin(v,t)-
の方向で の フィールド値を返す (これはたまたまポインタである)。 mymax(v,t)-
フィールドについて同様。 yourfileds(v,t),yourmin(v,t),yourmax(v,t)- 他の方向で
のフィールドの対応する値を返す。 minof(t)-
がアクティブの場合、 の 4 つの指示されたフィールドの最左端の値を返し、そうでなければ nilを返す。 maxof- 最右端の値について同様。
型 ranktp は部分範囲 0..h である。
最後に、手続き clear は、その引数の動的フィールドに非アクティブ状態に対応する値を設定する。手続き内で言及される識別子はほとんどが "ノードへのポインタ" 型 (ptr) である。ここで "ノード" は前のセクションで言及されたフィールドを含むレコード型である。
5.2.1. insert 手続き
insert は、挿入されるノードの隣接ノードへのポインタ値を結果としてす関数手続きである。この隣接ノードは、その後にノードを双方向連結リストに挿入するために使用される。(完全木ではない CS の場合に隣接の意味を暗黙的に一般化していることをに注意)。
insert は値渡しで呼び出される 5 つのパラメータがある。その手続きのヘッダは次の通りである:
function insert (leaf, top, pres: ptr; no branchpoint: boolean; order: ranktp): ptr;
パラメータの意味は次の通り:
order- 手続きが呼ばれた CS のランク
leaf- 挿入されるノード
top- 手続きが呼ばれた CS のルート
pres- トップの葉と同じ利便性を持つ、手続きが呼び出された CS の present 葉
nobranchpoint- 手続きが呼び出された CS の葉側に分岐ポイントが含まれていない場合にのみ true
一見するとパラメータ pres は myfields(leaf,top) から導出できるため不要に見える。しかし、対象とする CS が上位の CS のトップ CS である場合、top のフィールドは leaf のレベルよりはるかに下位のノードを参照するため、結果としてそれらの値が誤って解釈される可能性がある。この危険性 (我々のデータ構造の初期バージョン [3] では "動的アドレス変換" によって対処される) は、ノードアドレスに対するビット操作命令では解決できない。なぜなら、それらの実行時間は長さ
対象とする CS の leaf 側に present 葉が含まれていない場合 (pres = nil)、insert の呼び出しはそれ以上の再帰呼び出しを起こすことなく終了する。そうでなければ、ノード hl = FATHER(leaf,order-1) と hp = FATHER(pres,order-1) が計算される。nobranchpoint が true の場合、hp はアクティブではないもの present であり、この場合、特別な処理を実行する必要がある。この場合、hl は present であるのは hl = hp の場合のみであり、この等価性に応じて hp と hl の右 (right; 適切な?) フィールドを "アクティブ化" した後、ボトムコール
insert(leaf, hl, mymin (leaf, hl), true, order-1)
またはトップコール
insert(hl, top, hp, true, order-1)
が実行される。
この状況では、手続きは値として pres を返す。
nobranchpoint が false である場合、hl が present であるのは hl がアクティブである場合のみであり、アクティブかどうかは ub フィールドを調べることで判定される。hl がアクティブな場合、ボトムコール
insert (leaf, hl, mymin (leaf, hl), mymin (leaf, hl) = mymax (lef, hl), order-1)
が実行され、その値が insert の結果として返される。そうでなければ nobranchpoint := (hp↑.ub = minus) を設定し、hl と hp のフィールドをアクティブ化した後、トップコール
insert (hl, top, hp, nobranchpoint, order-1)
が実行される。この呼び出しは結果としてトップツリーの hl の隣接要素が nb で生成され、hl と nb の位置の比較結果に応じて、挿入の値は minof(nb) または maxof(nb) に等しくなる。
これらの処理の後、現在の呼び出しがボトム CS への呼び出しである場合、top ののフィールドを調整する必要があるかもしれない。これは、order が top のランクに等しい場合のみである。この観点から、完全な木はボトム CS を見なす必要があり、これは我々の構造に関する初期報告 [3, 4] で行われたようなレベルがリーフからトップに向かって逆順に番号付けされるのではなく、リーフからトップに向かって番号付けされる理由を説明している。
insert の初期呼び出しは次のようになる:
insert (pt, root, mymin (pt, root), mymin (pt, root) = mymax (pt, root), h)
ここで root はアクティブであり、pt は present 葉ではないと仮定する。(これらの条件はドライバによって強制される。)
次に、insert の完全な PASCAL テキストを示す。
function insert(leaf, top, pres : ptr;
nonbranchpoint : boolean; order : ranktp) : ptr;
var hl, hp, nb : ptr; fptr : fieldptr;
begin if pres = nil then
begin fptr:= myfields(leaf, top);
with fptr↑ do
begin min:= leaf; max:= leaf end;
if leaf↑.position <= top↑.position then
insert:= top↑.right↑.min
else insert:= top↑.left↑.max
end else
begin hl:= leaf↑.fathers[order - 1];
hp:= pres↑.fathers[order - 1];
if nobranchpoint then
if hp <> hl then
begin fptr:= myfields(leaf, hl);
with fptr↑ do
begin min:= leaf; max:= leaf end;
fptr:= myfields(pres, hp);
with fptr↑ do
begin min:= pres; max:= pres end;
hl↑.ub:= plus; hp↑.ub:= plus;
nb:= insert(hl, top, hp, true, order - 1);
insert:= pres
end else
begin fptr:= myfields(pres, hp);
with fptr↑ do
begin min:= pres; max:= pres end;
hp↑.ub:= minus;
insert:= insert(leaf, hl, mymin(leaf, hl),
true, order - 1)
end
else if hl↑.ub <> undefined then
insert:= insert(leaf, hl, mymin(leaf, hl),
mymin(leaf, hl) = mymax(leaf, hl), order- 1)
else
begin fptr:= myfields(leaf, hl);
with fptr↑ do
begin min:= leaf: max:= leaf end;
nobranchpoint:= hp↑.ub = minus;
hl↑.ub:= plus; hp↑.ub:= plus;
nb:= insert(hl, top, hp,
nobranchpoint, order- 1);
if hl↑.position <= nb↑.position then
insert:= minof(nb) else insert:= maxof(nb);
end;
fptr:= myfields(leaf, top);
if top↑.rank = order then with fptr↑ do
if leaf↑.position < min↑.position
then min:= leaf else
if leaf↑.position > max↑. position
then max:= leaf
end;
end
5.2.2. delete 手続き
手続き delete は値を返さない。6 個のパラメータを持ち、最初の 3 つは値渡しで、残りは参照渡しである (結果渡しでも同様だが、これは PASCAL では不可能である)。手続きのヘッダは以下の通り:
procedure delete (leaf, top: ptr; order : ranktp;
var pres 1, pres 2: ptr;
var nobranchpoint: boolean);
値パラメータの意味は以下の通り:
leaf- 削除される葉
top- 対象となる CS のルート
order- 対象となる CS のランク
残りのパラメータは delete 呼び出し後に以下の意味を持つ。
pres 1, pres 2- 対象となる CS 内の present 葉であり、そのうち一つが
leafの隣接要素 (以下の説明を参照) nobranchpointtopからpres 1への経路上に分岐点が発生していない場合に限り true
delete の呼び出しは、leaf とその祖先を再開の分岐点まで非 present 状態にする必要があるが、その過程で異なる経路上にあるアクティブだった他のノードを非アクティブにする必要がある場合がある。この条件が満たされる限り、nobranchpoint は true のままである。
再開の分岐点のもう一つの子から可能な限り下方向に進むと隣接要素に到達する。しかし、常に最も遠い present ノードを選択すると、木内の leaf の極値要素 (extreme) と呼ばれるノードに到達する。極値は leaf の "二進近似" としては隣接要素と同等だが、通常の意味では限りなく遠いノードである。
削除呼び出し後、pres 1 と pres 2 は leaf の位置にしたがって順序付けされた隣接要素と極値要素である (つまり pros1↑.pos ≦ pres 2↑.pos)。最下位の分岐点が top と等しい場合、delete は内部呼びなしで終了する。この時点で pres 1 と pres 2 は yourmin(leaf, top) と yourmax(leaf, top) の値で初期化され、これら 2 つの値が等しい場合 nobranchpoint は true に設定される。
これらの値の更新は、終了した呼び出しがトップ呼び出しかボトム呼び出しか (delete の現在のバージョンでは認識されている) に応じて行われる。最後の呼び出しがトップ呼び出しだった場合、pres 1:= minof(pres 1); pres 2:= maxof(pres 2) となり、それらの等価性が再度テストされ、nobranchpoint を true のままにするかが決定される。true のままにする場合、以前に pres 1 が指していたノードは非アクティブ化される。
最後の呼び出しがボトム呼び出しだった場合、以前のトップでの ub フィールドと、このノードでの pres 1 から離れた位置へのポインタを調べて、このノードまたはその上位に分岐点が存在するかを判断する。分岐点が存在しない場合、以前のトップは非アクティブ化されます。
現在のトップのフィールドは、現在の呼び出しがボトム呼び出しの場合にのみ調整される。
delete の初期呼び出しは以下のようになる:
delete(pt, root, h, pres 1, pres 2, nobranchpoint);
ドライバは pt が present 葉であり、かつ唯一の present 葉ではなことを確認する。delete の完全なテキストを以下に示す。
procedure delete(leaf, top : ptr; order : ranktp;
var pres1,pres2: ptr; var nobranchpoint : boolean);
var fptr : fieldptr; hl, hp : ptr;
begin fptr:= myfields(leaf, top);
with fptr↑ do if min = max then
begin min:= nil; max:= nil;
pres1:= yourmin(leaf, top);
pres2:= yourmax(leaf, top);
nobranchpoint:= pres1 = pres2
end else
begin hl:= leaf↑.fathers[order - 1];
if minof(hl) = maxof(hl) then
begin delete(hl, top, order - 1,
pres1, pres2, nobranchpoint);
clear(hl); hp:= pres1;
if nobranchpoint then hp↑.ub:= minus;
pres1:= minof(pres1); pres2:= maxof(pres2);
if nobranchpoint then
if (pres1 = pres2) then clear(hp)
else nobranchpoint:= false
end else
begin delete(leaf, hl, order - 1,
pres1, pres2, nobranchpoint);
if nobranchpoint then
if (hl↑.ub = minus)
and (yourmin(pres1, hl) = nil)
then clear(hl)
else nobranchpoint:= false
end;
if top↑.rank = order then
if min = leaf then min:= pres1 else
if max = leaf then max:= pres2
end
end;
5.2.3. neighbour 手続き
関数 neighbour は値渡しで呼び出される 5 つ引数を持つ。これらの引数の意味は insert の引数とほぼ同じだが、、pres は pmin と pmax のペアに置き換えられる。
neighbour は present 葉と非 present 葉の両方に対して呼び出すことができる。これは、位置に関する高価なビット操作を行わない限り、近傍要素が与えられた引数の先行者か後続者かを判断できないという事実によって正当化される。
pmin と pmax は、対象とする CS の leaf 側における最左端と最右端の present 葉である。
neighbour は以下の場合に内部呼び出しなしに終了する:
pmin = nil;この場合、隣接要素は木の反対側にある。pmin = pmax = leaf;同様。leafがpmin - pmax区間の外にある; この場合、隣接要素は通常の意味で木の内部構造を調べることなく 2 つのうち最も近い方を返す。
ショートカット iii は手続き neighbour に固有である。これらの状況のいずれも発生しない場合、再帰呼び出しが実行される。この内部呼び出しは leaf と top の間の中心レベルのノード hl が present でない場合 (この状況では非アクティブと同等)、または leaf が hl の唯一の present 子孫である場合はトップ呼び出しとなる。それ以外の場合はボトム呼び出しが実行される。
neighbour の初期呼び出しは以下のように記述される。
neighbour(pt, root, mymin(pt, root), mymax(pt, root), h)
空の木、または唯一の present 葉に対して呼び出された場合、neighbour はその結果として nil を返す。ドライバはこれらの退化的なケースが処理されるように配慮する。
neighbour のテキストを以下に示す:
function neighbour(leaf, top, pmin, pmax : ptr;
order : ranktp) : ptr;
var y, z, nb, hl : ptr; pos : 1..n;
begin pos:= leaf↑.position;
if (pmin = nil)
or ((pmin = pmax) and (pmin = leaf)) then
if pos <= top↑.position
then neighbour:= yourmin(leaf, top)
else neighbour:= yourmax(leaf, top)
else if pmin↑.position > pos then neighbour:= pmin
else if pmax↑.position < pos then neighbour:= pmax
else
begin hl:= leaf↑.fathers[order - 1];
y:= minof(hl); z:= maxof(hl);
if ((y = z) and (y = leaf))
or (hl↑.ub = undefined) then
begin nb:= neighbour(hl, top,
pmin↑.fathers[order -1], pmax↑.fathers[order -1],
order - 1);
if hl↑.position < nb↑.position
then neighbour:= minof(nb)
else neighbour:= maxof(nb)
end
else neighbour:= neighbour(leaf, hl,
mymin(leaf, hl), mymax(leaf, hl), order - 1)
end
end;
5.2.4. 手続きに関するいくつかの中期
手続き
insert,delete,neighbourはすべてその内部呼び出しがボトム呼び出しであるという性質を持つ。この場合、完全な木も下位木であるとみなす。この観察は KAAS & ZIJLSTRA [8] によるものである。ランク
のノードでは、ランク の親ポインタは手続きによって検査されることはない。これは、それらの値が内包する再帰呼び出しのローカル変数のスタックに保持されているという事実に起因する。特に、 次の呼び出しでは対象とする CS 内に ( topを除く) すべてのノードの 次ランクの親がパラメータ topに渡される。これらのポインタに必要な空間を省略することで、必要な記憶領域を定数倍削減できる可能性がある。
6. 階層化木の応用
このセクションでは、オフサイズ優先度付きキュー (すなわち
6.1. オフサイズ優先度付きキュー
6.1.1. 下位部分木の除去
このアプローチでは、数
6.1.2. レベルの除去
6.2. 多数の優先度付きキューの表現
複数の優先度付きキューを表現しなければならない場合、ノード内の静的情報と動的情報を分離することが理にかなっている。静的情報は各キューについてほぼ同じである。より具体的には、ノードの位置をそのアドレスとして使用する "アドレス + 変異" 戦略を用いることで、位置が機知の各ノードにアクセスできる。すべてのノードは、下位からは親ポインタ、動的情報からは下向きポインタによってアクセスされるため、階層化ツリー内の静的情報の事前計算済みコピーを 1 つ用意するだけで十分である。アルゴリズムに関係する各キューには、ノードの位置から直接アクセスできる
上記の戦略を用いることで、導入部で約束された
直接的なアプローチはノードがアクティブ化された時点でそのノードに記憶域を割り当てることである。しかしこの方法は間違っているように思われる。次のような質問に正しく答えることができなければならない: "ここで渡しは根 top といくつかの葉の pres と leaef を持つ特定の CS を考えており、pres は present だが leaf は present ではない。hl を中心レベルにおける leaf の祖先とする。hl がアクティブかどうか、そしてもしアクティブであればどこに割り当てられるかを判断する。" pres の中心レベルの祖先の検査は、pres が実際に leaf の隣接要素である場合にのみ正しい答えをもたらす。しかし、これは我々のアルゴリズムでは保証されていない。
最初に leaf の隣接要素を計算しようとした場合にも同じ問題が発生する。したがって、対象とする CS における hl の位置が分かっており、その場所にはその根からのアクセスがあることで、hl を格納するための特定の位置を確保することが必要であると思われる。
以下のアプローチは insert の呼び出しは最大 neighbour は追加のメモリを使用せず、delete は包含する CS の両側が単一の葉を除いて使い尽くされている場合に上位木のための空間を返す可能性がある。
現在の関連する記憶セグメントの初期アドレスは、すべての包含する呼び出しがボトム呼び出しでないかぎり、その値が内部呼び出しに渡される手続きへの新しいパラメータとして与えられる。
マージ可能ヒープアルゴリズムで使用されるメモリの上限 insert 命令を実行して得られる値と等しいことに注目することで得られる。
このセクションを完了するにあたり、レベルの 2 進分数を
7. 集合捜査問題間の帰着性
優先度付きキューのオンライン操作 (オンライン insert-extract-min 問題とも呼ばれる) は、数多くの集合操作問題の一つである。これらの問題はそれぞれ、対応するオフライン変種も存在する。オフライン変種では命令の順序が事前に与えられ、それ絵に応じて返答の順序が生成されなければならず、プログラマは返答が与えられる順序を自由に選択できる。
明らかに、各オンラインアルゴリズムはオフライン変種を解くために使用できるが、その逆は成り立たない。
[3] では、insert-extract-min 問題、Union-Find 問題、insert-allmin 問題のオンライン変種とオフライン変種間の帰着性 (reducibility) を調査した。ここで、問題 A が問題 B に帰着可能であるとは、B のアルゴリズムを使用して同じ複雑さのオーダーを持つ A のアルゴリズムを設計できることを意味する。さらに、A と B が両方ともオフライン問題である場合、
HOPCROFT, AHO & ULLMAN はオフラインの insert-extract-min 問題がオンラインの Union-Find 問題に帰着可能であることを示した [2]。著者は、オフライン Union-Find 問題がオフライン insert-allmin 問題と等価であることを示した [3]。オンライン insert-allmin からオンライン insert-extract-min への "自然な" 帰着と合わせて、これらの帰着性が Diagram 4 に表現されている (接頭辞は議論されている問題を示している)。
ACKNOWLEDGEMENTS
I wish to thank J. Hopcroft for suggesting the problem and other useful suggestions. For the current state of the algorithms I am heavily indebted to R. Kaas and E. Zijlstra who, during the process of implementing the priority queue structure, discovered several hideous bugs in my earlier versions, and who contributed some nice and clever tricks; their use of a position field should be mentioned in particular. They also found the first recursive version of the delete procedure. Finally, T would like to thank R.E. Tarjan, Z. Galil and J. van Leeuwen for valuable ideas.
Author's addresses: Mathematical Institute, University of Amseterdam, Roetersstraat 15, Amsterdam; or Mathematical Centre, 2e Boerhaavestraat 49, Amsterdam
REFERENCES
- AHO, A.V., J.E. HOPCROFT & J.D. ULLMAN, The design and analysis of computer Algorithms, Addison Wesley, Reading, Mass. (1974).
- AHO, A.V., J.E. HOPCROFT & J.D. ULLMAN, On finding lowest common ancestors in trees, Proc. 5-th ACM symp. Theory of Computing (1973), 253-265.
- EMDE BOAS, P. VAN, An
On-Line Algorithm for the Insert-Extract Min Problem, Rep. TR 74-221 Dept. of Compo Sci., Cornell Univ., Ithaca 14853, N.Y. - EMDE BOAS, P. VAN, The On-Line Insert-Extract Min Problem, Rep. 75-04, Math. Institute, Univ. of Amsterdam.
- EVEN, S., M.R. GAREY & Y. PERL, Efficient Generation of Optimal Prefix Code: Equiprobable Words Using Unequal Cost Letters, J. Assoc. Comput. Mach. 22 (1975), 202-214.
- FISHER, M.J., Efficiency of equivalence algorithms, in: R.E. MILLER & J.W. THATCHER (eds.), Complexity of Computer Computations~ Plenum Press, New York (1972), 158-168.
- HOPCROFT, J. & J.D. ULLMAN, Set-merging Algorithms, SIAM J. Comput. 2 (Dec. 1973), 294-303.
- KAAS, R. & E. ZIJLSTRA, A PASCAL implementation of an efficient priority queue, Rep. Math. Institute, Univ. of Amsterdam (to appear).
- TARJAN, R.E., Efficiency of a good but non linea set union algorithm, J. Assoc. Comput. Mach. 22 (1975), 215-224.
- TARJAN, R.E., Edge disjoint spanning trees, dominators and depth first search, Rep. CS~74-455 (Sept. 1974), Stanford.
- WIRTH, N., The Programming Language PASCAL (revised report), in K. JENSEN & N. WIRTH PASCAL User Manual and Report, Lecture Notes in Computer Science 18, Springer, Berlin (1974).
翻訳抄
現在は van Emde Boas Tree として知られている、整数の優先度付きキューにおいて従来の
- VAN EMDE BOAS, Peter. Preserving order in a forest in less than logarithmic time. In: 16th Annual Symposium on Foundations of Computer Science (sfcs 1975). IEEE, 1975. p. 75-84.