@amanojaku

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

深さ優先探索が動作しない。

解決したいこと

C言語を学習し始めた最中なのですがスタックを用いた深さ優先探索がうまく動きません。最後のマップを出力する一歩手前で止まっています。

発生している問題・エラー

under flow
Stack Underflow.

該当するソースコード

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

#define BUF_SIZE 100
#define SIZE_X 20
#define SIZE_Y 10

int visit[SIZE_X][SIZE_Y];

struct coord {
  int x;  //x座標
  int y;  //y座標
  int px; //一つ前のx座標
  int py; //一つ前のy座標
};

struct stack {
  struct coord buf[BUF_SIZE];
  int top;
};

/*
 * push関数
 * 機能: スタックへの値のpush操作
 * 引数s: 値を挿入するスタック構造体へのポインタ
 * 引数p: 挿入する値(座標)
 * 返却値: pushに成功すれば1, (overflow等で)失敗すれば-1。
 */ 
int push(struct stack *s, struct coord p) {
	if(s->top > BUF_SIZE){
		printf("overflow\n");
		return -1;
	}
	s->buf[s->top] = p;
	s->top++;
	return 1;
}

/* pop関数
 * 機能: スタックからの値のpop操作
 * 引数s: 値を取り出すスタック構造体へのポインタ
 * 返却値: popに成功すればpopした値へのポインタ、(underflow等で)失敗すればNULL。
 */ 
struct coord *pop(struct stack *s) {
	if(s->top == 0){
		printf("under flow\n");
		return NULL;
	}else{
	int n = s->top;
	s->top--;
	return &(s->buf[s->top]);
	}
}

/* display_stack関数
 * 機能: スタックの内部(格納されている値)の表示
 * 引数s: 内部を表示するスタック構造体へのポインタ
 */
void display_stack(struct stack *s) {
	int i = s->top-1;
	while(i >= 0){
		printf("%d %d\n",s->buf[i].x,s->buf[i].y);
		i--;
	}
}

/* read_map関数
 * 機能: ファイルからマップを読み込む
 * 引数map: 読み込んだマップを格納する配列
 */
void read_map(char map[][SIZE_Y]) {
	FILE *fp;
	int ch;
	int i=0;
	int j=0;
	fp =fopen("meiro1.txt","r");
	while((ch = fgetc(fp)) != EOF){
		if(ch == '\n'){
			j++;
			i = 0;
		}else{
			map[i][j] = ch;
			i++;
		}
	}
	fclose(fp);
}

/* print_map関数 
 * 機能: マップを画面に表示する。
 * 引数map: 表示するマップが格納されている配列
 */
void print_map(char map[][SIZE_Y]) {
	for(int j=0;j < SIZE_Y;j++){
		for(int i=0;i < SIZE_X;i++){
			printf("%c",map[i][j]);
		}
		printf("\n");
	}
}

/* record_path関数
 * 機能: 探索経路(探索順路)を記録する。新たなマスを探索する毎に、そのマスを記録するために呼び出すこと。
 * 引数rec: 探索経路を記録する配列
 * 引数x,y: 現在位置の座標
 * 引数px,py: 一つ前の位置の座標
 */
void record_path(struct coord rec[][SIZE_Y], int x, int y, int px, int py) {
  rec[x][y].px = px;
  rec[x][y].py = py;
}

/* print_path関数
 * 機能: 記録した探索経路を基に、SからGまでの経路を表示する。
 * 引数map: 表示するマップの配列。これに経路を示す'.'を書き加える
 * 引数rec: 探索経路が記録された配列
 * 引数gx,gy: ゴールの座標
 */
void print_path(char map[][SIZE_Y], struct coord rec[][SIZE_Y], int gx, int gy) {
  int x, y, tx, ty;

  // ゴールの位置から始める
  x = gx; y = gy;

  // ゴールの前の座標に移動(ゴールに'.'を打たない)
  tx = rec[x][y].px;
  ty = rec[x][y].py;
  x = tx; y = ty; 

  // スタートまで一手ずつ戻りながらmapに'.'を打つ
  while(map[tx][ty] != 'S') {
    map[tx][ty] = '.';
    tx = rec[x][y].px;
    ty = rec[x][y].py;
    x = tx; y = ty; 
  }
  print_map(map);
}

/********************** 
 *  メイン関数
 **********************/
