まずは再帰を考えず、データが無いツリーを表示すると考えてみます。
ご提示されている例にはありませんが、恐らく "NULL" とだけ表示されることでしょう。
つまり(関数名はテキトウです)
c
1 void printTree(Node *n) {
2 printf("NULL");
3 }
これだけです。
次に "15" だけが入ったツリーとしましょう。
先の root=NULL を壊さずに実装するとして、
c
1 void printTree(Node *n) {
2 if(n == NULL) {
3 printf("NULL");
4 } else {
5 printf("15(NULL,NULL)");
6 }
7 }
が一番簡単でしょうか。ズルイ? でも、結果は想定通りです。
このコードから始めます。
もちろんこのままでは実際のデータに関係無く "15" や "NULL" になってしまいますので、表示の各部分を置き換えていきます。
まず、 else の表示部分を固定部分と可変部分に分けます。
(以降、else の中だけ表記します。)
c
1 printf("15"); //可変
2 printf("("); //固定
3 printf("NULL"); //可変
4 printf(","); //固定
5 printf("NULL"); //可変
6 printf(")"); //固定
"15" は引数 n からの label に入っていますので置き換えます。
c
1 printf(n->label);
2 printf("(");
3 printf("NULL");
4 printf(",");
5 printf("NULL");
6 printf(")");
最初の "NULL" は同じく n からの left の値で、その次は right です。
c
1 printf(n->label);
2 printf("(");
3 if(n->left == NULL) {
4 printf("NULL");
5 }
6 printf(",");
7 if(n->right == NULL) {
8 printf("NULL");
9 }
10 printf(")");
・・・これでは left も right もそれぞれ NULL じゃない場合に何も出力されません。
では、例えば left が NULL では無い場合、何を表示するのでしょうか。
"left をルートとしたツリー" を表示すれば良いはずです。
恐らくはココが一番のキモと思います。
ここで、「ツリーの一部はまたツリー」を処理する為に再帰を使うことになります。
c
1 printf(n->label);
2 printf("(");
3 if(n->left == NULL) {
4 printf("NULL");
5 } else {
6 printTree(n->left);
7 }
8 printf(",");
9 if(n->right == NULL) {
10 printf("NULL");
11 } else {
12 printTree(n->right);
13 }
14 printf(")");
これで完成・・・ではあるのですが、コードを注意してみると、left や right が NULL の場合の処理は printTree に既に「if(n == NULL) {}」としてあります。
ですのでそれぞれの if 文は省くことが出来ます。(関数全体を表記します。)
c
1 void printTree(Node *n) {
2 if(n == NULL) {
3 printf("NULL");
4 } else {
5 printf(n->label);
6 printf("(");
7 printTree(n->left);
8 printf(",");
9 printTree(n->right);
10 printf(")");
11 }
12 }
もし left が NULL だったら、一番上の "n == NULL" に引っかかって "NULL" が表示されるはずで、 NULL で無かったら else に入って label 等が表示されるはずです。
時には全体(構造)を見("ツリー")、時には目の前のコードだけに集中("printf の分解")してみたりという臨機応変(?)な視点から考えるのも、必要になるかと思います。