電子情報4年情報構造論99年後期中間試験

1.リスト処理の基本

	#include <stdio.h>
	struct List {
	    int          data ;
	    struct List* next ;
	} ;

	#define MALLOC(TYPE) (TYPE*)malloc( sizeof(TYPE) )
	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(A)
	/* 1つのセルを作り要素を代入する */
	struct List* cons( int d , struct List* n ) {
	    struct List* nn ;
	    if ( (nn = MALLOC( struct List )) != NULL ) {
	        nn->data = d ;
	        nn->next = n ;
	    }
	    return nn ;
	}
	/* リストの中からデータを探す */
	int find( struct List* p , int key ) {
	    if ( p == NULL )
	        return 0 ; /* 見つからなかった */
	    else if ( p->data == key )
	        return 1 ; /* 見つかった */
	    else           /* 残りから探す */
	        return find( p->next , key ) ;
	}
	/* 集合和を求める関数 */
	_______ union_set( _______ list1 , ______ list2 )
	{
	    変数リストへのポインタ ans の宣言。
	    ans の初期値は、list2 全体とする。
	    list1 の全要素についての繰り返す。
	        list1 の要素が list2 に含まれていなければ、
	            その要素を ans の先頭に加える。
	    集合和の結果として ans を返す。
	}

	void main()
	{
	    struct List *p1 = cons( 1 , cons( 2 , cons( 3 , NULL ))) ;
	    struct List *p2 = cons( 2 , cons( 4 , cons( 6 , NULL ))) ;
	    struct List *p3 = union_set( p1 , p2 ) ;
	    /* 何らかの処理.... */
	}

数値の集合を扱うために、前述のプログラムを作成した。
この中の集合和を求める関数 union_set() が、未完成である。 プログラム中の説明に従って関数を完成させよ。
関数の戻り値や引数の宣言も含めて記述せよ。

2.リスト処理プログラムの修正

設問1 のリスト処理で作られたリストを 解放する関数として、以下の freeList() を作った。 しかし、正しく動作しなかった。

	void freeList( struct List* p ) {
	    /* リストの全要素を解放する関数のつもり */
	    /*   ただし、正しく動作しない */
	    for( ; p != NULL ; p = p->next )
	        free( p ) ;
	}

動かない理由を説明し、正しいプログラムを示せ。

3.循環リスト

	#include <stdio.h>
	struct List {
	    char*        data ;
	    struct List* next ;
	} ;
	#define MALLOC(TYPE) (TYPE*)malloc( sizeof(TYPE) )

	/* 1つのセルを作り要素を代入する */
	struct List* cons( char* d , struct List* n ) {
	    struct List* nn ;
	    if ( (nn = MALLOC( struct List )) != NULL ) {
	        nn->data = d ;
	        nn->next = n ;
	    }
	    return nn ;
	}
	/* 循環リストに1要素を追加する */
	void insert( struct List* r , char* d ) {
	    struct List* nn ;
	    if ( (nn = MALLOC( struct List ))          ) {
	        nn->data =               ;    ~~~~~~~~(A)
	                   ~~~~~~~~~~~~~(B)
	        nn->next =               ;
	                   ~~~~~~~~~~~~~(C)
	                                 ;
	    }   ~~~~~~~~~~~~~~~~~~~~~~~~(D)
	}
	void printRingList( struct List* p ) {
	    /* 循環リストの全データを表示する関数 */
	    /* ただしリストは必ず1件以上のデータを含む */
	    /* データ件数3だけ正しく動くのは不可 */


	}
	void main()
	{
	    struct List* ring = cons( NULL , NULL ) ;
	    ring->next = ring ;         /* (E) */

	    insert( ring , "Sadako" ) ; /* データを追加 */
	    insert( ring , "Ring2" ) ;
	    insert( ring , "Psyco" ) ;
	    printRingList( ring ) ;     /* リストを全表示 */
	}
  1. 図を参考にして、循環リストに1件データを追加する 関数 insert() の空白部 (A)〜(D) を埋めよ。
  2. 循環リストの全データを表示する関数 printRingList()を作成せよ。
    ヒント:リストの先頭の場所を記憶し、元の場所に戻ったら終了。

4.用語の説明