int main(void) {

  /***** 変数の宣言(必要に応じて変数を追加すること)*****/
  char map[SIZE_X][SIZE_Y];  // マップを格納する配列
  struct coord rec[SIZE_X][SIZE_Y]; // 探索経路を記録する配列
  struct coord tmp; // 一時変数として利用可能
  struct coord *p;  // 一時変数として利用可能
  struct stack s;   // 探索に利用するスタック
  int i,j;          // ループ変数
  int x,y,px,py;    // 座標を格納する一時変数
  int gx,gy;        // ゴールの位置を格納

  /***** 各変数の初期化 *****/
  s.top = 0; 
  tmp.x = -1; tmp.y = -1; tmp.px = -1; tmp.py = -1;
  for (i=0; i<SIZE_X; i++) {
    for (j=0; j<SIZE_Y; j++) {
      visit[i][j] = 0;
      rec[i][j] = tmp;
    }
  }

  /***** マップの読み込み *****/
  read_map(map); 

  /***** スタート地点をpush *****/
  tmp.x = 1; tmp.y = 1; tmp.px = -1; tmp.py = -1;
  if (push(&s, tmp) == -1) {
    printf("Stack Overflow.\n");
    return 1;
  }
  /***** 探索処理 *****/
  while(1) {
    // 処理する座標をpop
    p = pop(&s);
    if (p == NULL) {
      printf("Stack Underflow.\n");
      return 1;
    }
    x = p->x, y = p->y; px = p->px; py = p->py; 

		if(map[x][y] == 'G'){
			printf("Goal found:x=%d,y=%d,\n",x,y);
			record_path(rec,x,y,px,py);
			gx = x;
			gy = y;
			break;
		}
		if(map[x][y] == '#'){
			continue;
		}
		if(visit[x][y] ==1) continue;
		visit[x][y];
		
		record_path(rec,x,y,px,py);
		
		tmp.x = x;tmp.y = y-1;tmp.px = x;tmp.py = y;
		if(push(&s,tmp) < 0) return 1;
		
		tmp.x = x-1;tmp.y = y;tmp.px = x;tmp.py = y;
		if(push(&s,tmp) < 0) return 1;
		
		tmp.x = x;tmp.y = y+1;tmp.px = x;tmp.py = y;
		if(push(&s,tmp) < 0) return 1;
		
		tmp.x = x+1;tmp.y = y;tmp.px = x;tmp.py = y;
		if(push(&s,tmp) < 0) return 1;
  }
  /***** 探索経路の表示 *****/
  print_path(map,rec,gx,gy);

}

meiro1.txtの中身

####################
#S    #            #
# ### # ########## #
#     #      #     #
### ### #### #### ##
#     #      #     #
# ####### #### #   #
#  #   #  #    #####
#    #    #       G#
####################

自分で試したこと

print_pathの手前のwhile文でプログラムが終了していたためmapが出力されなかった。

0 likes

2Answer

とりあえず、push関数のoverflowの判定が間違っているので、修正しましょう。
そこさえ直せば、あとは芋づる式に修正できると思います。

2Like

Comments

  1. @amanojaku

    Questioner

    助言有難うございます。改善できました。

@actorbug さんのヒントにて、問題箇所を発見できます。次の3箇所です。

  • 32行目
  • 198行目
  • 4行目

修正コードを掲示した方がよければコメントください。

0Like

Comments

  1. @amanojaku

    Questioner

    自身で考えましたがわかりませんでした。
    可能なら修正コードを掲示して欲しいです。

  2. @amanojaku

    Questioner

    すいません。問題発見し、改善出来ました。
    助言ありがとうございます。

  3. 改善出来て、よかったですね。

    一応、答え合わせとして、修正内容を書いておきます。
    (行位置がずれていました。すみません)

    30行目(32行目と書きましたが30行目の間違いでした)
    -	if(s->top > BUF_SIZE){
    +	if(s->top >= BUF_SIZE){
    
    195行目(198行目と書きましたが195行目の間違いでした)
    -	visit[x][y];
    +	visit[x][y] = 1;
    
    4行目
    -	#define BUF_SIZE 100
    +	#define BUF_SIZE 200
    

    最後のスタックサイズについてですが、
    最大値の検証はしていませんが、今回のmeiro1.txtで140まで到達していました。迷路が20x10のため、200としてみました。


    解決であれば、当Q&Aをクローズしてください。

Your answer might help someone💌