質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.77%

c言語で二分探索木を実装したいです

解決済

回答 5

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,840

score 5

コード
`#include <stdio.h>
#include <stdlib.h>

/* 木の節の定義 */
typedef struct node{
    int data;           /* 探索のキーになるデータ型 */
    struct node *left;  /* 左の子 */
    struct node *right; /* 右の子 */
}NODE;
/* メンバright,leftにNULLポインタがセットされているときは該当する子がいないことを表す */

/* 初期状態では二分探索木は空の状態。
   グローバル変数rootが二分探索木の根へのポインタを保持しているものとする */
/* グローバル変数rootをNULLで初期化 */
NODE *root = NULL;

/* エラーメッセージをプリントしてexitする関数*/
/* ポインタ(アドレス)の先のデータを読み取り専用にする */
void   fatal_error(const char *s)
{
    /*  fprintf関数を使ってstderrで標準エラーを出力する*/
    fprintf(stderr,"%s", s); 
    exit(1);  /* 異常終了 */
}

/* 二分探索木から最小の要素を削除する関数 */
/* 削除した節へのポインタを返す
   p:二分探索木へのポインタへのポインタ
     (つまり*pが木へのポインタとなる) 
    NODE型へのポインターが戻り値 */
NODE *deletemin(NODE **p)
{
    NODE *x;

    while ((*p)->left != NULL)
        p=&(*p)->left;
        x=*p;
        *p=(*p)->right;
        return x;
}

/* 二分探索木を探索する関数 */
/* パラメータkeyを受け取ってポインタを返す関数として定義
   key:探索すべきキーの値
   探索に成功した場合そのデータの節へのポインタを返す
   NODE型へのポインターが戻り値 */
NODE *search(int key)
{
    NODE *p;     /* 現在注目している節 */                    
    p=root;      /* まず根に注目する */

    /* 進むべき子が存在する限り繰り返す */
  while (p != NULL) { 
      /* キーと注目している節のデータが等しいか比較 */
    if (key == p->data){
        /* もしキーと注目している節のデータとが等しければポインタを関数として返す */
        printf("探索した値の番地です>>%p\n",p);
        return p;
    }else if (key < p->data){
        /* キーの方が小さければ左部分木に進む */
         p = p->left;
    }else{
        /* キーの方が大きければ右部分木に進む*/
         p = p->right;
  }
  /* ループを抜け出たということは見付からなかったというと
     NULL返して失敗したことを知らせる */
  printf("NotExist\n");
  return NULL;
  }
}

/* 二分探索木から要素を追加する関数*/
/* 探索と同じ要領で木を辿り、辿るべき部分木がなければそこに挿入する */
/* 挿入した要素が置かれる節へのポインタを返す 
   すでに要素が登録されているのなら、何もしないでNULLを返す
   key:挿入するデータ
   NODE型へのポインターが戻り値 */
NODE *insert(int key)
{
    NODE **p,*new;
    /* 変数pが変数rootを指すように初期化する */
    p=&root;
    /* 挿入すべき場所が見つかるまで繰り返す */
    while (*p != NULL) {
    /* キーと注目している節のデータが等しいか比較 */
     if (key == (*p)->data){
     /* すでに登録されている */
        printf("AlreadyExsits\n");
        return NULL;
     }else if (key < (*p)->data){ 
         /* キーの方が小さければ左部分木に進む */
          p =&(*p)->left;
    }else{
        /* キーの方が大きければ右部分木に進む */
         p =&(*p)->right;
     }
    }
     /* 挿入されるべき場所が見つかったら */
     /* 挿入される節をつくる */

     /* strct型のメモリを確保した分だけnewに入れる。もしNULLだったらエラー */
     if((new=malloc(sizeof(NODE)))==NULL)
     fatal_error("out of memory!!");
     /* 新しい節には子がないのでNULLにしておく */
     new->left =NULL;
     new->right=NULL;
     /* 要素の値をセットする */
     new->data=key;
     /* 正しい場所に挿入する */
     /* ポインタpは挿入される節へのポインタが入っている場所を指している */
     *p=new;
     return new;
}

/* 二分探索木から要素を削除する関数 */
/* 削除に成功したら1、要素が存在しなければ0を返す
    key:削除するデータ */
int delete(int key)
{
    /* 親へのポインタを使う */
    NODE **p, *x;
    /* 変数pが変数rootを指すように初期化する */
    p = &root;
    /* 削除対象となる要素を探す */
    while (*p != NULL) {
        /* 見つかった
           変数pは削除される節の親のメンバleft,right
           変数xは削除される節そのものを指している */
        if (key == (*p)->data) {
            x = *p;
            /* 1つも子を持っていない、葉である場合 */
            if (x->left == NULL && x->right == NULL){ 
               *p = NULL;
            /* 右の子のみをもつ */
            }else if (x->left == NULL){
                *p = x->right;
                    /* 左の子のみをもつ */
            }else if (x->right == NULL){
                    *p = x->left;
                    /* 左右2つの子を持つ */
            }else{
                /* 部分木から最小の要素を取り去る */
                *p = deletemin(&x->right);
                (*p)->right = x->right;
                (*p)->left = x->left;
           }
            /* 取り除いた節を解放させる */
            free(x);
            printf("Done\n");
            return 1;

       }else if (key < (*p)->data){
           /* 左部分木に進む */
           p = &(*p)->left;
       }else{
           /* 右部分木に進む */
           p = &(*p)->right;
        }
    }
    /* 削除対象が見つからなかった */
    printf("NotExist\n");
    return 0;
}

/* 二分木を行きがけ順でなぞる */
void preorder(NODE *p)
{
    /* 木が空なら何もしない */
    if(p==NULL)
    return;
    else{
        printf("%d  ",p->data); /* 自身の値を出力 */
        preorder(p->left);    /* 左ノードへ移動 */
        preorder(p->right);   /* 右ノードへ移動 */
    }

}

/* 二分木を通りがけ順でなぞる */
/* 全要素を昇順に表示する関数
   二分探索木の全要素を小→大の順で表示する */
/* 全体の流れ
   ある要素の左枝先を全部調べ終えたら、自身の値を出力し、
   次にその要素の右枝先の要素を全部調べる
   左枝を調べる
   調べ終えたら戻ってくる
   値を出力する
   右枝を調べる
   調べ終えたら戻ってくる
   関数を終了して、自身を呼び出した関数の元へと戻る */
void inorder(NODE *p)
{
    /* 木が空なら何もしない */
    if(p==NULL)
    return;
    else{
        /* 引数に左へのポインタを渡すことで、再帰を繰り返して延々と左枝へ移動し
          一番左、一番小さい値にたどり着く
          子ノードはないため、引数の値はNULLに当たって戻ってくる
          一番下位の要素なので、左枝と同様に右枝もない。
          なので終点である NULL に行き当たり、また return で戻ってくる。
          そして「一番左を操作する関数」は処理を終える。
          この関数に戻り値はない。「画面に出力」して、それで終了*/
        inorder(p->left);      /* 左ノードへ移動 */
        printf("%d  ",p->data);  /* 自身の値を出力 */
        inorder(p->right);     /* 右ノードへ移動 */
    }
    /* 関数を終了して、自身を呼び出した関数のもとへ戻る */

}

/* 二分木を帰りがけ順でなぞる */
void postorder(NODE *p)
{
    /* 木が空なら何もしない */
    if(p==NULL)
    return;
    else{
    postorder(p->left);   /* 左ノードへ移動 */
    postorder(p->right);  /* 右ノードへ移動 */
    printf("%d  ",p->data); /* 自身の値を出力 */
    }

}

int main(void)
{     
    int a,key; 
    int mn=0;

    printf("二分探索木をします\n");
    do{
        printf("\nメニューを選んでください\n1=追加   2=行きがけ順  3=通りがけ順  4=帰りがけ順  5=指定した値の番地  6=削除  9=終了\n");
        scanf("%d",&mn);
   switch(mn){

       case 1:
       printf("追加する数字を入力してください\n");
       scanf("%d",&a);
       insert(a);
    break;

    case 2:
    printf("行きがけ順です\n");
    preorder(root);
     break;

    case 3:
    printf("通りがけ順です\n");
    inorder(root);
     break;

    case 4:
    printf("帰りがけ順です\n");
    postorder(root);
     break;

    case 5:
    printf("指定した値の番地を出力します\n");
    scanf("%d",&key);
    search(key);
    break;

    case 6:
    printf("指定した値を削除します\n");
    scanf("%d",&key);
    delete(key);
    break;

    case 9:
    printf("終了します\n");
    break;

    default:
    printf("エラー:メニューの中の数字を入力してください\n");
   }
    }while (mn !=9);
   return 0;
}
``

 前提・実現したいこと``````

二分探索木のプログラムを実装したいです 仕様は  数字を入力して 1=追加  2=行きがけ順で表示  3=通りがけ順で表示  4=帰りがけ順で表示  5=指定した値の番地で表示  6=削除  9=終了  ができるようにしたいです  アドバイスお願いいたします!!

発生している問題・エラーメッセージ

「34行目」で記述エラーを発見しました。 「identifier」を付け忘れています。 と出てしまいます

### 該当のソースコード  

```ここに言語を入力 ここに言語を入力

