コンテンツにスキップ

赤黒木

出典: フリー百科事典『ウィキペディア(Wikipedia)』
赤黒木
種類 木構造
発表時期 1972
発明者 en:Rudolf Bayer
ビッグオー記法による計算量 (en
アルゴリズム 平均 最悪の場合
空間
探索 [1] [1]
挿入 償却された [2] [1]
削除 償却された [2] [1]

2

1972 (en:Rudolf Bayer) "symmetric binary B-trees" 1978 (en:Leonidas J. Guibas)  (en:Robert Sedgewick) 

O(log n)n

wikipedia(Red-black_tree)

用語[編集]

赤黒木の例
図1: 明示的な葉(NIL)を持つ赤黒木
図2: 左右の暗黙のドッキングポイントを持つ赤黒木

:1222

1NIL 









5[3]:154165 NIL020

[]


AVLAVL

 

2-3-42-3-42-3-42-3-42-3-42-3-4

[]


[4]

(一)

(二) ()

(三) (NIL) 

(四)2

(五)







425

 ()nil leafnull leaf2

2

[]


 Linux CFS Completely Fair Scheduler使

AVL AVLAVL

AVL

 ()使 

[]


使O    [5]:310 [3]:15832

C++使
// 基本の型定義:

enum color_t { BLACK, RED };

struct RBnode {     // 赤黒木のノード
  RBnode* parent;   // == NULL (木のルートの時)
  RBnode* child[2]; // == NIL (子が空の時)
    // Index is:
    //   LEFT  := 0, if (key < parent->key)
    //   RIGHT := 1, if (key > parent->key)
  enum color_t color;
  int key;
};

#define NIL   NULL // ヌルポインタまたは番兵ノードへのポインタ
#define LEFT  0
#define RIGHT 1
#define left  child[LEFT]
#define right child[RIGHT]

struct RBtree { // 赤黒木
  RBnode* root; // == NIL (木が空の時)
};

// ルートでない非NILの RBnode* N の子方向(∈ { LEFT, RIGHT })を取得する
#define childDir(N) ( N == (N->parent)->right ? RIGHT : LEFT )
Animation of tree rotations taking place.
Animation of tree rotations taking place.
RBnode* RotateDirRoot(
    RBtree* T,   // 赤黒木
    RBnode* P,   // 部分木の根 (Tの根であってもよい)
    int dir) {   // dir ∈ { LEFT, RIGHT }
  RBnode* G = P->parent;
  RBnode* S = P->child[1-dir];
  RBnode* C;
  assert(S != NIL); // 真のノードへのポインターを要求する
  C = S->child[dir];
  P->child[1-dir] = C; if (C != NIL) C->parent = P;
  S->child[  dir] = P; P->parent = S;
  S->parent = G;
  if (G != NULL)
    G->child[ P == G->right ? RIGHT : LEFT ] = S;
  else
    T->root = S;
  return S; // 部分木の新しい根
}

#define RotateDir(N,dir) RotateDirRoot(T,N,dir)
#define RotateLeft(N)    RotateDirRoot(T,N,LEFT)
#define RotateRight(N)   RotateDirRoot(T,N,RIGHT)

[]


6使15

 N N

    1 NILNIL NIL

324
1[6]

(一) "entry" 
NN

(二) "rotate" 

(三) "color" [7]

(四)NN "reassign "
1

410
120





U == NIL || U->color == BLACK // 


U != NIL && U->color == RED   // 

使

U == NIL  U->color
(2

[6]if

[]


NILNNIL PP->child[dir] == NIL  dir P
void RBinsert1(
  RBtree* T,         // -> 赤黒木
  struct RBnode* N,  // -> 挿入するノード
  struct RBnode* P,  // -> Nの親ノード(NULLでも可)
  byte dir)          // Nを挿入するPの側(LEFTまたはRIGHT)
{
  struct RBnode* G;  // -> Pの親ノード
  struct RBnode* U;  // -> Nのおじ

  N->color = RED;
  N->left  = NIL;
  N->right = NIL;
  N->parent = P;
  if (P == NULL) {   // 親がない場合
    T->root = N;     // Nが赤黒木Tの新しい根とし、
    return; // 挿入完了。
  }
  P->child[dir] = N; // NをPのdir側の子として挿入する
  // (do while)ループを開始する
  do {



N 

4PNNP nodeparent 

5

[]

前の状態 ケース
回転 割り当て 後の状態
Δh
P G U x P G U x
I3
I1
I4
I2 N:=G ? ? 2
i I5 PN N:=P o I6 0
o I6 PG
挿入: この一覧表では、前の列で、ノード群の起こりうるすべてのケース[8]をカバーしている。

PNGNUN

PGP dir 

NI2

PN

xoPNiPNGP

I2N1

RB



NPGU



1

12I2GN   

I1I4I6I5 + I6I3

I6I5 + I62

1[]


P45
    if (P->color == BLACK) {
      // Case_I1 (Pは黒):
      return; // 挿入完了
    }
    // ここからPは赤
    if ((G = P->parent) == NULL) 
      goto Case_I4; // Pは赤かつ根
    // else: Pは赤 そして G!=NULL.
    dir = childDir(P); // ノードPが位置するGの側(右か左か)
    U = G->child[1-dir]; // おじ
    if (U == NIL || U->color == BLACK) // Uが黒とみなされると
      goto Case_I56; // Pは赤 && Uは黒
最初の反復
上位の反復
挿入ケース2

挿入ケース2[編集]


PUG5G4GN12
    // Case_I2 (PとUが赤):
    P->color = BLACK;
    U->color = BLACK;
    G->color = RED;
    N = G; // 新しいカレントノード
    // 1段階上の黒を反復
    //   (= 2の木レベル)
  } while ((P = N->parent) != NULL);
  // (do while)ループの終了

挿入ケース3[編集]


2 1 N
  // (do while)ループを抜ける(Case_I2から抜け出した後)。
  // Case_I3: Nは根であり、赤。
  return; // 挿入完了

挿入ケース4[編集]


PN4P1
Case_I4: // Pは根かつ赤:
  P->color = BLACK;
  return; // 挿入完了
最初の反復
上位の反復
挿入ケース5

挿入ケース5[編集]


PUPNGNGGPdir-NPN2P4PN546
Case_I56: // Pは赤 && Uは黒:
  if (N == P->child[1-dir])
  { // Case_I5 (Pは赤 && Uは黒 && NはGの内側の孫):
    RotateDir(P,dir); // Pは根にはならない
    N = P; // 新しいカレントノード
    P = G->child[dir]; // Nの新しい親
    // Case_I6に該当する
  }
最初の反復
上位の反復
挿入ケース6

挿入ケース6[編集]


NGG(1-dir)-GPPNG4GGPPG4GP5
  // Case_I6 (Pは赤 && Uは黒 && NはGの外側の孫):
  RotateDirRoot(T,G,1-dir); // Gは根である可能性がある
  P->color = BLACK;
  G->color = RED;
  return; // 挿入完了
} // RBinsert1の終了

使使

[]


N

NNILNNIL

N2NILNNNNNR1NILNRNRB-colorRNRNN1NIL

N1NILNN52NIL

NNIL4N1N

N1NILNN

[]


NNILNNIL
void RBdelete2(
  RBtree* T,         // -> 赤黒木
  struct RBnode* N)  // -> 削除対象ノード
 {
  struct RBnode* P = N->parent;  // -> Nの親ノード
  byte dir;          // Nの位置するPの側 (∈ { LEFT, RIGHT })
  struct RBnode* S;  // -> Nの兄弟ノード
  struct RBnode* C;  // -> 近いおい
  struct RBnode* D;  // -> 遠いおい

  // P != NULL, Nは根ではないので。
  dir = childDir(N); // ノードNが位置する親Pの側(LEFT か RIGHT)
  // 親PのNをNILに置き換える:
  P->child[dir] = NIL;
  goto Start_D;      // ループに移動する

  // (do while)-ループの開始:
  do {
    dir = childDir(N);   // ノードNの位置する親Pの側
Start_D:
    S = P->child[1-dir]; // Nの兄弟 (黒高さ >= 1)
    D = S->child[1-dir]; // 遠いおい
    C = S->child[  dir]; // 近いおい
    if (S->color == RED)
      goto Case_D3;                  // Sが赤 ===> P+C+Dが黒
    // S is black:
    if (D != NIL && D->color == RED) // 黒でないとみなす
      goto Case_D6;                  // Dが赤 && Sが黒
    if (C != NIL && C->color == RED) // 黒でないとみなす
      goto Case_D5;                  // Cが赤 && S+Dが黒
    // ここでは、両方のおい == NIL (最初の反復) または黒 (上位の反復).
    if (P->color == RED)
      goto Case_D4;                  // Pが赤 && C+S+Dが黒



N10N 

N1P

4

[]

前の状態 ケース
回転 割り当て 後の状態
Δh
P C S D P C S D
D2
D3 PS N:=N ? ? ? 0
D1 N:=P ? ? 1
D4
D5 CS N:=N D6 0
D6 PS
削除: この一覧表では、前の状態の列で、ノード群の起こりうるすべてのケース[8]をカバーしている。

PNSNCSNDSSN1NILCDNIL

NPN dir 

NNIL NNIL  1NIL(D1)

  N1P



RB



NPCSD



1

 Start_D 1PN    Mehlhorn & Sanders"AVL"[3]:165,158AVL[9]

D3D6D5 + D6D4D23D6D5D43

D6D5  D6D3  D5  D63
最初の反復
上位の反復
削除ケース1

削除ケース1[編集]


PSSSSN1PP14PN1=1
    // Case_D1 (P+C+S+Dは黒):
    S->color = RED;
    N = P; // 新しいカレントノード (根かもしれない)
    // 1黒レベル(1木レベル)を上げながら反復する
  } while ((P = N->parent) != NULL);
  // (do while)-ループの終了

削除ケース2[編集]


N1RB1
  // Case_D2 (P == NULL):
  return; // 削除完了

最初の反復
上位の反復
削除ケース3

削除ケース3[編集]


SPCDPdir-SNPSN1NPSD4D5D6
Case_D3: // Sは赤 && P+C+Dは黒:
  RotateDirRoot(T,P,dir); // Pは根かもしれない
  P->color = RED;
  S->color = BLACK;
  S = C; // != NIL
  // ここで: Pは赤 && Sは黒
  D = S->child[1-dir]; // 遠いおい
  if (D != NIL && D->color == RED)
    goto Case_D6;      // Dは赤 && Sは黒
  C = S->child[  dir]; // 近いおい
  if (C != NIL && C->color == RED)
    goto Case_D5;      // Cは赤 && S+Dは黒
  // それ以外のC+Dは黒とみなす。
  // Case_D4に該当する。

最初の反復
上位の反復
削除ケース4

削除ケース4[編集]


SSPSPSN1
Case_D4: // Pは赤 && S+C+Dは黒:
  S->color = RED;
  P->color = BLACK;
  return; // 削除完了

最初の反復
上位の反復
削除ケース5

削除ケース5[編集]


SSNCSNDS(1-dir)-CSNSCND6NPP 
Case_D5: // C red && S+D black:
  RotateDir(S,1-dir); // S is never the root
  S->color = RED;
  C->color = BLACK;
  D = S;
  S = C;
  // now: D red && S black
  // fall through to Case_D6

最初の反復
上位の反復
削除ケース6

削除ケース6[編集]


SSDPdir-SPSDPSD 4ND3N1PPSN15
Case_D6: // Dは赤 && Sは黒:
  RotateDirRoot(T,P,dir); // Pは根かもしれない
  S->color = P->color;
  P->color = BLACK;
  D->color = BLACK;
  return; // 削除終了
} // RBdelete2の終了

このアルゴリズムは、補助的なデータ構造を使用せずに入力を変換し、補助的な変数のためにわずかな記憶領域を余分に使用するだけなので、インプレースである。

脚注[編集]



(一)^ abcdJames Paton. RedBlack Trees. 20211222

(二)^ ab ()Tarjan and Mehlhorn

(三)^ abcMehlhorn, Kurt; Sanders, Peter (2008). 7. Sorted Sequences. Algorithms and Data Structures: The Basic Toolbox. Berlin/Heidelberg: Springer. doi:10.1007/978-3-540-77978-0. ISBN 978-3-540-77977-3. http://people.mpi-inf.mpg.de/~mehlhorn/ftp/Toolbox/SortedSequences.pdf 

(四)^ Cormen, Thomas; Leiserson, Charles; Rivest, Ronald; Stein, Clifford (2009). 13. RedBlack Trees. Introduction to Algorithms (3rd ed.). MIT Press. pp. 308309. ISBN 978-0-262-03384-8 

(五)^ Tarjan, Robert Endre (April 1985). Amortized Computational Complexity. SIAM Journal on Algebraic and Discrete Methods 6 (2): 306318. doi:10.1137/0606031. http://www.cs.duke.edu/courses/fall11/cps234/reading/Tarjan85_AmortizedComplexity.pdf. 

(六)^ abNILNIL.

(七)^ 2

(八)^ abBen Pfaff

(九)^ Dinesh P. Mehta, Sartaj Sahni (Ed.) Handbook of Data Structures and Applications 10.4.2

関連項目[編集]

外部リンク[編集]