<問題> 配列による実装例。教科書とプログラムを読み、自分なりのコメント文をつけて解読してみてください。 (プログラムは、教科書p.31の2.12から17までの解答をまとめたもの)
main 関数は以下。授業中の例題を実行。[c], [c,v], などと変化する。 print_array関数呼び出しを追加し、リスト構造を表現している2つの配列の変化を説明しよう。(実行例は問2.11) int main(void){ printf("(initialized)\n"); initialize(); print_array(0, MAX-1); /* 初期状態をプリント */ printf("(insert 1 a)\n"); insert(next[0],1,'a'); print_array(0, MAX-1); /* 状態をプリントして確認 */ access(next[0], 1); /* このaccess関数は、1番目の要素をreturn。ここでは戻り値 を表示するなどしていない */ printf("(insert 1 c)\n"); insert(next[0],1,'c'); print_array(0, MAX-1); printf("(insert 2 v)\n") ; insert(next[0],2,'v'); print_array(0, MAX-1); printf("(delete 1)\n") ; delete(next[0],1); print_array(0, MAX-1); printf("(insert 2 a)\n") ; insert(next[0],2,'a'); print_array(0, MAX-1); printf("(delete 3)\n") ; delete(next[0],3); print_array(0, MAX-1); /* empty関数使用の例 先頭から、1つずつプリントして削除する。手繰られているイメ ージ while(!empty()) { printf("%c",access(next[0],1)); delete(next[0],1); } printf("\n"); */ return(0); }
配列によるリストの表現について、コメント:
<実行例> initialze関数、insert関数、delete関数のプログラムを読んで、これらの実行例が、何をどのように表しているのか、説明しなさい。 (initiaizeだけの状態。これは空リストを表しているはずだが、どうしてか) next[0] = 1: data[0] = next[1] = 0: data[1] = next[2] = 3: data[2] = next[3] = 4: data[3] = next[4] = 5: data[4] = next[5] = 6: data[5] = next[6] = 7: data[6] = next[7] = 8: data[7] = next[8] = 9: data[8] = next[9] = 2: data[9] = (以下、この表現にコメント) 上を書きなおすと、 添え字 0 1 2 3 4 5 6 7 8 9
next 1 0 3 4 5 6 7 8 9 2
data (dataはすべて \0)
実行例の一部 ([a]となっているはず。insert(next[0], 1,'a')に実行で、どのように変化するか そのプログラムは、どうなっているのかを読むこと) next[0] = 1: data[0] = next[1] = 3: data[1] = next[2] = 4: data[2] = next[3] = 0: data[3] = a next[4] = 5: data[4] = next[5] = 6: data[5] = next[6] = 7: data[6] = next[7] = 8: data[7] = next[8] = 9: data[8] = next[9] = 2: data[9] = (途中[c,v]だったリストが、deleteによって[v]になり、最後には[v,a]になっている。 何がどのように変化していったのかを説明しよう) next[0] = 1: data[0] = next[1] = 5: data[1] = next[2] = 7: data[2] = next[3] = 0: data[3] = next[4] = 5: data[4] = next[5] = 6: data[5] = v next[6] = 0: data[6] = a next[7] = 8: data[7] = next[8] = 9: data[8] = next[9] = 2: data[9] =
解答例 #include <stdio.h> #define MAX 10 void print_array(int from, int to); char access(int p, int i); int newcell(void); void insert(int p, int i, char x); void delete(int p, int i); void initialize(void); int empty(void); int next[MAX]; /* 次のセルのポインタ */ char data[MAX]; /* データ */ void initialize (void) { int i; for (i = 0 ; i < MAX; i++) { next[i]=i+1; /* 次の要素を指すように初期化 :/ data[i]='\0'; /* データはヌル文字で初期化 */ } next[1]=0; /* next[1]がリストのヘッダ ヘッダーnext[1]にはNULL(nil) */ next[MAX-1]=2; /* next[9]は、フリーリストのヘッダの格納 フリーリストヘッダは、next[2] */ } /* 現在のポインタから、i回ポインターを辿って要素を取り出す */ char access (int p, int i) { if (next[p] != 0) { /* 再帰 */ if (i > 1) { /* 再帰条件 */ return (access(next[p],i-1)); /* 次のポインターから、i-1回 */ } else { /* 停止条件 */ return (data[next[p]]); } } else { /* i回辿れない。NULLポインターになった */ printf("List L ends before arriving "); printf("at the position.\n"); return('\0'); } } /* セルを1つ用意。何をする、と書いてあるのか */ /* ヒントなしに読み取るのは、なかなか大変かもしれない */ int newcell (void) { int new; /* 確保できたセル */ if (next[next[MAX-1]] != MAX-1) { /* まだセルがあるという条件のはず *//* 末尾のセルを指していない */ new = next[next[MAX-1]]; /* next[2]がポイントしているセル */ delete (next[MAX-1],1); /* ここがキモ。フリーリストの先頭セルをdelete */ return(new); } printf("List overflows.\n"); return(MAX); } /* pの前に新セル挿入 */ void insert (int p, int i, char x) { int q; if (p != 0) { if (i > 1) { /* 再帰条件 */ insert(next[p],i-1,x); } else { /* 停止条件 */ q = newcell(); /* 新セルを1つ */ data[q] = x; /* そのセルのデータ部にx */ next[q] = next[p]; /* 自分は後ろを指すように */ next[p] = q; /* 前は自分を指すように */ } } else { printf("List L ends before arriving "); printf("at the position.\n"); } } void delete (int p, int i) { if (next[p] != 0) { if(i > 1) { delete (next[p],i-1); } else { data[next[p]]='\0'; next[p]=next[next[p]]; } } else { printf("List L ends before arriving "); printf("at the position.\n"); } } /* 全体が現在空リストを表しているのかを判定する関数 YESなら1 */ /* 下の実行例ではコメントアウトして、使っていない。教科書 p.31 問2.17の答え */ int empty (void) { if (next[next[0]] == 0) { /* next[0]がポイントしているセルのポインターがNULL */ /* main関数の最初のinititializeでは、next[0]は1. next[1]は0 */ return(1); } return(0); } /* 定番の配列プリント用 リストとしてはプリントしていない。配列をそのままプリント */ void print_array(int from, int to) { int i; for (i = from ; i <= to; i ++) printf("next[%d] = %3d: data[%d] = %c \n",i,next[i],i, data[i] ); printf("\n\n"); } int main(void){ printf("(initialized)\n"); initialize(); /* next[MAX]. data[MAX]の初期化 */ /* next[0] が1。next[1]がリストのヘッダー。next[1] = 0なので、最初は空リスト next[MAX-1]が2, next[2]がフリーリストのヘッダー */ print_array(0, MAX-1); /* その状態をプリントして、確認 */ printf("(insert 1 a)\n"); insert(next[0],1,'a'); print_array(0, MAX-1); /* 状態をプリントして確認 */ access(next[0], 1); /* このaccess関数は、1番目の要素をreturn。ここでは戻り値 を表示するなどしていない */ printf("(insert 1 c)\n"); insert(next[0],1,'c'); print_array(0, MAX-1); printf("(insert 2 v)\n") ; insert(next[0],2,'v'); print_array(0, MAX-1); printf("(delete 1)\n") ; delete(next[0],1); print_array(0, MAX-1); printf("(insert 2 a)\n") ; insert(next[0],2,'a'); print_array(0, MAX-1); printf("(delete 3)\n") ; delete(next[0],3); print_array(0, MAX-1); /* empty関数使用の例 先頭から、1つずつプリントして削除する。手繰られているイメ ージ while(!empty()) { printf("%c",access(next[0],1)); delete(next[0],1); } printf("\n"); */ return(0); }