BTree.pasのソースコード
●BTreeのソースコード(BTree.pas)
unit BTree; interface uses SysUtils, Classes, StrUtils, Dialogs; type TKeyVal = record Key: Integer; Val: Integer; end; TBTreeNode = class(TObject) private FCount: Integer; FMaxKeys: integer; FKeys: array of Integer; FVals: array of integer; FChildNodes: array of TBTreeNode; FOrder: integer; function GetChildNodes(Index: Integer): TBtreeNode; function GetKeys(Index: Integer): Integer; function GetVals(Index: Integer): Integer; procedure InsertKeyVal(Index: Integer; const new_key, new_val: Integer); procedure InsertChildNode(Index: Integer; const new_node: TBtreeNode); procedure SetChildNodes(Index: Integer; const Value: TBtreeNode); procedure SetKeys(Index: Integer; const Value: Integer); procedure SetVals(Index: Integer; const Value: Integer); function IsLeaf:Boolean; protected procedure DumpNodes(var S : String; depth : Integer); function InternalAdd(new_key, new_val:Integer; var new_node:TBtreeNode;var return_key, return_val:Integer):boolean; procedure SetCount(Value : Integer); public constructor Create(Order:Integer); destructor Destroy;override; property Order : Integer read FOrder; property MaxKeys : Integer read FMaxKeys; property Keys[Index:Integer]:Integer read GetKeys write SetKeys; property Vals[Index:Integer]:Integer read GetVals write SetVals; property ChildNodes[Index:Integer]:TBtreeNode read GetChildNodes write SetChildNodes; property Count:Integer read FCount; end; TBtree = class(TObject) private FCount : Integer; FOrder : Integer; FRoot : TBTreeNode; protected public function DumpNodes:String; procedure Add(new_key, new_val:Integer); constructor Create(Order:Integer); destructor Destroy;override; property Order : Integer read FOrder; property Count : Integer read FCount; end; implementation { TBTreeNode } //節点のコンストラクタ constructor TBTreeNode.Create(Order:Integer); begin inherited Create; FCount := 0; FOrder := Order; FMaxKeys := Order * 2; //実装を簡易にするため、0..2*K、つまり要素数2K+1の配列とする SetLength(FKeys, FMaxKeys + 1); SetLength(FVals, FMaxKeys + 1); //子節点へのリンクは2K+1個を使用するので、余分を1つとる //こうしておくと、分割の際に、K+1個ずつ分配しやすい SetLength(FChildNodes, FMaxKeys + 2); end; //節点のデストラクタ //子節点があれば、それを解放する destructor TBTreeNode.Destroy; var idx : Integer; begin for idx := 0 to FCount do begin if (FChildNodes[idx] <> nil) then FChildNodes[idx].Free; end; inherited; end; //節点の状態を返す処理 procedure TBTreeNode.DumpNodes(var S: String; depth: Integer); var idx : Integer; begin //節点が葉かどうかで処理を分ける if (IsLeaf = True) then //葉である場合 begin for idx := 0 to FCount -1 do begin S := S + DupeString(' ', depth) + IntToStr(FKeys[idx]) + #13#10; end; end else //葉でない場合 begin for idx := 0 to FCount -1 do begin FChildNodes[idx].DumpNodes(S, depth + 1); S := S + DupeString(' ', depth) + IntToStr(FKeys[idx]) + #13#10; end; FChildNodes[FCount].DumpNodes(S, depth + 1); end; end; function TBTreeNode.GetChildNodes(Index: Integer): TBtreeNode; begin result := FChildNodes[Index]; end; function TBTreeNode.GetKeys(Index: Integer): Integer; begin result := FKeys[Index]; end; function TBTreeNode.GetVals(Index: Integer): Integer; begin result := FVals[Index]; end; //節点への要素の追加 function TBTreeNode.InternalAdd(new_key, new_val:Integer; var new_node:TBtreeNode;var return_key, return_val:Integer):boolean; var idx : Integer; begin //新しいキーがバケット内のどの位置にあたるかをチェックしておく //idxには、new_key の位置が入る if (FCount = 0) then idx := 0 else for idx := 0 to FCount - 1 do if (FKeys[idx] > new_key) then break; //葉の場合とそうでない場合で処理を分ける if (IsLeaf = True) then //節点が葉である場合 begin //新しいキーより大きい値の左側へ新しいキーを挿入する //余分を1つ取ってあるので必ず挿入できる InsertKeyVal(idx, new_key, new_val); //すでにバケットがいっぱいかどうかで処理を分ける if (FCount > FMaxkeys) then begin //バケットの分割が発生する result := True; //分割用の新しい節点を生成 new_node := TBTreeNode.Create(FOrder); //新節点へ値を移動する for idx := 0 to FOrder - 1 do begin //中央値はK番目にあたるので、K+1番目から上を新節点へ移動 //(K-1)+(K+1)=2K new_node.Keys[idx] := FKeys[idx + FOrder + 1]; new_node.Vals[idx] := FVals[idx + FOrder + 1]; end; //親節点へ返すキーと値の組をセット return_key := FKeys[FOrder]; return_val := FVals[FOrder]; //分割によって、CountはKになる FCount := FOrder; new_node.SetCount(FOrder); exit; end else begin //分割は発生していない result := False; exit; end; //節点が葉でない場合の処理 end else begin //新しいキーより大きいキーの左側の子節点へキーを追加する //idxには、キーの位置が入っているので、同じ位置のFChildNodesがそれにあたる //追加した結果分割が発生したかどうかで処理を分ける if (FChildNodes[idx].InternalAdd(new_key, new_val, new_node, return_key, return_val) = True) then //分割が発生した begin //分割の結果返されたキーを挿入するのも、idxの位置になるので //これを再度チェックする必要はない //新しいキーより大きい値の左側へ新しいキーを挿入する //余分を1つ取ってあるので必ず挿入できる InsertKeyVal(idx, return_key, return_val); //新しい子節点を追加する //位置としては、右側の子節点となるので、idx+1の位置へ挿入する InsertChildNode(idx + 1, new_node); //すでにバケットがいっぱいかどうかで処理を分ける if (FCount > FMaxkeys) then begin //バケットの分割が発生する result := True; //分割用の新しい節点を生成 new_node := TBTreeNode.Create(FOrder); //新節点へ値を移動する for idx := 0 to FOrder - 1 do begin //中央値はK番目にあたるので、K+1番目から上を新節点へ移動 //0+K+1=K+1 〜 (K-1)+(K+1)=2K を移動する new_node.Keys[idx] := FKeys[idx + FOrder + 1]; new_node.Vals[idx] := FVals[idx + FOrder + 1]; //子節点へのリンクも同様に移動し、移動元をnilで埋める new_node.ChildNodes[idx] := FChildNodes[idx + FOrder + 1]; FChildNodes[idx + FOrder + 1] := nil; end; //子節点へのリンクは、一番右側がはみ出すのでこれを移動する new_node.ChildNodes[FOrder] := FChildNodes[FMaxKeys + 1]; FChildNodes[FMaxKeys + 1] := nil; //親節点へ返すキーと値の組をセット return_key := FKeys[FOrder]; return_val := FVals[FOrder]; //分割によって、CountはKになる FCount := FOrder; new_node.SetCount(FOrder); exit; end else //分割は発生していない begin result := False; exit; end; end else //分割が発生していない begin result := False; exit; end; end; end; function TBTreeNode.IsLeaf: Boolean; begin result := (FChildNodes[0] = nil); end; procedure TBTreeNode.SetChildNodes(Index: Integer; const Value: TBtreeNode); begin FChildNodes[index] := Value; end; procedure TBTreeNode.SetCount(Value: Integer); begin FCount := Value; end; procedure TBTreeNode.SetKeys(Index: Integer; const Value: Integer); begin FKeys[Index] := Value; end; //Indexを指定した位置に、ChildNodeを挿入する procedure TBTreeNode.InsertChildNode(Index: Integer; const new_node: TBtreeNode); var idx : Integer; begin //追加するキーのために場所を確保する for idx := FCount + 1 downto index + 1 do begin FChildNodes[idx] := FChildNodes[idx - 1]; end; FChildNodes[index] := new_node; end; //Indexを指定した位置に、キー=Valueを挿入する procedure TBTreeNode.InsertKeyVal(Index: Integer; const new_key, new_val: Integer); var idx : Integer; begin //追加するキーのために場所を確保する for idx := FCount downto index + 1 do begin FKeys[idx] := FKeys[idx - 1]; FVals[idx] := FVals[idx - 1]; end; FKeys[index] := new_key; FVals[index] := new_val; Inc(FCount); end; procedure TBTreeNode.SetVals(Index: Integer; const Value: Integer); begin FVals[Index] := Value; end; { TBtree } procedure TBtree.Add(new_key, new_val: Integer); var created_node, old_root : TBTreeNode; return_key, return_val : Integer; begin //ルートに対して新しいキーを追加した結果、ルートが分割されるかどうかで処理を分ける if(FRoot.InternalAdd(new_key, new_val, created_node, return_key, return_val)=True) then begin old_root := FRoot; FRoot := TBTreeNode.Create(FOrder); FRoot.Keys[0] := return_key; FRoot.Vals[0] := return_val; FRoot.ChildNodes[0] := old_root; FRoot.ChildNodes[1] := created_node; FRoot.SetCount(1); end; end; //B木のコンストラクタ constructor TBtree.Create(Order: Integer); begin inherited Create; FOrder := Order; FRoot := TBtreeNode.Create(Order); end; //B木のデストラクタ destructor TBtree.Destroy; begin FRoot.Free; inherited; end; //ツリーの内部状態を返す function TBtree.DumpNodes: String; begin FRoot.DumpNodes(Result, 0); end; end.
Index | |
RDBMSで使われるB木を学ぼう | |
Page1 B木とは何か B木の成長 |
|
Page2 B木への要素の追加(葉の場合) B木への要素の追加(葉でない場合) B木の実装の工夫 |
|
Page3 B木のテストプログラム |
|
Appendix BTree.pasのソースコード |
Delphiアルゴリズムトレーニング |
Coding Edgeお勧め記事 |
いまさらアルゴリズムを学ぶ意味 コーディングに役立つ! アルゴリズムの基本(1) コンピュータに「3の倍数と3の付く数字」を判断させるにはどうしたらいいか。発想力を鍛えよう |
|
Zope 3の魅力に迫る Zope 3とは何ぞや?(1) Pythonで書かれたWebアプリケーションフレームワーク「Zope 3」。ほかのソフトウェアとは一体何が違っているのか? |
|
貧弱環境プログラミングのススメ 柴田 淳のコーディング天国 高性能なIT機器に囲まれた環境でコンピュータの動作原理に触れることは可能だろうか。貧弱なPC上にビットマップの直線をどうやって引く? |
|
Haskellプログラミングの楽しみ方 のんびりHaskell(1) 関数型言語に分類されるHaskell。C言語などの手続き型言語とまったく異なるプログラミングの世界に踏み出してみよう |
|
ちょっと変わったLisp入門 Gaucheでメタプログラミング(1) Lispの一種であるScheme。いくつかある処理系の中でも気軽にスクリプトを書けるGaucheでLispの世界を体験してみよう |
|
Coding Edge フォーラム 新着記事
- プログラムの実行はどのようにして行われるのか、Linuxカーネルのコードから探る (2017/7/20)
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。最終回は、Linuxカーネルの中では、プログラムの起動時にはどのような処理が行われているのかを探る - エンジニアならC言語プログラムの終わりに呼び出されるexit()の中身分かってますよね? (2017/7/13)
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。今回は、プログラムの終わりに呼び出されるexit()の中身を探る - VBAにおけるFileDialog操作の基本&ドライブの空き容量、ファイルのサイズやタイムスタンプの取得方法 (2017/7/10)
指定したドライブの空き容量、ファイルのタイムスタンプや属性を取得する方法、FileDialog/エクスプローラー操作の基本を紹介します - さらば残業! 面倒くさいエクセル業務を楽にする「Excel VBA」とは (2017/7/6)
日頃発生する“面倒くさい業務”。簡単なプログラミングで効率化できる可能性がある。本稿では、業務で使うことが多い「Microsoft Excel」で使えるVBAを紹介する。※ショートカットキー、アクセスキーの解説あり
|
|
>
Coding Edge 記事ランキング
本日
月間