STEP 7.1 2分探索木

<問題>

の疑似コードに対応するプログラムを実装する。

以下の実行例は、図4.6の2分探索木になるように順にinsertを行ったあと、図4.9のinsertを行った。
続いて、23をsearchしている。アドレスを、printfの%pオプションで出力

実行結果例になるように、下のプログラム中のprinttreeとmax_vを完成させなさい。

下のプログラムは、問4.3, 4.4の巻末解答 p.179を最小限変更しています。


<実行結果 例> 
INSERT: 32 16 38  7 25 59 42 95  4 18 81
     4
    7
  16
    18
   25
 32
  38
    42
   59
     81
    95


insert 23
     4
    7
  16
    18
     23
   25
 32
  38
    42
   59
     81
    95


The element 23 is found at 0x17d2170.
max_v is 95.

<下のプログラムの実行例>
INSERT: 32 16 38  7 25 59 42 95  4 18 81
insert 23
The element 23 is found at 0x209d170.



解答例
#include <stdio.h>
#include <stdlib.h>

typedef struct node{
   int data;
   struct node *leftson;
   struct node *rightson;
} Node;

Node *search (int x);
void new (Node **w);
void insert (int x);
void delete (int x); /* STEP7.1では、未定義 */
void nfree (Node *n);

void printtree(Node *n, int h); /* たとえばの関数 hは高さ。スペースの数にあたる */
int max_v(Node *n); /* 作成せよ */

//int S[11] = {59,38,7,42,16,81,4,32,95,18,25};  p.179の例。
int S[11] = {32, 16, 38, 7, 25, 59, 42, 95, 4, 18, 81}; /* 図4.6 */
Node *v, *root;   /* 変数rootはNodeを指すポインターを格納する */

/* xを探索する。ノードへのポインターが戻り値 */
Node *search(int x){
   v = root; /* v, rootは大域変数 vは注目しているノード(木)へのポインター。
           rootは、根にあたるノード(木)へのポインター */

   while(v != NULL){          /* vが葉でないノードへのポインターの間 */
      if (v->data == x) {
         return(v);          /* 発見。vをリターン */
      }
      if (v->data > x) {     /* xは、ポインターvの指すノードのdataより小さい */
         v = v->leftson;     /* 左にあるはず。左の木へポインターを更新 */
      }
      else {
         v = v->rightson;    /* 右へ更新 */
      }
   }
   /* 見つけられない場合。例外処理 */
   printf ("The element is not in S.\n");
   return (NULL); /* NULLアドレスを戻り値 */
}


/* ノードを確保 ダブルポインターが使用されているのに注意。wをアドレス1とすると、アドレス1は、Node *を指すアドレス2を格納するポインター変数のアドレス。*/
void new (Node **w) {
   /* wはアドレス。*wもアドレス。wを宣言するには、Node **wとなる */
   *w = (Node *)malloc(sizeof(Node));
   (*w)->leftson = NULL;  /* 教科書は初期化せず、それでもNULLになるのだが */
   (*w)->rightson = NULL; /* ここは初期化すべきです! */
}

/* xを2分探索木に追加する */
void insert(int x){
   Node *p = NULL; /* pはNodeへのポインター NULLに初期化 */
   Node *w;        /* 新規ノードへのポインター */

   v = root; /* 大域変数 vとroot。vの初期値は、根のノードへのポインター */
             /* vは注目しているノードへのポインター */

   while(v != NULL) { /* 葉に到達するまで */
      p = v; /* 次のvへ更新する前に、ポインターをバックアップ */
      if (v->data > x){ /* xが、dataより小さいなら、左の木へ */
         v = v->leftson;
      } else {
         v = v->rightson;
      }
   }

   new(&w);      /* wはノードへのポインター。そのポインター変数のアドレスを関数 に渡している */
   w->data = x;  /* *w.data = xと同じ */

   /* wをpの指すノードのどちら側につなぐのかを再計算。そこは上のv, NULLのはず */
   if (p->data > x){
      p->leftson = w;
   }
   else {
      p->rightson = w;
   }
}

/* メモリ解放 */
void nfree(Node *n){
   if(n->leftson != NULL) {
      nfree(n->leftson);
   }
   if(n->rightson != NULL){
      nfree(n->rightson);
   }
   free(n);
}

int main(void){
   int i;
   new(&root);    /* 変数rootのアドレスを引数に。rootには、根にあたる
                     ノードへのポインター */
   root->data = S[0]; /* 根のノードのdataをS[0]にセット *root.data = S[0]と同じ */

   /* insertした順序をプリント */
   printf("INSERT:%3d", S[0]);
   for (i=1; i<11; i++){
      insert(S[i]);
      printf("%3d", S[i]);
   }
   printf("\n");

   /* 2分木をプリント。作成せよ 課題1 図4.6を横から出力したものになっています */
  /*   printtree(root,0);
       printf("\n\n"); */

   insert(23); printf("insert 23\n");
   /* printtree(root,0);
      printf("\n\n"); */

   printf("The element 23 is found at %p.\n",search(23));

   /* 2分探索木中の最大値をプリント。作成せよ。課題2
   printf("max_v is %d.\n",max_v(root));
  */

   nfree(root);
   return 0;
}