void delete( struct node * * rt, int val ){ struct node * del_node, * parent; struct node * pred_node, * pred_parent; /*-- 二分探索木が空だったら何もせずに終了する --*/ if( *rt == NULL ){ return; } ...(存在検査処理へと続く)...【補足1】(ポインタ変数のポインタ渡しを使用する)
/*-- valを保持するノードが存在するか否かを検査する --*/ del_node = *rt; parent = NULL; while( del_node != NULL ){ if( val == del_node->value ){ break; } if( val < del_node->value ){ parent = del_node; del_node = del_node->left; } else { parent = del_node; del_node = del_node->right; } } /*-- valを保持したノードが存在しないときには何もせずに終了する --*/ if( del_node == NULL ){ return; } ...(削除処理へと続く)...
/*-- 削除対象ノードが子供を持っていない場合 --*/ if( del_node->left == NULL && del_node->right == NULL ){ if( del_node == *rt ){ /* (1) 削除対象ノードが根のとき */ *rt = NULL; } else { /* (2) 削除対象ノードが根でないとき */ if( val < parent->value ){ /* (a) */ parent->left = NULL; /* (b) */ } else { parent->right = NULL; /* (c) */ } } free(del_node); /* (3) メモリ領域を解放して、 */ return; /* 終了する */ } ...(次の場合へと続く)...
/*-- 削除対象ノードが左側の子供だけを持っている場合 --*/ if( del_node->left != NULL && del_node->right == NULL ){ if( del_node == *rt ){ /* (1) 削除対象ノードが根のとき */ *rt = del_node->left; } else { /* (2) 削除対象ノードが根でないとき */ if( val < parent->value ){ /* (a) */ parent->left = del_node->left; /* (b) */ } else { parent->right = del_node->left; /* (c) */ } } free(del_node); /* (3) メモリ領域を解放して、 */ return; /* 終了する */ } ...(次の場合へと続く)...
/*-- 削除対象ノードが右側の子供だけを持っている場合 --*/ if( del_node->left == NULL && del_node->right != NULL ){ if( del_node == *rt ){ /* (1) 削除対象ノードが根のとき */ *rt = del_node->right; } else { /* (2) 削除対象ノードが根でないとき */ if( val < parent->value ){ /* (a) */ parent->left = del_node->right; /* (b) */ } else { parent->right = del_node->right; /* (c) */ } } free(del_node); /* (3) メモリ領域を解放して */ return; /* 終了する */ } ...(次の場合へと続く)...
/*-- 削除対象ノードが二人の子供を持っている場合 --*/ if( del_node->left != NULL && del_node->right != NULL ){ /* [1] val(削除対象のデータ要素)のpredecessorノードを求める */ ...(詳細は後述)... /* [2] predecessorノードを二分探索木から取り外す */ ...(詳細は後述)... /* [3] 削除対象ノードをpredecessorノードに置き換える */ ...(詳細は後述)... /* 削除対象ノードのメモリ領域を解放して終了する */ free(del_node); return; }以下では、上記の[1]、[2]、[3]の詳細を示す。
/* valのpredecessorノードを求める */ pred_node = del_node->left; /* (a) */ pred_parent = del_node; while( pred_node->right != NULL ){ /* (b),(c) */ pred_parent = pred_node; pred_node = pred_node->right; }
/* predecessorノードを二分探索木から取り外す */ if( pred_parent == del_node ){ pred_parent->left = pred_node->left; } else { pred_parent->right = pred_node->left; }参考までに、precdecessorノードを取り外すときのイメージ図を次に示す。
/* 削除対象ノードをpredecessorノードに置き換える */ pred_node->left = del_node->left; pred_node->right = del_node->right; if( *rt == del_node ){ *rt = pred_node; } else { if( val < parent->value ){ parent->left = pred_node; } else { parent->right = pred_node; } }参考までに、この処理のイメージ図を次に示す。
if( del_node->left != NULL && del_node->right != NULL ){ /* val(削除対象のデータ要素)のpredecessorノードを求める */ pred_node = del_node->left; pred_parent = del_node; while( pred_node->right != NULL ){ pred_parent = pred_node; pred_node = pred_node->right; } /* predecessorノードを二分探索木から取り外す */ if( pred_parent == del_node ){ pred_parent->left = pred_node->left; } else { pred_parent->right = pred_node->left; } /* 削除対象ノードをpredecessorノードに置き換える */ pred_node->left = del_node->left; pred_node->right = del_node->right; if( *rt == del_node ){ *rt = pred_node; } else { if( val < parent->value ){ parent->left = pred_node; } else { parent->right = pred_node; } } /* 削除対象ノードのメモリ領域を解放して終了する */ free(del_node); return; }
/*-★ 削除関数(データ要素を削除する) --*/ void delete( struct node * * rt, int val ){ struct node * del_node, * parent; struct node * pred_node, * pred_parent; /*-- 二分探索木が空だったら何もせずに終了する --*/ if( *rt == NULL ){ return; } /*-- valを保持するノードが存在するか否かを検査する --*/ del_node = *rt; parent = NULL; while( del_node != NULL ){ if( val == del_node->value ){ break; } if( val < del_node->value ){ parent = del_node; del_node = del_node->left; } else { parent = del_node; del_node = del_node->right; } } /*-- valを保持したノードが存在しないときには何もせずに終了する --*/ if( del_node == NULL ){ return; } /*-- 削除対象ノードが子供を持っていない場合 --*/ if( del_node->left == NULL && del_node->right == NULL ){ if( del_node == *rt ){ /* 削除対象ノードが根のとき */ *rt = NULL; } else { /* 削除対象ノードが根でないとき */ if( val < parent->value ){ parent->left = NULL; } else { parent->right = NULL; } } free(del_node); /* 削除対象ノードのメモリ領域を解放 */ return; /* 終了 */ } /*-- 削除対象ノードが左側の子供だけを持っている場合 --*/ if( del_node->left != NULL && del_node->right == NULL ){ if( del_node == *rt ){ /* 削除対象ノードが根のとき */ *rt = del_node->left; } else { /* 削除対象ノードが根でないとき */ if( val < parent->value ){ parent->left = del_node->left; } else { parent->right = del_node->left; } } free(del_node); /* 削除対象ノードのメモリ領域を解放 */ return; /* 終了 */ } /*-- 削除対象ノードが右側の子供だけを持っている場合 --*/ if( del_node->left == NULL && del_node->right != NULL ){ if( del_node == *rt ){ /* 削除対象ノードが根のとき */ *rt = del_node->right; } else { /* 削除対象ノードが根でないとき */ if( val < parent->value ){ parent->left = del_node->right; } else { parent->right = del_node->right; } } free(del_node); /* 削除対象ノードのメモリ領域を解放 */ return; /* 終了 */ } /*-- 削除対象ノードが二人の子供を持っている場合 --*/ if( del_node->left != NULL && del_node->right != NULL ){ /* val(削除対象のデータ要素)のpredecessorノードを求める */ pred_node = del_node->left; pred_parent = del_node; while( pred_node->right != NULL ){ pred_parent = pred_node; pred_node = pred_node->right; } /* predecessorノードを二分探索木から取り外す */ if( pred_parent == del_node ){ pred_parent->left = pred_node->left; } else { pred_parent->right = pred_node->left; } /* 削除対象ノードをpredecessorノードに置き換える */ pred_node->left = del_node->left; pred_node->right = del_node->right; if( *rt == del_node ){ *rt = pred_node; } else { if( val < parent->value ){ parent->left = pred_node; } else { parent->right = pred_node; } } /* 削除対象ノードのメモリ領域を解放して終了する */ free(del_node); return; } }【参考】 上記のプログラムでは、削除対象ノードが子供を持っているか否か(さらに、何人持っているか)といった場合分けを、次のようなif文によって判定している。
/*-- 削除対象ノードが子供を持っていない場合 --*/ if( del_node->left == NULL && del_node->right == NULL ){ ...... } /*-- 削除対象ノードが左側の子供だけを持っている場合 --*/ if( del_node->left != NULL && del_node->right == NULL ){ ...... } /*-- 削除対象ノードが右側の子供だけを持っている場合 --*/ if( del_node->left == NULL && del_node->right != NULL ){ ...... } /*-- 削除対象ノードが二人の子供を持っている場合 --*/ if( del_node->left != NULL && del_node->right != NULL ){ ...... }各if文の条件が満たされて、その内部(then節)に処理が移行した場合、 上のプログラムでは、その内部でreturn文を実行している。 従って、2番目と3番目のif文の条件式を次のように書き換えたとしても正しく処理される。
..... /*-- 削除対象ノードが左側の子供だけを持っている場合 --*/ if( del_node->right == NULL ){ ...... } /*-- 削除対象ノードが右側の子供だけを持っている場合 --*/ if( del_node->left == NULL ){ ...... } ......さらに、3番目までのif文の条件が満たされなかったときには、削除対象ノードが二人の子供を持っていることは明らかである。 従って、4番目のif文については、その内部(then節)だけを残して、if節(ifと条件式)ならびにthen節全体を囲む中カッコを削除してしまっても構わない。 〈参考終わり〉
/********************************************************************** * ヘッダファイル **********************************************************************/ #include <stddef.h> /*-- NULL , size_t --*/ #include <stdio.h> /*-- printf関数 --*/ #include <stdlib.h> /*-- malloc関数 --*/ /********************************************************************** * 二分探索木のノードを表す自己参照構造体 **********************************************************************/ struct node { int value; /*-- データ要素 --*/ struct node * left; /*-- 左側の子供へのポインタ --*/ struct node * right; /*-- 右側の子供へのポインタ --*/ }; /********************************************************************** * 基本操作関数(プロトタイプ宣言) **********************************************************************/ void init( struct node * * rt ); /*-- 初期設定 --*/ void add( struct node * * rt, int val ); /*-- 追加 --*/ struct node* search( struct node * root, int val ); /*-- 検索 --*/ void delete( struct node * * rt, int val ); /*-- 削除 --*/ /********************************************************************** * その他の関数(プロトタイプ宣言) **********************************************************************/ void display( struct node * p, int level); /* 内容表示 */ void freeAll( struct node * * rt ); /* メモリ解放(main) */ void freeTree( struct node * p ); /* メモリ解放(sub) */ /********************************************************************** * main関数 **********************************************************************/ int main(void){ int A[] = { 29, 13, 5, 19, 15, 47, 35, 33, 40, 51, 65, -1 }; int i; struct node * root; /* 二分探索木(の根)を指すポインタ変数 */ /*-★ 二分探索木の初期設定 --*/ init( &root ); /*-★ 各A[i]を二分探索木に追加する --*/ i = 0; while( A[i] > 0 ){ add( &root, A[i] ); i++; } /*-★ 二分探索木の内容を表示する --*/ printf("[1] 登録直後の二分探索木\n"); printf("root=%p\n", root); /* 根へのポインタを表示 */ display(root,0); /* 二分探索木の内容表示 */ printf("\n"); /*-★ 40を削除したあと表示(子供なしの場合) --*/ delete(&root,40); printf("[2] 40を削除(子供なしの場合)\n"); printf("root=%p\n", root); /* 根へのポインタを表示 */ display(root,0); /* 二分探索木の内容表示 */ printf("\n"); /*-★ 35を削除したあと表示(左側の子供だけの場合) --*/ delete(&root,35); printf("[3] 35を削除(左側の子供だけの場合)\n"); printf("root=%p\n", root); /* 根へのポインタを表示 */ display(root,0); /* 二分探索木の内容表示 */ printf("\n"); /*-★ 51を削除したあと表示(右側の子供だけの場合) --*/ delete(&root,51); printf("[4] 51を削除(右側の子供だけの場合)\n"); printf("root=%p\n", root); /* 根へのポインタを表示 */ display(root,0); /* 二分探索木の内容表示 */ printf("\n"); /*-★ 47を削除したあと表示(子供が二人いる場合) --*/ delete(&root,47); printf("[5] 47を削除(子供が二人いる場合)\n"); printf("root=%p\n", root); /* 根へのポインタを表示 */ display(root,0); /* 二分探索木の内容表示 */ printf("\n"); /*-★ 29を削除したあと表示(根を削除) --*/ delete(&root,29); printf("[6] 29を削除(根を削除)\n"); printf("root=%p\n", root); /* 根へのポインタを表示 */ display(root,0); /* 二分探索木の内容表示 */ printf("\n"); /*-★ メモリ領域を解放する --*/ freeAll(&root); return 0; } /********************************************************************** * 基本操作関数(関数本体の定義) **********************************************************************/ /*-★ 初期設定関数 --*/ /*-★ 追加関数(データ要素を追加する) --*/ /*-★ 検索関数(データ要素を検索する) --*/ ...(上記の各関数については、以前のものと同じなので省略する)... /*-★ 削除関数(データ要素を削除する) --*/ void delete( struct node * * rt, int val ){ struct node * del_node, * parent; struct node * pred_node, * pred_parent; ...(前節で示したプログラムを参照)... } /*-★ 内容表示関数(レベルに応じて字下げする) --*/ void display( struct node * p, int level){ int i; if( p != NULL ){ if( level == 0 ){ printf("%p:[%d;%p,%p]\n", p, p->value, p->left, p->right); } else { for(i=0;i<level;i++){ printf(" "); } printf("┗ %p:[%d;%p,%p]\n", p, p->value, p->left, p->right); } display(p->left,level+1); /* 左部分木をトラバースする */ display(p->right,level+1); /* 右部分木をトラバースする */ } } /*-★ メモリ解放関数 --*/ ...(以前のものと同じなので省略する)...(実行結果)
[1] 登録直後の二分探索木 root=0x804a050 0x804a050:[29;0x804a060,0x804a0a0] ┗ 0x804a060:[13;0x804a070,0x804a080] ┗ 0x804a070:[5;(nil),(nil)] ┗ 0x804a080:[19;0x804a090,(nil)] ┗ 0x804a090:[15;(nil),(nil)] ┗ 0x804a0a0:[47;0x804a0b0,0x804a0e0] ┗ 0x804a0b0:[35;0x804a0c0,0x804a0d0] ┗ 0x804a0c0:[33;(nil),(nil)] ┗ 0x804a0d0:[40;(nil),(nil)] ┗ 0x804a0e0:[51;(nil),0x804a0f0] ┗ 0x804a0f0:[65;(nil),(nil)] [2] 40を削除(子供なしの場合) root=0x804a050 0x804a050:[29;0x804a060,0x804a0a0] ┗ 0x804a060:[13;0x804a070,0x804a080] ┗ 0x804a070:[5;(nil),(nil)] ┗ 0x804a080:[19;0x804a090,(nil)] ┗ 0x804a090:[15;(nil),(nil)] ┗ 0x804a0a0:[47;0x804a0b0,0x804a0e0] ┗ 0x804a0b0:[35;0x804a0c0,(nil)] ┗ 0x804a0c0:[33;(nil),(nil)] ┗ 0x804a0e0:[51;(nil),0x804a0f0] ┗ 0x804a0f0:[65;(nil),(nil)] [3] 35を削除(左側の子供だけの場合) root=0x804a050 0x804a050:[29;0x804a060,0x804a0a0] ┗ 0x804a060:[13;0x804a070,0x804a080] ┗ 0x804a070:[5;(nil),(nil)] ┗ 0x804a080:[19;0x804a090,(nil)] ┗ 0x804a090:[15;(nil),(nil)] ┗ 0x804a0a0:[47;0x804a0c0,0x804a0e0] ┗ 0x804a0c0:[33;(nil),(nil)] ┗ 0x804a0e0:[51;(nil),0x804a0f0] ┗ 0x804a0f0:[65;(nil),(nil)] [4] 51を削除(右側の子供だけの場合) root=0x804a050 0x804a050:[29;0x804a060,0x804a0a0] ┗ 0x804a060:[13;0x804a070,0x804a080] ┗ 0x804a070:[5;(nil),(nil)] ┗ 0x804a080:[19;0x804a090,(nil)] ┗ 0x804a090:[15;(nil),(nil)] ┗ 0x804a0a0:[47;0x804a0c0,0x804a0f0] ┗ 0x804a0c0:[33;(nil),(nil)] ┗ 0x804a0f0:[65;(nil),(nil)] [5] 47を削除(子供が二人いる場合) root=0x804a050 0x804a050:[29;0x804a060,0x804a0c0] ┗ 0x804a060:[13;0x804a070,0x804a080] ┗ 0x804a070:[5;(nil),(nil)] ┗ 0x804a080:[19;0x804a090,(nil)] ┗ 0x804a090:[15;(nil),(nil)] ┗ 0x804a0c0:[33;(nil),0x804a0f0] ┗ 0x804a0f0:[65;(nil),(nil)] [6] 29を削除(根を削除) root=0x804a080 0x804a080:[19;0x804a060,0x804a0c0] ┗ 0x804a060:[13;0x804a070,0x804a090] ┗ 0x804a070:[5;(nil),(nil)] ┗ 0x804a090:[15;(nil),(nil)] ┗ 0x804a0c0:[33;(nil),0x804a0f0] ┗ 0x804a0f0:[65;(nil),(nil)]