以下の2つの用語について説明せよ。

  1. 設問1のプログラムの (A) の行での、定義は一般的に、どう呼ばれるか? また、こういった手法を使わない場合と対比しながら、 利点や欠点を交えて説明せよ。
  2. 設問3のプログラムの (E) の行に 見られるような手法は、どういう名前で呼ばれるか?
    および、その様な方法をとる理由を具体的に説明せよ。

5.配列やリストを使ったStack

	#include <stdio.h>
	#define SUCCESS 1
	#define FAIL    0
	/* - - - - - - - - - - - - - - - - - */
	struct StackArray {
	    int  sp ;
	    int  array[ 100 ] ;
	} ;
	void initArray(                    stack ) {
	                ~~~~~~~~~~~~~~~~~~(A)
	    stack->sp = 0 ;
	}
	int pushArray( struct StackArray* stack , int value ) {
	    if ( stack->sp < sizeof( stack->array ) ) {
	        stack->array[           ] =       ;
	                      ~~~~~~~~~(B)  ~~~~~(C)
	        ~~~~~~~~~~~(D)
	        return SUCCESS ;
	    } else {
	        return FAIL ;  /* 配列溢れで失敗 */
		}
	}
	int popArray( struct StackArray* stack , int* ans ) {
	    if ( stack->sp > 0 ) {
	        stack->sp-- ;
	        *ans = stack->array[ stack->sp ] ;
	        return SUCCESS ;
	    } else {
	        return FAIL ;  /* 配列が空で取り出せない */
		}
	}
	/* - - - - - - - - - - - - - - - - - */
	struct List {
	    int          data ;
	    struct List* next ;
	} ;
	struct List* cons( int d , struct List* n ) {
	    struct List* ret
	        = (struct List*)malloc( sizeof( struct List ) ) ;
	    if ( ret != NULL ) {
	        ret->data = d ;
	        ret->next = n ;
	    }
	    return ret ;
	}
	struct StackList {
	    struct List* sp ;
	} ;
	void initList( struct StackList* stack ) {
	    stack->sp = NULL ;
	}

	int pushList( struct StackList* stack , int value ) {
	    struct List* n = cons( value , stack->sp ) ;

	    if ( n != NULL ) {
	        stack->sp = n ;
	        return SUCCESS ;
	    } else {
	        return FAIL ;  /* ヒープ領域が不足 */
		}
	}
	int popList(                   stack ,      ans ) {
	             ~~~~~~~~~~~~~~~~~(E)      ~~~~(F)
	    if ( stack->sp         ) {
	                   ~~~~~~~(G)
	        struct List* del = stack->sp ;

	        stack->sp = (stack->sp)->next ;
	        *ans = del->data ;
	        free( del ) ;
	        return         ;
	    } else     ~~~~~~~(H)
	        return         ;  /* リストが空で失敗 */
	}              ~~~~~~~(I)

	void main() {
	    struct StackArray stack1 ;
	    struct StackList  stack2 ;
	    int x ;

	    initArray( &stack1 ) ;      /* 配列 stack の初期化 */
	    pushArray( &stack1 , 12 ) ;
	    pushArray( &stack1 , 56 ) ;
	    if ( popArray( &stack1 , &x ) )
	        printf( "%d\n" , x ) ;  /* 56 が表示される */
	    if ( popArray( &stack1 , &x ) )
	        printf( "%d\n" , x ) ;  /* 12 が表示される */

	    initList(         ) ;       /* リスト stack の初期化 */
	              ~~~~~~~(J)
	    pushList( &stack2 , 34 ) ;
	    pushList( &stack2 , 78 ) ;
	    if ( popList( &stack2 , &x ) )
	        printf( "%d\n" , x ) ;  /* 78 が表示される */
	    if ( popList( &stack2 , &x ) )
	        printf( "%d\n" , x ) ;  /* 34 が表示される */
	}

(a)配列を使った Stack 構造と、(b)リストを使った Stack 構造を、 同じ使い方の関数を作ることで、データ構造を簡単に切替えることができるよ うにプログラムを記述した。
ただし Stack への追加・取り出しの関数は、配列溢れやメモリ不足での チェックができるように、定数値 SUCCESS,FAILを返す。
プログラム中の下線部(A)〜(J)を埋めよ。