トップ->C言語入門

あなたは

人目のC(C++)言語入門受講生です。

C言語入門内検索

目次
C言語入門〜トップ
C++言語入門〜トップ
0. はじめに

1. 基本的な決まり
   1. 書式
   2. 予約語
   3. 構成
   4. データの形と宣言
   5. 定数と変数
   6. 簡単な演算

2. 入出力
   1. printf
   2. scanf
   3. 練習問題1
   4. 1文字入出力
   5. エスケープシーケンス

3. 制御文
   1. 順次構造
   2. 単一分岐
   3. 多重分岐
   4. ケース構造
   5. 反復構造(while)
   6. 反復構造(do)
   7. 反復構造(for)
   8. 補助制御文
   9. 練習問題
   10.練習問題

4. 配列
   1. 配列とは
   2. 配列の宣言と初期化
   3. 配列の使用法
   4. 練習問題
   5. 文字列
   6. 2次配列と多次元配列
   7. 練習問題

5. 演算子
   1. 演算子の種類
   2. インクリメント演算子とデクリメント演算子
   3. 複合代入演算子
   4. ビット演算子
   5. シフト演算子
   6. キャスト演算子
   7. 順次演算子
   8. 条件演算子(三項演算子)
   9. sizeof演算子
   10.演算子の優先順位
   11.式と値
   12.条件式と値
   13.練習問題

6. ポインタ
   1. ポインタとは
   2. 配列とポインタ
   3. 文字列とポインタ
   4. ポインタのメリット

7. 関数
   1. 関数の作り方
   2. プロトタイプ宣言
   3. 配列とポインタ
   4. 値渡しとアドレス渡し
   5. main関数の引数
   6. 練習問題
   7. 標準関数
   8. 再帰関数

8. プリプロセッサ
   1. プリプロセッサとは
   2. #define, #undef
   3. #include
   4. #if
   5. #error、#warning
   6. マクロ
   7. 定義済みマクロ

9. 変数の有効範囲
   1. 変数の種類
   2. グローバル変数の有効範囲
   3. オート変数の有効範囲
   4. スタティック変数の有効範囲

10. 構造体
   1. 構造体とは
   2. 構造体の宣言
   3. 構造体の使用法
   4. 構造体のポインタ
   5. 構造体の構造体
   6. 構造体と関数
   7. 練習問題

11. 共用体
   1. 共用体とは
   2. 共用体の宣言
   3. 共用体の使用法

12. ファイル
   1. ファイル
   2. ファイル構造体
   3. ファイル作成・オープン
   4. ファイル読み込み
   5. ファイル書き込み
   6. ファイルクローズ
   7. ファイルエラー
   8. ランダムアクセス
   9. 標準入出力
   10. 練習問題
   11. ファイルの検索
   12. ファイルの削除
   13. ファイル名変更
   14. ディレクトリ操作

13. 低水準入出力関数
   1. 高水準入出力関数との違い
   2. ファイル作成
   3. ファイルオープン
   4. ファイル読み込み
   5. ファイル書き込み
   6. ファイルクローズ
   7. 標準入出力
   8. ランダムアクセス
   9. ファイルポインタとファイルディスクリプタ
   10. 練習問題

14. データ構造
   1. データ構造とは
   2. データ構造の種類
   3. 線形リスト
     4. 単方向リスト
     5. 双方向リスト
     6. 環状リスト
   7. ベクター
   8. 木
     9. 二分検索木
   10. スタック
   11. キュー

15 標準関数
   1. 文字分類・文字変換
   2. 文字列操作
   3. データ変換
   4. メモリー操作
   5. 数値演算
   6. ファイル操作(高水準入出力関数)
   7. ファイル操作(低水準入出力関数)
   8. プロセス関係

16 関数ポインタ
   1. 関数ポインタとは
   2. 関数ポインタ
   3. 関数ポインタと引数・戻り値
   4. 関数ポインタの配列

17. そしてその後は (PR)
   1. ヒューマンアカデミー C言語講座
   2. el school C言語講座
   3. C++入門


・ このページの先頭に戻る
・ トップページに戻る


・ このページの先頭に戻る
・ トップページに戻る