コード  

悩んでいること

① まず、
NODE *search(int key)
教科書に書いてあったこの宣言の仕方がよく分かりません

型名なしの関数の宣言の仕方を調べると一つだけ解説しているサイトが見つかり、自動的にint型になると書  いてあったのですが正しいのでしょうか?

引数があり、戻り値がある場合はvoid型ではない、
引数があり、戻り値がない場合はvoid型、
引数がなし、戻り値がある場合もvoid型、
引数がなし、戻り値がない場合もvoid型
と学びました

preorder(NODE *p)
postorder(NODE *p)
inorder(NODE *p)
これらの関数は引数があり、戻り値がない場合なのでvoid型、

int delete(int key)
この関数も引数があり、戻り値がない場合なのでvoid型だと思われるのですがなぜint型なのでしょうか

NODE *search(int key)
NODE *insert(int key)
NODE *deletemin(NODE **p)
これらの関数は引数があり、戻り値がある場合はvoid型ではない、int型だと思います。

②keyの扱い方についてです

if (key == p->data)
ここの部分で教科書では関数を使って比べていましたがむずかしいので不等号を使って比べることにしました
keyは値ですが、p->dataは番地ですよね
同じように値同士で扱うにはどのようにしたら良いのでしょうか
構造体の場合、アロー演算子は番地、ドットが実体だと習いましたがエラーが出てしまいます

