再帰呼び出しについて

このQ&Aのポイント
  • C言語のプログラムでテキストからランダムに並べられたアルファベットを二分木の構造に格納し、順番で表示するプログラムについて説明してください。
  • 再帰呼び出しの動きと、hyouji関数の動作について詳しく教えてください。
  • 特定の順番でアルファベットが格納された二分木を表示する関数hyoujiの動きについて説明してください。
回答を見る
  • ベストアンサー

再帰呼び出しについて

C言語のプログラムについてです。 テキストからランダムに並べられたアルファベットを順番にひっぱってきて、 二分木の構造にアルファベットをアルファベット順に格納していき、その順番で表示するプログラムなのですが、 typedef struct rec{ char word[1]; struct rec *left; struct rec *right; }rec; という構造体が定義されており、 二分木構造に(なるように)アルファベットを順番に格納していったあと、 それをアルファベット順に表示していく関数が以下の関数hyoujiなのですが、 (二分木を生成する際にmallocを使用しているので、freeがありますが今回は無視してください。) void hyouji(rec *top){ if(top!= NULL){ hyouji(top->left); printf("%s\n",top->word); hyouji(top->right); free(top); } } この再帰呼び出しの動きがよくわかりません。(つまりはhyoujiの関数の動きがわかりません。) *topは二分探索木の一番上のアドレスを示しています。 wordにアルファベットが格納されています。 たとえば、下のように格納されていたらどのようにhyouji関数は動くのでしょうか。 順番に説明していっていただけたらありがたいです。 (ちなみにプログラム的に下の例のように格納されていきます。 まずtopを生成して、テキストファイルの先頭のアルファベットを入れて、そこから順に 格納していくので、topがaとは限りません。) ........................[b] ..................../...\ .................[a]........[e] .........................../...\ .......................[c].........[g]

質問者が選んだベストアンサー

  • ベストアンサー
  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.3

落ち着いて、よく考えてください。 「見ての通り、hyouji(top->left);の後、hyouji(top->right);の前です。」と書きました。 「top->left全体の表示」の後 「top->right全体の表示」の前 に、top自身だけのprintfです。 これが void hyouji(rec *top){ if(top!= NULL){ hyouji0(top->left); printf("%s\n",top->word); hyouji0(top->right); free(top); } } なら、実行順番わかりますよね?

qwewqwe
質問者

お礼

度重なる質問に、丁寧に答えていただきありがとうございました。 なんとか理解をつめたかったので本当に助かりました。

その他の回答 (4)

  • jjon-com
  • ベストアンサー率61% (1599/2592)
回答No.5

No.4に対して2点補足。 ---------------- 1点目。 (1) 左の人を呼び出す(左の人にバトンを渡す) (2) (左の人から自分にバトンが戻ってきたら)自分のwordを表示する (3) 右の人を呼び出す(右の人にバトンを渡す) (4) 自分を呼び出した呼び出し元にバトンをもどす という箇所の(4)は,(2)の表記に合わせて次のように書いた方がよかったです。 (4) (右の人から自分にバトンが戻ってきたら)自分を呼び出した呼び出し元にバトンをもどす ---------------- 2点目。 (1)○の左は存在しないのでバトンはそのまま という箇所は単純にこう説明したけれど,存在しないかどうか判定しているのは本当は自分ではなく,次のようになります。 ↓左の人を呼び出す ↓呼び出してみたら if (top != NULL) で左の人は存在しないことが判明 ↓そのまま呼び出し元に戻る

  • jjon-com
  • ベストアンサー率61% (1599/2592)
回答No.4