トップ-> C言語入門:データ構造-> 14-9. 二分検索木

←前ページへ :  トップへ :  次ページへ→


例題

  学籍番号と名前(と左と右の節へのポインタ)からなる構造体(meibo)を定義する。 キーボードからの名簿入力、名簿削除を行えるようにする。また、学籍番号を 入力すると、名前を検索できるようなプログラムを作れ。

  そのほかに、この木を操作する関数群を作れ。関数群は以下の通り。

int size(); 木の要素数を返す
int append(struct meibo newdata); newdataを木に追加する
void removenode(int n); 学籍番号がnの要素を削除する
void removeall(); 木を削除する
struct meibo lookup(int n); 学籍番号がnの要素を検索して返す


解答


#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <conio.h>

// // 構造体の定義 struct meibo{ struct meibo *pleft; struct meibo *pright; int no; char name[20]; };
// // 木操作用変数 struct meibo *root;
// // プロトタイプ宣言 int size(); int append(struct meibo newdata); void removenode(int n); void removeall(); struct meibo lookup(int n); struct meibo input(); void disp(struct meibo data); void clear();
// // メイン関数 void main(){ struct meibo data; int no; char buffer[10]; while(1){ // 画面をクリア clear(); printf("メニュー\n"); printf(" 1 : 入力\n"); printf(" 2 : 検索\n"); printf(" 3 : 削除\n\n"); printf(" 0 : 終了\n\n"); printf("処理を選んでください : "); switch(getche()){ case '1' : data = input(); if ( data.name[0] != '\0' ){ if ( append(data) ) printf("\n\n登録しました\n"); else printf("\n\nすでに登録されています\n"); } else printf("\n\n入力を中止しました\n"); break; case '2': printf("\n\n検索する学籍番号を入力してください : "); gets(buffer); no = atoi(buffer); if ( no > 0 ){ // 検索 data = lookup(no); if ( data.name[0] != '\0' ) disp(data); else printf("\n\n見つかりませんでした\n"); } else printf("\n\n検索を中止しました\n"); break; case '3': printf("\n\n削除する学籍番号を入力してください : "); gets(buffer); no = atoi(buffer); if ( no > 0 ){ // 削除 removenode(no); } else printf("\n\n削除を中止しました\n"); break; case '0': case 0x1B: printf("\n\n終了します\n"); removeall(); return; break; } getche(); clear(); } }
// // 名簿を入力する struct meibo input(){ struct meibo ret; char buffer[10]; int no = -1; // 戻り値用名簿をきれいにする memset(&ret, 0, sizeof(struct meibo)); // 改行 printf("\n\n"); // 学籍番号入力 printf("学籍番号 : "); gets(buffer); no = atoi(buffer); // 学籍番号のチェック if ( no <= 0 ){ return ret; } // 名前入力 printf("氏  名 : "); gets(ret.name); // 名前のチェック if ( ret.name[0] == '\0' ){ return ret; } ret.no = no; return ret; }
// // 名簿を表示する void disp(struct meibo data){ printf("学籍番号 : %d\n", data.no); printf("氏  名 : %s\n", data.name); printf("\n\n"); }
// // 画面をクリアする void clear(){ int i = 0; for ( i = 0 ; i < 30 ; i++ ) printf("\n"); }
// // 要素の数を数える // ptrがNULLでなければ、nを1増やし、左側と右側で再帰的に自分を呼び出す void sizesub(struct meibo *ptr, int *n){ if ( ptr ){ (*n)++; sizesub(ptr->pleft, n); sizesub(ptr->pright, n); } }
// // 要素の数を数える int size(){ int n = 0; sizesub(root, &n); return n; }
// // 要素追加 int append(struct meibo newdata){ struct meibo* tmp; // 登録する要素をコピーする struct meibo* pnewdata = malloc(sizeof(struct meibo)); *pnewdata = newdata; pnewdata->pleft = pnewdata->pright = NULL; // 初めての追加 if ( root == NULL ){ root = pnewdata; return 1; } // 2回目の追加 tmp = root; while( tmp ){ // すでに登録済み if ( tmp->no == newdata.no ){ free( pnewdata ); return 0; } // 左側 if ( tmp->no > newdata.no ){ // 左側にまだ要素がある if ( tmp->pleft ){ tmp = tmp->pleft; continue; } // 左側に登録しよう else{ tmp->pleft = pnewdata; return 1; } } // 右側 else{ // 右側にまだ要素がある if ( tmp->pright ){ tmp = tmp->pright; continue; } // 右側に登録しよう else{ tmp->pright = pnewdata; return 1; } } } // ここに来ることはないはずだ return 0; }
// // つなぎ換えを行う。removenode関数から呼ばれる void tsunagikae(struct meibo *parent, int n){ struct meibo **tmp = NULL; struct meibo *tmp2, *tmp3; if ( parent == NULL ){ // ありえない return; } // 左側が削除する要素? if ( parent->pleft && parent->pleft->no == n ) tmp = &parent->pleft; // 右側が削除する要素? else if ( parent->pright && parent->pright->no == n ) tmp = &parent->pright; // チェック if ( !tmp || !(*tmp) ){ // ありえない return; } // 削除する要素の左側はない? if ( !(*tmp)->pleft ){ // 削除する要素の右側の節を、削除する節の代わりにつなげよう *tmp = (*tmp)->pright; return; } // 削除する要素の右側はない? if ( !(*tmp)->pright ){ // 削除する要素の左側の節を削除する節の代わりにつなげよう *tmp = (*tmp)->pleft; return; } // 削除する要素には左右2つの枝が伸びている tmp2 = (*tmp)->pright; *tmp = (*tmp)->pleft; tmp3 = *tmp; while( tmp3->pright ) tmp3 = tmp3->pright; tmp3->pright = tmp2; return; }
// // 節を削除する void removenode(int n){ struct meibo* tmp; struct meibo* pre; // 根がなければ削除も何もない if ( root == NULL ) return; // 削除は根? if ( root->no == n ){ tsunagikae((struct meibo*)&root, n); return; } tmp = pre = root; while( tmp ){ // 削除はここだ if ( tmp->no == n ){ // ありえない return; } // 左側? if ( tmp->no > n ){ // 左側にもう要素がない? if ( !tmp->pleft ){ // 見つからなかった return; } // 左側は削除する要素? else if ( tmp->pleft->no == n ){ struct meibo *delnode = tmp->pleft; tsunagikae(tmp, n); free( delnode ); return; } // もっと左側を調べる必要がある else{ pre = tmp; tmp = tmp->pleft; continue; } } // 右側? else{ // 右側にもう要素がない? if ( !tmp->pright ){ // 見つからなかった return; } // 右側は削除する要素? else if ( tmp->pright->no == n ){ struct meibo *delnode = tmp->pright; tsunagikae(tmp, n); free( delnode ); return; } // もっと右側を調べる必要がある else{ pre = tmp; tmp = tmp->pright; continue; } } } }
// // removeall関数から呼ばれる関数で、自分以下を削除する // 再帰的に自分を呼び出す void removeallsub(struct meibo* ptr){ if ( ptr ){ removeallsub(ptr->pleft); removeallsub(ptr->pright); free(ptr); } }
// // 全ての要素を削除する void removeall(){ removeallsub(root); root = NULL; }
// // 学籍番号からmeibo構造体を取得する struct meibo lookup(int n){ struct meibo *ptr = root; struct meibo ret; // 戻り値用構造体をきれいにする memset(&ret, 0, sizeof(struct meibo)); while ( ptr ){ if ( ptr->no == n ) return *ptr; // 左側にあるはず if ( ptr->no > n ){ // 左側にあるはずなのに、左側がない? if ( !ptr->pleft ){ // みつからない return ret; } else{ // もっと左を調べる必要がある ptr = ptr->pleft; continue; } } // 右側にあるはず else{ // 右側に張るはずなのに、右側がない? if ( !ptr->pright ){ // 見つからない return ret; } else{ // もっと右側を調べる必要がある ptr = ptr->pright; continue; } } } // ここに来るはずはないんだが・・・ return ret; }


←前ページへ :  トップへ :  次ページへ→