③関数の値渡しについてです
case 1:
printf("追加する数字を入力してください\n");
scanf("%d",&a);
insert(a);
break;
case 5:
printf("指定した値の番地を出力します\n");
scanf("%d",&key1);
search(key1);
break;

case 6:
printf("指定した値を削除します\n");
scanf("%d",&key2);
delete(key2);
break;

ここの部分で出力する場合の値と指定した値の変数名はそれぞれ変えたほうが良いですよね?

④探索した値の番地を出力する際に、

/* もしキーと注目している節のデータとが等しければポインタを関数として返す */
printf("探索した値の番地です>>%d\n",p->data);
return p;

としていますが、めちゃくちゃな状態です
探索した値の番地を返す場合、 return pはどのように扱えば良いのか悩みます。

補足情報(FW/ツールのバージョンなど

初心者で使い方がよく分かっていません
自分の理解が正しいのか不安なのでコメント多いです
見づらければ修正します
ご指摘ください

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Zuishin

    2020/12/01 12:34

    > 他にも凡ミスがあるかもしれません

    凡ミスというのは、本来できるはずのことをミスした時に使う言葉です。
    20 行目や 30 行目などを見ても、まったく何もわからない人が書いたものにしか見えません。

    あなたにはこの課題は早すぎます。この手の質問を見るたびに思うんですが、なぜ学習してから取り組まないのでしょう?
    テスト勉強せずにテストしなければならないルールでもあるんでしょうか?

    キャンセル

  • この投稿は削除されました

  • fana

    2020/12/01 15:49

    > ドットにしたり、他の変数にに代入して値にしてもダメだった

    行き当たりばったりにいじくってても仕方ないのでは.
    (確率的にたまたま正解と同じになることもあるハズ!という可能性に賭けた取り組み方なのか?)

    課題が教科書に沿う内容であれば,教科書を読み返すのが最も手っ取り早い「頑張り」方なのでは.

    キャンセル

回答 5

checkベストアンサー

+1

searchのインデントを修正しました。
あきらかにおかしいことが判るかと思います。
whileの外側でNotExistを印字すべきです。

NODE *search(int key)
{
    NODE *p;                                        /* 現在注目している節 */
    p = root;                                       /* まず根に注目する */

    /* 進むべき子が存在する限り繰り返す */
    while (p != NULL) {
        /* キーと注目している節のデータが等しいか比較 */
        if (key == p->data) {
            /* もしキーと注目している節のデータとが等しければポインタを関数として返す */
            printf("探索した値の番地です>>%p\n", p);
            return p;
        } else if (key < p->data) {
            /* キーの方が小さければ左部分木に進む */
            p = p->left;
        } else {
            /* キーの方が大きければ右部分木に進む */
            p = p->right;
        }
        /* ループを抜け出たということは見付からなかったというと
           NULL返して失敗したことを知らせる */
        printf("NotExist\n");
        return NULL;
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/12/02 18:19

    インデントについてよく理解していなくて申し訳ありません
    ルールをしっかり学ぼうと思います
    インデントをきちんとするとミスがすぐ見つかりますね

    誠にありがとうございました!!!
    完璧に動作するようになりました、丁寧な解説で本当に助かりました!!

    キャンセル

+1

回答ではありません。
https://paiza.io/ja/projects/new?language=c
上記URLがwebで実行可能なCコンパイラです。
こちらで、実行してみてください。
エラーがたくさん出ます。
会員にならなくても、とりあえず、実行はできるみたいです。
但し、本格的に使うなら、会員になったほうが良いかも知れません。

蛇足ですが、下記が私の環境でコンパイルした結果です。(ソースはgoo1.cとしています)
(Mingw gcc version 8.3.0 (x86_64-posix-seh, Built by strawberryperl.com project))

D:\goo\c>vgcc goo1.c
<gcc goo1.c -o goo1.exe --input-charset=cp932 --exec-charset=cp932>
goo1.c: In function 'search':
goo1.c:20:17: error: 'key' redeclared as different kind of symbol
     struct node key;
                 ^~~
goo1.c:18:18: note: previous definition of 'key' was here
 NODE *search(int key)
              ~~~~^~~
goo1.c:26:13: error: invalid operands to binary == (have 'struct node' and 'int')
     if (key == p->data)
             ^~ ~~~~~~~
goo1.c:30:5: error: 'else' without a previous 'if'
     else if (key < (p->data)) {
     ^~~~
goo1.c:30:18: error: invalid operands to binary < (have 'struct node' and 'int')
     else if (key < (p->data)) {
                  ^ ~~~~~~~~~
goo1.c:33:10: error: expected '}' before 'else'
          else
          ^~~~
goo1.c: In function 'insert':
goo1.c:44:9: error: 'key' redeclared as different kind of symbol
     int key;
         ^~~
goo1.c:42:18: note: previous definition of 'key' was here
 NODE *insert(int key)
              ~~~~^~~
goo1.c:57:5: error: expected '}' before 'else'
     else
     ^~~~
goo1.c:63:14: warning: implicit declaration of function 'malloc' [-Wimplicit-function-declaration]
      if((new=malloc(sizeof(NODE)))=NULL)
              ^~~~~~
goo1.c:63:14: warning: incompatible implicit declaration of built-in function 'malloc'
goo1.c:63:14: note: include '<stdlib.h>' or provide a declaration of 'malloc'
goo1.c:2:1:
+#include <stdlib.h>

goo1.c:63:14:
      if((new=malloc(sizeof(NODE)))=NULL)
              ^~~~~~
goo1.c:63:35: error: lvalue required as left operand of assignment
      if((new=malloc(sizeof(NODE)))=NULL)
                                   ^
goo1.c:64:6: warning: implicit declaration of function 'error'; did you mean 'perror'? [-Wimplicit-function-declaration]
      error("out of memory!!");
      ^~~~~
      perror
goo1.c: In function 'delete':
goo1.c:79:9: error: 'key' redeclared as different kind of symbol
     int key;
         ^~~
goo1.c:77:16: note: previous definition of 'key' was here
 int delete(int key)
            ~~~~^~~
goo1.c:103:14: warning: implicit declaration of function 'deletemin'; did you mean 'delete'? [-Wimplicit-function-declaration]
           *p=deletemin(&x->right);
              ^~~~~~~~~
              delete
goo1.c:103:13: warning: assignment to 'NODE *' {aka 'struct node *'} from 'int' makes pointer from integer without a cast [-Wint-conversion]
           *p=deletemin(&x->right);
             ^
goo1.c:104:23: error: lvalue required as left operand of assignment
           &(*p)->right=x->right;
                       ^
goo1.c:105:22: error: lvalue required as left operand of assignment
           &(*p)->left=x->left;
                      ^
goo1.c:108:7: warning: implicit declaration of function 'free' [-Wimplicit-function-declaration]
       free(x);
       ^~~~
goo1.c:108:7: warning: incompatible implicit declaration of built-in function 'free'
goo1.c:108:7: note: include '<stdlib.h>' or provide a declaration of 'free'
goo1.c: At top level:
goo1.c:125:7: error: conflicting types for 'deletemin'
 NODE *deletemin(NODE **p)
       ^~~~~~~~~
goo1.c:103:14: note: previous implicit declaration of 'deletemin' was here
           *p=deletemin(&x->right);
              ^~~~~~~~~
goo1.c:137:1: warning: return type defaults to 'int' [-Wimplicit-int]
 preorder(NODE *p)
 ^~~~~~~~
goo1.c: In function 'preorder':
goo1.c:141:5: warning: 'return' with no value, in function returning non-void
     return;
     ^~~~~~
goo1.c:137:1: note: declared here
 preorder(NODE *p)
 ^~~~~~~~
goo1.c:150:1: warning: implicit declaration of function 'inorder'; did you mean 'preorder'? [-Wimplicit-function-declaration]
 inorder(NODE *p)
 ^~~~~~~
 preorder
goo1.c:150:9: error: expected expression before 'NODE'
 inorder(NODE *p)
         ^~~~
goo1.c:150:17: error: expected ';' before '{' token
 inorder(NODE *p)
                 ^
                 ;
 {
 ~
goo1.c:230:1: error: expected declaration or statement at end of input
 }

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/12/02 09:48

    ありがとうございます!!!
    paizaを使って一つずつ確認していこうと思います!

    キャンセル

  • 2020/12/02 17:25

    オートインデントも補完もスペルチェックもステップ実行も使えない、いい加減な環境ではなく、まともなソースコードエディタを使いましょう。

    何度も質問を編集されていますが、一向にインデントが直る気配がなく、それに起因するバグもたくさん入ったままです。
    まともなエディタを使えば問題の大半が解決すると思います。

    キャンセル

+1

インラインで回答します。

① まず、
NODE *search(int key)
教科書に書いてあったこの宣言の仕方がよく分かりません

型名なしの関数の宣言の仕方を調べると一つだけ解説しているサイトが見つかり、自動的にint型になると書  いてあったのですが正しいのでしょうか?
(回答:正しいです。 sub(int key) と int sub(int key)は同じとみなされます。

引数があり、戻り値がある場合はvoid型ではない、(回答:正しい 例 int sub(int key)の場合)
引数があり、戻り値がない場合はvoid型、(回答:正しい 例 void sub(int key)の場合)
引数がなし、戻り値がある場合もvoid型、(回答:誤り 例 int sub(void) は引数なしのint型を返す関数)
引数がなし、戻り値がない場合もvoid型   回答:正しい 例 void sub(void)の場合)
と学びました

preorder(NODE *p)
postorder(NODE *p)
inorder(NODE *p)
これらの関数は引数があり、戻り値がない場合なのでvoid型、
回答:正しい。正確には
void preorder(NODE *p)
void postorder(NODE *p)
void inorder(NODE *p)
と記述すべきです。

int delete(int key)
この関数も引数があり、戻り値がない場合なのでvoid型だと思われるのですがなぜint型なのでしょうか
回答:戻り値はあります。int型です。あなたのソースの中で
return 1;
return 0;
の個所で、0又は1のint型のデータを戻り値として返しています。

NODE *search(int key)
NODE *insert(int key)
NODE *deletemin(NODE **p)
これらの関数は引数があり、戻り値がある場合はvoid型ではない、int型だと思います。
回答:誤り NODE型へのポインターが戻り値です。NODE型へのポインター型です。int型ではありません。

②keyの扱い方についてです

if (key == p->data)
ここの部分で教科書では関数を使って比べていましたがむずかしいので不等号を使って比べることにしました
keyは値ですが、p->dataは番地ですよね
回答:p->dataは値です。
printf("追加する数字を入力してください\n");
scanf("%d",&a);
で入力した数値が格納されます。
p->dataのpは番地(ポインター)です。p->dataは値です。
従ってif (key == p->data)は何の問題もありません。

同じように値同士で扱うにはどのようにしたら良いのでしょうか
構造体の場合、アロー演算子は番地、ドットが実体だと習いましたがエラーが出てしまいます

③関数の値渡しについてです
case 1:
printf("追加する数字を入力してください\n");
scanf("%d",&a);
insert(a);
break;
case 5:
printf("指定した値の番地を出力します\n");
scanf("%d",&key1);
search(key1);
break;

case 6:
printf("指定した値を削除します\n");
scanf("%d",&key2);
delete(key2);
break;

ここの部分で出力する場合の値と指定した値の変数名はそれぞれ変えたほうが良いですよね?
回答:a,key1,key2は全てint型なので同じにしても良いが、変えておいた方が、良いでしょう。
意味的に異なる変数は、別々に確保したほうが判りやすくなります。

④探索した値の番地を出力する際に、

/* もしキーと注目している節のデータとが等しければポインタを関数として返す */
printf("探索した値の番地です>>%d\n",p->data);
return p;
としていますが、めちゃくちゃな状態です
探索した値の番地を返す場合、 return pはどのように扱えば良いのか悩みます。
回答:p->dataは探索した値のデータです。したがって
printf("探索した値のデータです>>%d\n",p->data);
printf("探索した値の番地(アドレス)です>>%p\n",p);
return p;
とします。
ポインター(p)を印字する場合は %pを使用します。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/12/02 09:50

    丁寧に解説してくださり助かります、誠にありがとうございます!
    参考にさせていただきます!!

    キャンセル

+1

とりあえず、コンパイルエラーをとり、整形しました。
https://paiza.io/ja/projects/new?language=c
でも、確認できます。実行はしていません。
//追加
//変更
//削除
のコメントを入れてます。

#include <stdio.h>
#include <stdlib.h>
/* 木の節の定義 */
typedef struct node {
    int data;                                       /* 探索のキーになるデータ型 */
    struct node *left;                              /* 左の子 */
    struct node *right;                             /* 右の子 */
} NODE;

/* 初期状態では二分探索木は空の状態。
   グローバル変数rootが二分探索木の根へのポインタを保持しているものとする */
/* グローバル変数rootをNULLで初期化 */
NODE *root = NULL;

/* エラーメッセージをプリントしてexitする関数*/
/* ポインタ(アドレス)の先のデータを読み取り専用にする */
void fatal_error(const char *s)
{
    /*  fprintf関数を使ってstderrで標準エラーを出力する */
    fprintf(stderr, "%s", s);
    exit(1);                                        /* 異常終了 */

}

/* 二分探索木を探索する関数 */
NODE *search(int key)
{
    NODE *p;                                        /* 現在注目している節 */
    p = root;                                       /* まず根に注目する */
    /* 進むべき子が存在する限り繰り返す */
    while (p != NULL) {
        /* キーと注目している節のデータが等しいか比較 */
        if (key == p->data) {                       //変更開始
            /* もしキーと注目している節のデータとが等しければポインタを関数として返す */
            printf("探索した値の番地です>>%d\n", p->data);
            return p;
        } else {
            if (key < (p->data))
                /* キーの方が小さければ左部分木に進む */
                p = p->left;
            else
                /* キーの方が大きければ右部分木に進む */
                p = p->right;
        }
    }                                               //変更終了
    printf("NotExist\n");
    return NULL;
}

/* 二分探索木から要素を追加する関数*/
NODE *insert(int key)
{
    //int key;  //削除
    NODE **p, *new;
    /* 変数pが変数rootを指すように初期化する */
    p = &root;
    /* 挿入すべき場所が見つかるまで繰り返す */
    while (*p != NULL) {
        /* キーと注目している節のデータが等しいか比較 */
        if (key == (*p)->data)
            /* すでに登録されている */
            return NULL;
        else if (key < (*p)->data)                  //変更
            /* キーの方が小さければ左部分木に進む */
            p = &(*p)->left;
        else
            /* キーの方が大きければ右部分木に進む */
            p = &(*p)->right;
    }
    /* 挿入されるべき場所が見つかったら */
    /* 挿入される節をつくる */
    if ((new = malloc(sizeof(NODE))) == NULL)       //変更
        fatal_error("out of memory!!");
    /* 新しい節には子がないのでNULLにしておく */
    new->left = NULL;
    new->right = NULL;
    /* 要素の値をセットする */
    new->data = key;
    /* 正しい場所に挿入する */
    /* ポインタpは挿入される節へのポインタが入っている場所を指している */
    *p = new;
    return new;
}

/* 二分探索木から最小の要素を削除する関数 */

NODE *deletemin(NODE ** p)
{
    NODE *x;

    while ((*p)->left != NULL)
        p = &(*p)->left;
    x = *p;
    *p = (*p)->right;
    return x;
}

/* 二分探索木から要素を削除する関数 */
int delete(int key)
{
    //int key;  //削除
    /* 親へのポインタを使う */
    NODE **p, *x;
    /* 変数pが変数rootを指すように初期化する */
    p = &root;
    /* 削除対象となる要素を探す */
    while (*p != NULL) {
        /* 見つかった
           変数pは削除される節の親のメンバleft,right
           変数xは削除される節そのものを指している */
        if (key == (*p)->data) {
            x = *p;
            /* 1つも子を持っていない、葉である場合 */
            if (x->left == NULL && x->right == NULL)
                *p = NULL;
            /* 右の子のみをもつ */
            else if (x->left == NULL)
                *p = x->right;
            /* 左の子のみをもつ */
            else if (x->right == NULL)
                *p = x->left;
            /* 左右2つの子を持つ */
            else {
                /* 部分木から最小の要素を取り去る */
                *p = deletemin(&x->right);
                (*p)->right = x->right;             //変更
                (*p)->left = x->left;               //変更
            }
            /* 取り除いた節を解放させる */
            free(x);
            printf("Done\n");
            return 1;
        } else if (key < (*p)->data)
            /* 左部分木に進む */
            p = &(*p)->left;
        else
            /* 右部分木に進む */
            p = &(*p)->right;
    }
    /* 削除対象が見つからなかった */
    printf("NotExist\n");
    return 0;
}

/* 二分木を行きがけ順でなぞる */
void preorder(NODE * p)                             //変更
{
    /* 木が空なら何もしない */
    if (p == NULL)
        return;
    else {
        printf("%d", p->data);                      /* 自身の値を出力 */
        preorder(p->left);                          /* 左ノードへ移動 */
        preorder(p->right);                         /* 右ノードへ移動 */
    }
}

/* 二分木を通りがけ順でなぞる */

void inorder(NODE * p)                              //変更
{
    /* 木が空なら何もしない */
    if (p == NULL)
        return;
    else {
        inorder(p->left);                           /* 左ノードへ移動 */
        printf("%d", p->data);                      /* 自身の値を出力 */
        inorder(p->right);                          /* 右ノードへ移動 */
    }
}

/* 二分木を帰りがけ順でなぞる */
void postorder(NODE * p)                            //変更
{
    /* 木が空なら何もしない */
    if (p == NULL)
        return;
    else {
        postorder(p->left);                         /* 左ノードへ移動 *///変更
        postorder(p->right);                        /* 右ノードへ移動 */
        printf("%d", p->data);                      /* 自身の値を出力 */
    }
}


int main(void)
{
    int i;
    int n;
    int a, b, c;
    int key;                                        //追加
    int mn = 0;

    printf("二分探索木をします\n");
    do {
        printf
            ("メニューを選んでください\n\n1=追加\n2=行きがけ順\n3=通りがけ順\n4=帰りがけ順\n5=指定した値の番地\n6=削除\n9=終了\n");
        scanf("%d", &mn);
        switch (mn) {


        case 1:
            printf("追加する数字を入力してください\n");
            scanf("%d", &a);
            insert(a);
            break;

        case 2:
            printf("行きがけ順です\n");
            preorder(root);                         //変更
            break;

        case 3:
            printf("通りがけ順です\n");
            inorder(root);                          //変更
            break;

        case 4:
            printf("帰りがけ順です\n");
            postorder(root);                        //変更
            break;

        case 5:
            printf("指定した値の番地を出力します\n");
            search(key);
            break;

        case 6:
            printf("指定した値を削除します\n");
            delete(key);
            break;

        case 9:
            printf("終了します\n");
            break;

        default:
            printf("エラー:メニューの中の数字を入力してください\n");
        }
    } while (mn != 9);

    return 0;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/12/02 10:09

    訂正してくださりありがとうございます、
    変なミスばかりで申し訳ありません
    paizaでエラーを確認します
    ありがとうございます!!!

    キャンセル

  • 2020/12/02 11:40

    上記を参考にし、他の細々としたコンパイルエラーはとれました!
    プログラムコードを編集しました、よろしければご覧下さい

    paizaでは実行できても
    Runtime error(Exit status:153(File size limit exceeded))
    というエラーがでてしまいます。
    オーバーフローしているそうなのですが
    関係しているとしたらmalloc関数ですが
    うまく解放できていないのでしょうか
    それとも
    int delete(int key)
    では
    //ここからif文の書き方を変えました
    のところでif文の書き方を変えたのですが、エラーがでなくて{}の閉じ方が違うのでしょうか??

    EasyIDECでは実行はできましたが削除のみできませんでした

    アドバイスよろしくお願いいたします

    キャンセル

+1

あなたの修正したソースをコピペして、
こちらの環境(Mingw gcc)でコンパイルするとエラーになります。
全角の空白が混じっています。あなたの環境ではエラーになりませんか。
(コンパイラによっては全角空白も半角空白と同じ扱いをするのがあるかもしれませんが)
添付図の黄色の部分です。
イメージ説明

int deleteのみ整形しました。
if (x->left == NULL) {              //ここからif文の書き方変えました
の個所ですが、少し上の行で
if (x->left == NULL && x->right == NULL) {
の判断をしているので、
if (x->left == NULL) {        は必ず成立します。
以前のより悪くなっているような気がします。
インデントをきちんとすれば、わかりやすいかと。

int delete(int key)
{
    /* 親へのポインタを使う */
    NODE **p, *x;
    /* 変数pが変数rootを指すように初期化する */
    p = &root;
    /* 削除対象となる要素を探す */
    while (*p != NULL) {
        /* 見つかった
           変数pは削除される節の親のメンバleft,right
           変数xは削除される節そのものを指している */
        if (key == (*p)->data) {
            x = *p;
            /* 1つも子を持っていない、葉である場合 */
            if (x->left == NULL && x->right == NULL) {
                *p = NULL;
                /* 右の子のみをもつ */
                if (x->left == NULL) {              //ここからif文の書き方変えました
                    *p = x->right;
                    /* 左の子のみをもつ */
                } else if (x->right == NULL) {
                    *p = x->left;
                    /* 左右2つの子を持つ */
                } else {
                    /* 部分木から最小の要素を取り去る */
                    *p = deletemin(&x->right);
                    (*p)->right = x->right;
                    (*p)->left = x->left;
                }
                /* 取り除いた節を解放させる */
                free(x);
                printf("Done\n");
                return 1;

            } else if (key < (*p)->data)
                /* 左部分木に進む */
                p = &(*p)->left;
            else
                /* 右部分木に進む */
                p = &(*p)->right;
        }
    }
    /* 削除対象が見つからなかった */
    printf("NotExist\n");
    return 0;
}

こちらで実行した結果です。
30を追加
20を追加
行きがけ順で表示
20を削除・・・ここで応答なしになるため、Ctrl+Cで強制終了
削除の処理で無限ループしていると思われます。

D:\goo\c>goo1_1
二分探索木をします
メニューを選んでください

1=追加
2=行きがけ順
3=通りがけ順
4=帰りがけ順
5=指定した値の番地
6=削除
9=終了
1
追加する数字を入力してください
30
メニューを選んでください

1=追加
2=行きがけ順
3=通りがけ順
4=帰りがけ順
5=指定した値の番地
6=削除
9=終了
1
追加する数字を入力してください
20
メニューを選んでください

1=追加
2=行きがけ順
3=通りがけ順
4=帰りがけ順
5=指定した値の番地
6=削除
9=終了
2
行きがけ順です
3020メニューを選んでください

1=追加
2=行きがけ順
3=通りがけ順
4=帰りがけ順
5=指定した値の番地
6=削除
9=終了
6
指定した値を削除します
20
^C

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/12/02 17:03

    最新のソースで、既存のソースを更新してください。そのソースをみてみます

    キャンセル

  • 2020/12/02 17:19

    最新のソースに修正しました!
    だらだら長くなるのでメニューは横並びにしました

    キャンセル

  • 2020/12/02 17:52

    次の回答欄に書きました。
    インデントを正しく書けばわかるバグかと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る