再帰をイメージする際, 「自分自身を呼び出す」のように自分にループして戻ってくるイメージで捉えると,同じ場所をぐるぐる回って何回目の呼び出しか自分でも区別していないイメージになるので, 「自分のクローンを生み出してそれを呼び出す。用が済んだらそのクローンは消える」のように,「自分のコピーという“別人”」を呼び出すイメージで捉えると分かりやすいように思います。 (1) hyouji(top->left); (2) printf("%s\n",top->word); (3) hyouji(top->right); (4) } というコードは(1)→(2)→(3)→(4)の順に処理が進んでいくのだけれど,陸上競技の400mリレー走よろしく,現在その箇所を実行中であることをバトンの有無で示すならば,次のような比喩で説明できます。 (1) 左の人を呼び出す(左の人にバトンを渡す) (2) (左の人から自分にバトンが戻ってきたら)自分のwordを表示する (3) 右の人を呼び出す(右の人にバトンを渡す) (4) 自分を呼び出した呼び出し元にバトンをもどす ........................[b] ..................../...\ .................[a]........[e] .........................../...\ .......................[c].........[g] ですから,上記の二分木の根(root)を起点に,この規則にしたがって木を走査すると,次の流れを上から一つずつ順に追った処理になります。 (0) [現在位置はb] (1) 左のaにバトンを渡す ........(0) [現在位置はa] ........(1)aの左は存在しないのでバトンはそのまま ........(2)「私はa」と表示★ ........(3)aの右は存在しないのでバトンはそのまま ........(4)呼出元bにバトンをもどす (2)「私はb」と表示★ (3) 右のeにバトンを渡す ........(0) [現在位置はe] ........(1)左のcにバトンを渡す ................(0) [現在位置はc] ................(1)cの左は存在しないのでバトンはそのまま ................(2)「私はc」と表示★ ................(3)cの右は存在しないのでバトンはそのまま ................(3)呼出元eにバトンをもどす ........(2)「私はe」と表示★ ........(3)右のgにバトンを渡す ................(0) [現在位置はg] ................(1)gの左は存在しないのでバトンはそのまま ................(2)「私はg」と表示★ ................(3)gの右は存在しないのでバトンはそのまま ................(3)呼出元eにバトンをもどす ........(3)呼出元bにバトンをもどす (4)呼出元にバトンを戻す(一連の処理の終了)

qwewqwe
質問者

お礼

本当にわかりやすい説明ありがとうございました。 助かります!

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.2

再帰呼び出しを理解するコツは、呼び出した先のことは考えないで、機能だけを考える、ということだと思います。 これは、普段からやっていることでもあります。 printfが中でどんな動きをしているか、考えて使ってますか?「指定した書式で出力する」って「機能」だけを考えているのではないでしょうか。 また、同じ行を実行しているので、「(ループのように)戻ってくる」と考えると、混乱の元です。 呼び出されたhyoujiと呼び出したhyoujiは、#1にあるように「機能はまったく同じの、別の関数」だと考えましょう。 void hyouji(rec *top){ if(top!= NULL){ /* top->leftを「hyouji」する。 今実行している「hyouji」ではなく、別の「hyouji」が実行される 中でどうなっているかは、ひとまず置いておく */ hyouji(top->left); /* top->world をprintfで出力 */ printf("%s\n",top->word); /* top->rightを「hyouji」する。 今実行している「hyouji」ではなく、別の「hyouji」が実行される 中でどうなっているかは、ひとまず置いておく */ hyouji(top->right); } } > どのタイミングでPrintfが実行されるのでしょうか。 見ての通り、hyouji(top->left);の後、hyouji(top->right);の前です。 hyouji(top->left);やhyouji(top->right);の中でprintfが実行されているかもしれませんが、それは「別の関数」なので、関係ありません。 > Printfでこけてしまうとはどういうことでしょうか? char word[1]; に格納できる「文字列」は、何文字までだと思いますか? 文字列には、本体の文字列の他に'\0'が必要です。少なくとも、printfの書式%sは、「'\0'で終わる文字列」を必要とします。 あるいは、1文字だけなら、「文字列」である必要はありません。charで文字だけ記憶して、%cを使う、という方法もあります。

qwewqwe
質問者

補足

ただ、そうなるとtop、(top)のleft、(top)のrightが表示されてから、つまり今回の木の例だと[b]、[a]、[e]の順に表示されてしまいそうですが、(実際はabcegとできている)どこで理解が間違えているのでしょうか。 何度も申し訳ありません。 printfがこける に関してはそのとおりです。 指摘ありがとうございます。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

これ, printf でこけるよね. さておき. top が b を指すとしたら ・a を指すポインタを使って hyouji を呼び出す ・printf を実行する ・e を指すポインタを使って hyouji を呼び出す というだけですが.... わかりにくかったら, hyouji をコピーして void hyouji0(rec *top){ if(top!= NULL){ hyouji1(top->left); printf("%s\n",top->word); hyouji1(top->right); free(top); } } void hyouji1(rec *top){ if(top!= NULL){ hyouji2(top->left); printf("%s\n",top->word); hyouji2(top->right); free(top); } } (以下略) としてみたら?

qwewqwe
質問者

補足

回答ありがとうございます。 どのタイミングでPrintfが実行されるのでしょうか。 質問に質問をかさねてすいません。 あと、Printfでこけてしまうとはどういうことでしょうか? どこか不都合な点があったりしますか

