B木の高さが低くなる場合
さて、こうやってどんどん要素を削除していくと、親から要素を削除する際に左側の部分木からも右側の部分木からも要素をもらってくることができない状態になります。
つまり、部分木内部の節点がすべてK個の要素しか保持していない状態です。
このような状態に陥ってしまうと、新たに1つでも要素を拠出するとツリーのバランスルールが守れません。そこで、左右の子節点をマージして新たなルートとし、木の高さが一段低くなります。これが、B木が低くなるたった1つの場合です。
このとき、左側部分木の一番右側の一列と、右側部分木の一番左側の一列で上から下までのマージが発生することになりますが、これらはすべてK個の要素しか保持していないことになるので問題なく実行できます。
B木への追加も削除もできるテストプログラム
前回作成したテストプログラムを改良して、要素の削除もできるようにしました(BTree_Test2.exe)。今回も、なるべくノード内部に実装を集中するようにしています。
複数の節点間でのデータのやりとりが発生する場合には、自分が節点になったつもりで考えると分かりやすいかもしれません。こうしたオブジェクト指向の視点で考えることがアルゴリズムを理解するうえでも役に立つことでしょう。
既定値では、次数が2のB木が生成されます。任意の次数のB木を作成したい場合は、画面右側のSpinEditから次数を指定して「K次のB木を生成」ボタンをクリックしてください。
また、「キーの削除」ボタンで指定したキーを削除できます。いろいろと試してB木の動きを確認して下さい。
001: unit BTree; 002: 003: interface 004: 005: uses 006: SysUtils, Classes, StrUtils, Dialogs; 007: 008: type 009: 010: TKeyVal = record 011: Key: Integer; 012: Val: Integer; 013: end; 014: 015: TBTreeNode = class(TObject) 016: private 017: FCount: Integer; 018: FMaxKeys: integer; 019: FKeys: array of Integer; 020: FVals: array of integer; 021: FChildNodes: array of TBTreeNode; 022: FOrder: integer; 023: FIsRoot: Boolean; 024: function GetChildNodes(Index: Integer): TBtreeNode; 025: function GetKeys(Index: Integer): Integer; 026: function GetVals(Index: Integer): Integer; 027: procedure InsertKeyVal(Index: Integer; const new_key, new_val: Integer); 028: procedure InsertChildNode(Index: Integer; const new_node: TBtreeNode); 029: procedure SetChildNodes(Index: Integer; const Value: TBtreeNode); 030: procedure SetKeys(Index: Integer; const Value: Integer); 031: procedure SetVals(Index: Integer; const Value: Integer); 032: function IsLeaf:Boolean; 033: procedure SetIsRoot(const Value: Boolean); 034: protected 035: procedure DumpNodes(var S : String; depth : Integer); 036: function InternalAdd(new_key, new_val:Integer; var new_node:TBtreeNode;var return_key, return_val:Integer):boolean; 037: function InternalDel(del_key:Integer; var new_root:TBTreeNode):boolean; 038: procedure GetRightMostinTree(var right_key, right_val : Integer); 039: procedure GetLeftMostInNode(var left_key, left_val : Integer; var return_node:TBTReeNode); 040: procedure GetRightMostInNode(var right_key, right_val : Integer; var return_node:TBTReeNode); 041: procedure SetCount(Value : Integer); 042: public 043: constructor Create(Order:Integer); 044: destructor Destroy;override; 045: property Order : Integer read FOrder; 046: property MaxKeys : Integer read FMaxKeys; 047: property Keys[Index:Integer]:Integer read GetKeys write SetKeys; 048: property Vals[Index:Integer]:Integer read GetVals write SetVals; 049: property ChildNodes[Index:Integer]:TBtreeNode read GetChildNodes write SetChildNodes; 050: property Count:Integer read FCount write SetCount; 051: property IsRoot:Boolean read FIsRoot write SetIsRoot; 052: end; 053: 054: TBtree = class(TObject) 055: private 056: FCount : Integer; 057: FOrder : Integer; 058: FRoot : TBTreeNode; 059: protected 060: public 061: function DumpNodes:String; 062: procedure Add(new_key, new_val:Integer); 063: procedure Del(del_key:Integer); 064: constructor Create(Order:Integer); 065: destructor Destroy;override; 066: property Order : Integer read FOrder; 067: property Count : Integer read FCount; 068: end; 069: 070: implementation 071: 072: { TBTreeNode } 073: 074: //節点のコンストラクタ 075: constructor TBTreeNode.Create(Order:Integer); 076: begin 077: inherited Create; 078: 079: FCount := 0; 080: FOrder := Order; 081: FMaxKeys := Order * 2; 082: FIsRoot := False; 083: 084: //実装を簡易にするため、0..2*K、つまり要素数2K+1の配列とする 085: SetLength(FKeys, FMaxKeys + 1); 086: SetLength(FVals, FMaxKeys + 1); 087: 088: //子節点へのリンクは2K+1個を使用するので、余分を1つとる 089: //こうしておくと、分割の際に、K+1個ずつ分配しやすい 090: SetLength(FChildNodes, FMaxKeys + 2); 091: 092: end; 093: 094: //節点のデストラクタ 095: //子節点があれば、それを解放する 096: destructor TBTreeNode.Destroy; 097: var 098: idx : Integer; 099: begin 100: //ルートが不要になった際に連鎖解放されないための仕掛け 101: if (IsRoot = False) then 102: begin 103: for idx := 0 to FCount do 104: begin 105: if (FChildNodes[idx] <> nil) then FChildNodes[idx].Free; 106: end; 107: end; 108: 109: inherited; 110: end; 111: 112: //節点の状態を返す処理 113: procedure TBTreeNode.DumpNodes(var S: String; depth: Integer); 114: var 115: idx : Integer; 116: begin 117: //節点が葉かどうかで処理を分ける 118: if (IsLeaf = True) then 119: //葉である場合 120: begin 121: for idx := 0 to FCount -1 do 122: begin 123: S := S + DupeString(' ', depth) + IntToStr(FKeys[idx]) + #13#10; 124: end; 125: end else 126: //葉でない場合 127: begin 128: for idx := 0 to FCount -1 do 129: begin 130: FChildNodes[idx].DumpNodes(S, depth + 1); 131: S := S + DupeString(' ', depth) + IntToStr(FKeys[idx]) + #13#10; 132: end; 133: FChildNodes[FCount].DumpNodes(S, depth + 1); 134: end; 135: end; 136: 137: function TBTreeNode.GetChildNodes(Index: Integer): TBtreeNode; 138: begin 139: result := FChildNodes[Index]; 140: end; 141: 142: function TBTreeNode.GetKeys(Index: Integer): Integer; 143: begin 144: result := FKeys[Index]; 145: end; 146: 147: //節点内の一番左端の値を返して削除する 148: procedure TBTreeNode.GetLeftMostInNode(var left_key, left_val: Integer; 149: var return_node: TBTReeNode); 150: var 151: idx : Integer; 152: begin 153: 154: //左端の値を戻り値に入れる 155: left_key := FKeys[0]; 156: left_val := FVals[0]; 157: return_node := FChildNodes[0]; 158: 159: //左へ詰める 160: for idx := 0 to FCount - 1 do 161: begin 162: FKeys[idx] := FKeys[idx+1]; 163: FVals[idx] := FVals[idx+1]; 164: end; 165: for idx := 0 to FCount do 166: FChildNodes[idx] := FChildNodes[idx+1]; 167: 168: //クリーンアップ 169: FChildNodes[FCount+1] := nil; 170: Dec(FCount); 171: end; 172: 173: //節点内の一番右端の値を返して削除する 174: procedure TBTreeNode.GetRightMostInNode(var right_key, right_val: Integer; 175: var return_node: TBTReeNode); 176: begin 177: 178: //右端の値を戻り値に入れる 179: right_key := FKeys[FCount - 1]; 180: right_val := FVals[FCount - 1]; 181: return_node := FChildNodes[FCount]; 182: 183: //クリーンアップ 184: FChildNodes[FCount] := nil; 185: Dec(FCount); 186: end; 187: 188: //部分木の中の最右端を返す 189: procedure TBTreeNode.GetRightMostinTree(var right_key, right_val: Integer); 190: begin 191: if (FChildNodes[FCount] <> nil) then 192: begin 193: FChildNodes[FCount].GetRightMostinTree(right_key, right_val); 194: end else 195: begin 196: right_key := FKeys[FCount - 1]; 197: right_val := FVals[FCount - 1]; 198: //ここではキーを削除しない 199: end; 200: end; 201: 202: function TBTreeNode.GetVals(Index: Integer): Integer; 203: begin 204: result := FVals[Index]; 205: end; 206: 207: //節点への要素の追加 208: function TBTreeNode.InternalAdd(new_key, new_val:Integer; var new_node:TBtreeNode;var return_key, return_val:Integer):boolean; 209: var 210: idx : Integer; 211: begin 212: //新しいキーがバケット内のどの位置にあたるかをチェックしておく 213: //idxには、new_key の位置が入る 214: if (FCount = 0) then 215: idx := 0 216: else 217: for idx := 0 to FCount - 1 do 218: if (FKeys[idx] > new_key) then break; 219: 220: //葉の場合とそうでない場合で処理を分ける 221: if (IsLeaf = True) then 222: //節点が葉である場合 223: begin 224: //新しいキーより大きい値の左側へ新しいキーを挿入する 225: //余分を1つ取ってあるので必ず挿入できる 226: InsertKeyVal(idx, new_key, new_val); 227: 228: //すでにバケットがいっぱいかどうかで処理を分ける 229: if (FCount > FMaxkeys) then 230: begin 231: //バケットの分割が発生する 232: result := True; 233: 234: //分割用の新しい節点を生成 235: new_node := TBTreeNode.Create(FOrder); 236: 237: //新節点へ値を移動する 238: for idx := 0 to FOrder - 1 do 239: begin 240: //中央値はK番目にあたるので、K+1番目から上を新節点へ移動 241: //(K-1)+(K+1)=2K 242: new_node.Keys[idx] := FKeys[idx + FOrder + 1]; 243: new_node.Vals[idx] := FVals[idx + FOrder + 1]; 244: end; 245: 246: //親節点へ返すキーと値の組をセット 247: return_key := FKeys[FOrder]; 248: return_val := FVals[FOrder]; 249: 250: //分割によって、CountはKになる 251: FCount := FOrder; 252: new_node.SetCount(FOrder); 253: 254: exit; 255: end else 256: begin 257: //分割は発生していない 258: result := False; 259: exit; 260: end; 261: end else 262: //節点が葉で無い場合の処理 263: begin 264: //新しいキーより大きいキーの左側の子節点へキーを追加する 265: //idxには、キーの位置が入っているので、同じ位置のFChildNodesがそれにあたる 266: 267: //追加した結果分割が発生したかどうかで処理を分ける 268: if (FChildNodes[idx].InternalAdd(new_key, new_val, new_node, return_key, return_val) = True) then 269: //分割が発生した 270: begin 271: //分割の結果返されたキーを挿入するのも、idxの位置になるので 272: //これを再度チェックする必要はない 273: 274: //新しいキーより大きい値の左側へ新しいキーを挿入する 275: //余分を1つ取ってあるので必ず挿入できる 276: InsertKeyVal(idx, return_key, return_val); 277: 278: //新しい子節点を追加する 279: //位置としては、右側の子節点となるので、idx+1の位置へ挿入する 280: InsertChildNode(idx + 1, new_node); 281: 282: //すでにバケットがいっぱいかどうかで処理を分ける 283: if (FCount > FMaxkeys) then 284: begin 285: //バケットの分割が発生する 286: result := True; 287: 288: //分割用の新しい節点を生成 289: new_node := TBTreeNode.Create(FOrder); 290: 291: //新節点へ値を移動する 292: for idx := 0 to FOrder - 1 do 293: begin 294: //中央値はK番目にあたるので、K+1番目から上を新節点へ移動 295: //0+K+1=K+1 〜 (K-1)+(K+1)=2K を移動する 296: new_node.Keys[idx] := FKeys[idx + FOrder + 1]; 297: new_node.Vals[idx] := FVals[idx + FOrder + 1]; 298: 299: //子節点へのリンクも同様に移動し、移動元をnilで埋める 300: new_node.ChildNodes[idx] := FChildNodes[idx + FOrder + 1]; 301: FChildNodes[idx + FOrder + 1] := nil; 302: end; 303: 304: //子節点へのリンクは、一番右側がはみ出すのでこれを移動する 305: new_node.ChildNodes[FOrder] := FChildNodes[FMaxKeys + 1]; 306: FChildNodes[FMaxKeys + 1] := nil; 307: 308: //親節点へ返すキーと値の組をセット 309: return_key := FKeys[FOrder]; 310: return_val := FVals[FOrder]; 311: 312: //分割によって、CountはKになる 313: FCount := FOrder; 314: new_node.SetCount(FOrder); 315: 316: exit; 317: 318: end else 319: //分割は発生していない 320: begin 321: result := False; 322: exit; 323: end; 324: end else 325: //分割が発生していない 326: begin 327: result := False; 328: exit; 329: end; 330: end; 331: end; 332: 333: //節点からの要素の削除 334: function TBTreeNode.InternalDel(del_key: Integer; var new_root:TBTreeNode): boolean; 335: var 336: idx, Match : Integer; 337: IsMatch, IsShort : Boolean; 338: key, val : Integer; 339: new_node, return_node : TBTreeNode; 340: return_key, return_val : Integer; 341: begin 342: result := False; 343: 344: //削除するキーがバケット内のどの位置にあたるかをチェックしておく 345: //マッチするキーがあればフラグを立てる 346: //idxには、マッチした位置または子節点へ下がる位置が入る 347: IsMatch := False; 348: Match := FCount; 349: for idx := 0 to FCount - 1 do 350: begin 351: if (FKeys[idx] = del_key) then 352: begin 353: IsMatch := True; 354: Match := idx; 355: break; 356: end; 357: if (FKeys[idx] > del_key) then 358: begin 359: IsMatch := False; 360: Match := idx; 361: break; 362: end; 363: end; 364: //削除キーがバケット内の要素のどれよりも大きい場合 365: //マッチせずに、初期値のままとなる 366: 367: 368: //葉の場合とそうでない場合で処理を分ける 369: if (IsLeaf = True) then 370: //節点が葉である場合 371: begin 372: //マッチするキーがあるので、それを削除する 373: if (IsMatch = True) then 374: begin 375: //該当するキーを削除して左へ寄せる 376: //マッチを無視して、マッチから右を左へ寄せれば良い 377: for idx := Match to FCount - 1 do 378: begin 379: FKeys[idx] := FKeys[idx + 1]; 380: FVals[idx] := FVals[idx + 1]; 381: end; 382: FKeys[FCount] := 0; 383: FVals[FCount] := 0; 384: Dec(FCount); 385: 386: //削除によって節点の要素がK個を割った場合 387: //親の節点に要素を1つ要求する 388: if (FCount < FOrder) then result := True; 389: 390: end else 391: //マッチするキーがないのでエラーを返す 392: begin 393: Exception.CreateFmt('Not Find %d in the tree.', [del_key]); 394: end; 395: end else 396: //節点が葉でない場合の処理 397: begin 398: if (IsMatch = True) then 399: begin 400: //該当するキーを削除して、左部分木の右端で置き換える 401: //結果がTrueの場合には、 402: FChildNodes[Match].GetRightMostInTree(key, val); 403: FKeys[Match] := key; 404: FVals[Match] := val; 405: 406: //もらったキーを左部分木から削除する 407: //複数同じキーがあるのであれば、どれが削除されても結果は変わらない 408: IsShort := FChildNodes[Match].InternalDel(key, new_root); 409: 410: end else 411: //マッチするキーが無いので、子の節点へ削除キーを渡す 412: begin 413: //削除の結果、バケットの要素が不足しているかどうかがIsShortに入る 414: IsShort := FChildNodes[Match].InternalDel(del_key, new_root); 415: 416: end; 417: 418: //子節点での削除の結果、要素が足りないといわれたので 419: //右側の節点の要素をもらいに行く 420: //MatchがCount-1である場合は左側からもらう、それ以外は右側から 421: if (IsShort = True) then 422: begin 423: //MatchがCountである場合は左側を残し、それ以外は右側を残す 424: //つまり右端を例外視する 425: if (Match < FCount) then 426: //この場合は、右側を残す 427: begin 428: //右側の子節点の要素がぎりぎりの場合はマージする 429: if (FChildNodes[Match+1].Count = FOrder) then 430: begin 431: //右側の子節点の要素を右寄せする 432: for idx := FOrder - 1 downto 0 do 433: begin 434: FChildNodes[Match+1].Keys[idx+FOrder] := FChildNodes[Match+1].Keys[idx]; 435: FChildNodes[Match+1].Vals[idx+FOrder] := FChildNodes[Match+1].Vals[idx]; 436: end; 437: for idx := FOrder downto 0 do 438: FChildNodes[Match+1].ChildNodes[idx+FOrder] := FChildNodes[Match+1].ChildNodes[idx]; 439: 440: //左側の子節点の残りの要素を右側の子節点へ移動 441: for idx := 0 to FChildNodes[Match].Count - 1 do 442: begin 443: FChildNodes[Match+1].Keys[idx] := FChildNodes[Match].Keys[idx]; 444: FChildNodes[Match+1].Vals[idx] := FChildNodes[Match].Vals[idx]; 445: end; 446: for idx := 0 to FChildNodes[Match].Count do 447: FChildNodes[Match+1].ChildNodes[idx] := FChildNodes[Match].ChildNodes[idx]; 448: 449: //マッチしているキーを右側の子節点の空いているFOrder-1へ移す 450: FChildNodes[Match+1].Keys[FOrder-1] := FKeys[Match]; 451: FChildNodes[Match+1].Vals[FOrder-1] := FVals[Match]; 452: 453: //最終的に右側の子節点は満杯となる 454: FChildNodes[Match+1].Count := FMaxKeys; 455: 456: //自節点内で譲渡した要素の分を左に詰める 457: for idx := Match to FCount - 1 do 458: begin 459: FKeys[idx] := FKeys[idx+1]; 460: FVals[idx] := FVals[idx+1]; 461: end; 462: for idx := Match to FCount do 463: FChildNodes[idx] := FChildNodes[idx+1]; 464: 465: //要素が1つ減る 466: Dec(FCount); 467: 468: //結果としてバランス条件が崩れた場合、親へ波及する 469: if (FCount < FOrder) then result := True; 470: 471: //ルートの場合はどんどん要素が減ってなくなる場合がある 472: if (FIsRoot = True) and (FCount = 0) then 473: begin 474: new_root := FChildNodes[0]; 475: new_root.Count := FMaxKeys; 476: new_root.IsRoot := True; 477: end; 478: end else 479: //右側の子節点から要素をもらえる場合 480: //MatchがCountより小さい場合は右側からもらう 481: begin 482: //右側の子節点の左端の要素をもらう 483: FChildNodes[Match+1].GetLeftMostInNode(key, val, return_node); 484: 485: //左側の子節点の右端へ自分のマッチした要素を追加する 486: FChildNodes[Match].Keys[FChildNodes[Match].count] := FKeys[Match]; 487: FChildNodes[Match].Vals[FChildNodes[Match].count] := FVals[Match]; 488: FChildNodes[Match].ChildNodes[FChildNodes[Match].count + 1] := return_node; 489: FChildNodes[Match].Count := FChildNodes[Match].Count + 1; 490: 491: //自分のマッチした要素を右側の子節点から返った値で置き換える 492: FKeys[Match] := key; 493: FVals[Match] := val; 494: 495: end; 496: end else 497: //Match=Countの場合 498: begin 499: //左側の子節点の要素がぎりぎりの場合はマージする 500: if (FChildNodes[Match-1].Count = FOrder) then 501: begin 502: 503: //右側の子節点の残りの要素を左側の子節点へ移動 504: for idx := 0 to FChildNodes[Match].Count - 1 do 505: begin 506: FChildNodes[Match-1].Keys[FOrder+idx+1] := FChildNodes[Match].Keys[idx]; 507: FChildNodes[Match-1].Vals[FOrder+idx+1] := FChildNodes[Match].Vals[idx]; 508: end; 509: for idx := 0 to FChildNodes[Match].Count do 510: FChildNodes[Match-1].ChildNodes[FOrder+idx+1] := FChildNodes[Match].ChildNodes[idx]; 511: 512: //マッチしているキーを左側の子節点の右端へ移す 513: FChildNodes[Match-1].Keys[FOrder] := FKeys[Match-1]; 514: FChildNodes[Match-1].Vals[FOrder] := FVals[Match-1]; 515: 516: //最終的に右側の子節点は満杯となる 517: FChildNodes[Match-1].Count := FMaxKeys; 518: 519: //自節点内で譲渡した要素の分を左に詰める 520: for idx := Match to FCount - 1 do 521: begin 522: FKeys[idx] := FKeys[idx+1]; 523: FVals[idx] := FVals[idx+1]; 524: end; 525: for idx := Match to FCount do 526: FChildNodes[idx] := FChildNodes[idx+1]; 527: 528: //要素が1つ減る 529: Dec(FCount); 530: 531: //結果としてバランス条件が崩れた場合、親へ波及する 532: if (FCount < FOrder) then result := True; 533: 534: //ルートの場合はどんどん要素が減ってなくなる場合がある 535: if (FIsRoot = True) and (FCount = 0) then 536: begin 537: new_root := FChildNodes[Match-1]; 538: new_root.Count := FMaxKeys; 539: new_root.IsRoot := True; 540: end; 541: end else 542: //左側の子節点から要素をもらえる場合 543: //MatchがCountである場合は左側からもらう 544: begin 545: //左側の子節点の右端の要素をもらう 546: FChildNodes[Match-1].GetRightMostInNode(key, val, return_node); 547: 548: //右側の子節点の0位置へ自分のマッチした要素を追加する 549: for idx := FChildNodes[Match].Count - 1 downto 0 do 550: begin 551: FChildNodes[Match].Keys[idx+1]:=FChildNodes[Match].Keys[idx]; 552: FChildNodes[Match].Vals[idx+1]:=FChildNodes[Match].Vals[idx]; 553: end; 554: for idx := FChildNodes[Match].Count downto 0 do 555: FChildNodes[Match].ChildNodes[idx+1]:=FChildNodes[Match].ChildNodes[idx]; 556: 557: FChildNodes[Match].Keys[0] := FKeys[Match-1]; 558: FChildNodes[Match].Vals[0] := FVals[Match-1]; 559: FChildNodes[Match].ChildNodes[0] := return_node; 560: FChildNodes[Match].Count := FChildNodes[Match].Count + 1; 561: 562: //自分のマッチした要素を右側の子節点から返った値で置き換える 563: FKeys[Match-1] := key; 564: FVals[Match-1] := val; 565: 566: end; 567: end; 568: end; 569: end; 570: end; 571: 572: function TBTreeNode.IsLeaf: Boolean; 573: begin 574: result := (FChildNodes[0] = nil); 575: end; 576: 577: procedure TBTreeNode.SetChildNodes(Index: Integer; const Value: TBtreeNode); 578: begin 579: FChildNodes[index] := Value; 580: end; 581: 582: procedure TBTreeNode.SetCount(Value: Integer); 583: begin 584: FCount := Value; 585: end; 586: 587: procedure TBTreeNode.SetIsRoot(const Value: Boolean); 588: begin 589: FIsRoot := Value; 590: end; 591: 592: procedure TBTreeNode.SetKeys(Index: Integer; const Value: Integer); 593: begin 594: FKeys[Index] := Value; 595: end; 596: 597: //Indexを指定した位置に、ChildNodeを挿入する 598: procedure TBTreeNode.InsertChildNode(Index: Integer; 599: const new_node: TBtreeNode); 600: var 601: idx : Integer; 602: begin 603: //追加するキーのために場所を確保する 604: for idx := FCount + 1 downto index + 1 do 605: begin 606: FChildNodes[idx] := FChildNodes[idx - 1]; 607: end; 608: 609: FChildNodes[index] := new_node; 610: 611: 612: end; 613: 614: //Indexを指定した位置に、キー=Valueを挿入する 615: procedure TBTreeNode.InsertKeyVal(Index: Integer; const new_key, new_val: Integer); 616: var 617: idx : Integer; 618: begin 619: 620: //追加するキーのために場所を確保する 621: for idx := FCount downto index + 1 do 622: begin 623: FKeys[idx] := FKeys[idx - 1]; 624: FVals[idx] := FVals[idx - 1]; 625: end; 626: 627: FKeys[index] := new_key; 628: FVals[index] := new_val; 629: 630: Inc(FCount); 631: end; 632: 633: procedure TBTreeNode.SetVals(Index: Integer; const Value: Integer); 634: begin 635: FVals[Index] := Value; 636: end; 637: 638: { TBtree } 639: 640: procedure TBtree.Add(new_key, new_val: Integer); 641: var 642: created_node, old_root : TBTreeNode; 643: return_key, return_val : Integer; 644: begin 645: //ルートに対して新しいキーを追加した結果、ルートが分割されるかどうかで処理を分ける 646: if(FRoot.InternalAdd(new_key, new_val, created_node, return_key, return_val)=True) then 647: begin 648: old_root := FRoot; 649: old_root.IsRoot := False; 650: 651: FRoot := TBTreeNode.Create(FOrder); 652: FRoot.IsRoot := True; 653: 654: FRoot.Keys[0] := return_key; 655: FRoot.Vals[0] := return_val; 656: FRoot.ChildNodes[0] := old_root; 657: FRoot.ChildNodes[1] := created_node; 658: FRoot.Count := 1; 659: end; 660: end; 661: 662: //B木のコンストラクタ 663: constructor TBtree.Create(Order: Integer); 664: begin 665: inherited Create; 666: 667: FOrder := Order; 668: FRoot := TBtreeNode.Create(Order); 669: FRoot.IsRoot := True; 670: end; 671: 672: //ツリーから要素を削除する 673: procedure TBtree.Del(del_key: Integer); 674: var 675: idx, Match : Integer; 676: new_root : TBTreeNode; 677: begin 678: //ルートに対してキーの削除を指示 679: //FOrderを割るだけなら捨て置くが、要素が0になった場合 680: //子節点をマージして新しいルートにする 681: new_root := nil; 682: if(FRoot.InternalDel(del_key, new_root) = True) then 683: begin 684: if (new_root <> nil) then 685: begin 686: FRoot.Free; 687: FRoot := new_root; 688: end; 689: end; 690: end; 691: 692: //B木のデストラクタ 693: destructor TBtree.Destroy; 694: begin 695: //ルートが不要になった際に連鎖解放されないための仕掛け 696: FRoot.IsRoot := False; 697: FRoot.Free; 698: 699: inherited; 700: end; 701: 702: //ツリーの内部状態を返す 703: function TBtree.DumpNodes: String; 704: begin 705: FRoot.DumpNodes(Result, 0); 706: end; 707: 708: end.
さて、今回で「Delphiアルゴリズムトレーニング」は最終回となります。リンクリスト、キュー、AVL木やB木を解説しながら、Delphiでの実装例を通してアルゴリズムをプログラムで実現する際の手法を見てきました。
ぜひ皆さんも、さまざまなアルゴリズムの実装にチャレンジしてみてください。筆者のつたない知識でどこまでアルゴリズムの世界をお伝えできたか心配ですが、多少なりとも皆さまのお役に立てたのであれば幸いです。それでは、またの機会にお会いしましょう。ご愛読ありがとうございました。
2/2 |
Index | |
B木から要素を削除する方法を学ぼう | |
Page1 B木からの要素の削除(葉の場合) B木からの要素の削除(葉でない場合) |
|
Page2 B木の高さが低くなる場合 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の世界を体験してみよう |
|
- プログラムの実行はどのようにして行われるのか、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を紹介する。※ショートカットキー、アクセスキーの解説あり
|
|