関連するQ&A

  • 再帰処理について

    以下のソースコードを用いて二分木を生成して、それをトラバーサルすると 昇順に表示されますが! これを、用いて全ての節の子を(左の子と右の子)を交換する。 関数定義 void ReverseTree( struct BTREE *ptr); を再帰的呼び出しによって実現してのですが! どう考えればよいのでしょうか? さらに、2分木の生成後に入力した整数値がリスト中にあるかどうかを 判定し、(存在すれば1、存在しなければ、0を関数値とする) 関数定義 int ExistTest(struct BTREE *ptr, searchdata); を作成し動作を確認したいのですが! ご教授して下さい。お願いします.

  • C言語で、再帰呼び出しを使用せずに、文字列"(12 + 3) * (

    C言語で、再帰呼び出しを使用せずに、文字列"(12 + 3) * ( 3 * (4 + 5 ))"を、優先順位が低い順に二分木に入れる関数を作成したいのですが・・・。 char str[15] = ""(12 + 3) * ( 3 * (4 + 5 ))";なら char n[100];に n[0] = '*' n[1] = '+' n[2] = '*' n[3] = 12 n[4] = 3 n[5] = 3 n[6] = '+' n[7] = \0 n[8] = \0 n[9] = \0 n[10] = \0 n[11] = \0 n[12] = \0 n[13] = 4 n[14] = 5 (n[15] 以降は\0が格納されています。) というように入れたいのですが関数からその関数を呼び出す再帰を使わずに作成する方法がわかりません。 再帰を使用しなければかなり処理が複雑になるような気がしますがどなたか詳しい方よろしくお願いします。 言語はC言語です。

  • 2分木

    実行すると 「トップノードの値を入力してください。20 ノード[20:深さ0]の左の子の値を入れてください。15 ノード[20:深さ0]の右の子の値を入れてください。40 ノード[15:深さ1]の左の子の値を入れてください。11 ノード[15:深さ1]の右の子の値を入れてください。18 ノード[11:深さ2]の左の子の値を入れてください。/ ノード[18:深さ2]の左の子の値を入れてください。17 ノード[18:深さ2]の右の子の値を入れてください。/           ・ が表示されて2分木のデータを作成するプログラムを作りたいのです。絵で表すと、       20 / \     15 40 / \ /\ 11 18 30 55 / /\ \ 17 25 33 61 のようになります(なんか絵がずれて表示される)。 一応、自分で考えてみたのですが #include<stdio.h> #include<malloc.h> struct data { int kazu; struct data *left; struct data *right; }; main() { struct data *root,*p; root=(struct data *)malloc(sizeof(struct data)); printf("トップノードの値を入れてください:"); scanf("%d",root->kazu); root->left=root->right=NULL; while(1){ p=(struct data *)malloc(sizeof(struct data)); printf("ノード%d,深さ%dの左の子の値を入れてください",root->kazu, scanf("%d", ); printf("ノード%d,深さ%dの右の子の値を入れてください",root->kazu, scanf("%d", ); if( break; なんか全然良くわからないのです。アルゴリズムの本を見て勉強していて、データ構造は理解できたのですが、2分木(グラフ)で詰まってしまいました。 小さい順に数を表示させるとか、そういうのじゃなくて、単にデータを格納するだけです。どうかよろしくお願いします。

  • 二分木のなぞり

    ノードにデータが与えられているものとして帰りがけで二分木をなぞり、ノードに立ち寄った順にデータを書き出すときにそのデータが大きい順に並んでいるか判定するプログラムを作りたいです。 typedef struct node Node; struct node{ int data; Node* left; Node* right; }; int flag; void postorder(Node * p) { int kakunou; if( p == NULL ){ return 0 ; } postorder( p->left ); postorder( p->right );      kakunou=p->data;      if(kakunou<p->data){      flag=1; } printf( "%2d ", p->data ); } そこで上のようにkakunouにひとつ前に出力したp->dataを格納し、kakunouとp->dataを比べた時にkakunouの方が小さければflag(グローバル変数)を1にしmain関数でflagが1なら、"大きい順ではない"と出力しようと思うのですが、kakunouとp->dataの値が同じになってしまいます。 どのタイミングでkakunou=p->data;を入れればよいのか、また根本的に考え方がおかしいなら方針を教えていただきたいです。よろしくお願いします。

  • PHPでの再帰を用いたツリー構造について

    PHPを勉強中の初心者です。PHPで、ツリー構造を再帰関数を用いて実装するプログラムを作成し、そのツリーを表示しようとているのですが、どうもよくわかりません。 このプログラムの挙動としては、以下でクリエイトしたTreeオブジェクトを、preorder(トップダウン、左側から)で出力させるもので、期待値は以下のとおりです。 (期待値) preorder: 1 2 4 5 3 6 7 (クリエイトされたオブジェクト) $myTree = new Tree(1, new Tree(2, new Tree(4), new Tree(5)), new Tree(3, new Tree(6), new Tree(7))); =========サンプルプログラム================================ #!/usr/bin/env php <?php class Tree { var $top; var $left; var $right;     #コンストラクタを定義はこれであっているでしょうか。 function Tree($top, $left, $right){ $this ->top = $top; $this ->left = $left; $this ->right= $right; }; function preorder( ){         #preorder() メソッドを実装方法がよくわかりません。 } } $myTree = new Tree(1, new Tree(2, new Tree(4), new Tree(5)), new Tree(3, new Tree(6), new Tree(7))); function printPreorder($tree) { echo "preorder:\n"; $tree->preorder(create_function('$v', 'echo "$v\n";')); } printPreorder($myTree); ?>

    • 締切済み
    • PHP
  • C言語 2分木探索について質問です

    C言語初心者です。 2分木構造体 struct node{ int data; Tree left_subtree; Tree right_subtree; } を上記のように定義した場合、 2分木の根節点のポインタ struct node *Tree を引数として与えられたとき、 2分木内の節点に保持された整数データの総和を戻り値として返す関数 int sum_data(Tree t);を再帰呼び出しで作成してみたのですが、正しいでしょうか? ■2分木内の節点に保持された整数データの総和を戻り値として返す関数 int sum_data(Tree t) {     // 左分木、右分木のどちらも存在しなければ根節点の値を返す     if ((t->left_subtree == NULL) && (t->right_subtree == NULL))      {      return t->data;    }     else// どちらか存在していれば再帰呼び出しする    {      return (t->data + sum_data(t->left_subtree) + sum_data(t->right_subtree))    } } ご指摘ありましたら、お願いしますm(_ _)m

  • アルゴリズムの問題です・・・

    大学で出された問題なのですが、どうしてもわからなかったのでみなさんの力をお貸しください。 以下が問題内容です。 各節点のラベルが1文字のアルファベット(小文字のみで最大26文字,重複は無いものと仮定してよい)である二分木を実現するデータ構造を構築し、与えられた二分木に対してメニューにより「前順走査」、「間順走査」おるいは「後順走査」を選択して、その結果を出力するプログラムのフローチャートをサブルーチンも含めて全て書け。また設計したプログラムを作成せよ。

  • アルゴリズム

    フローチャート作成 各節点のラベルが1文字のアルファベット(小文字のみで最大26文字,重複は無いものと仮定してよい)である二分木を実現するデータ構造を構築し、与えられた二分木に対してメニューにより「前順走査」、「間順走査」おるいは「後順走査」を選択して、その結果を出力するプログラムのフローチャートをサブルーチンも含めて全て書く。 プログラム作成 上で設計したプログラムを作成する。 実行結果 上で作成したプログラムの実行し、その実行結果を示す。

  • 自己参照構造体を使った2分探索用ファイル処理

    大学での明日の課題なのですが、昇順にソートされている単語ファイルを2分探索できるようにツリー型に自己参照構造体に格納する方法がわかりません。構造体の配列に一度入れてからならできるのですが意味がありません。またファイル処理でソートされている単語を50音順に読んでくるので、自己参照構造体に入れる順序が複雑になります。最初にある単語をキーにするとただの長い構造体になってしまいます。説明不足かも知れませんがよろしくお願いします。 <構造体の宣言> struct dictionary{ char *tango struct dictionary *small; struct dictionary *big; } 構造体の中身の順[]内はデータの読み込む順     [0]     /   [1]   / \ [3]   [2]  \    [4]   \   /    \[5]      \[6]

  • アルゴリズム 教えてください

    フローチャート作成 各節点のラベルが1文字のアルファベット(小文字のみで最大26文字,重複は無いものと仮定してよい)である二分木を実現するデータ構造を構築し、与えられた二分木に対してメニューにより「前順走査」、「間順走査」おるいは「後順走査」を選択して、その結果を出力するプログラムのフローチャートをサブルーチンも含めて全て書く。 プログラム作成 上で設計したプログラムを作成する。 実行結果 上で作成したプログラムの実行し、その実行結果を示す。 フローチャートとプログラムの書き方を教えてください

専門家に質問してみよう