test_12_3_3

递归替代堆栈实现走迷宫

根据堆栈实现迷宫的前向查找分容易,但是错误回溯是个问题

第一次思考

最开始我想到很简单,假设死路终点回退,
怎么判断死路终点呢?只有当前这点为0,周围要么到了边界,要么非0,那么就置当前点为死路顶点。
将死路顶点标记-1, 再以死路顶点为输入根据路线递归倒退即可。
倒退规则:

  1. 当前点为-1
  2. 当前点周围全为非0
  3. 与当前点相邻的标记为2的块即为倒退点,将其标记为-1,下一轮迭代。
0000
0000
0111
0000

以左下为起点,右上为终点,假设查找顺序为 → \rightarrow ↑ \uparrow ← \leftarrow ↓ \downarrow , 那么路径图就是

0000
0000
0111
2000
0000
0000
0111
2200
0000
0000
0111
2200
0000
0000
0111
2220
0000
0000
0111
2222
0000
0000
0111
222-1
0000
0000
0111
22-1-1
0000
0000
0111
2-1-1-1
0000
0000
0111
2-1-1-1
0000
0000
0111
-1-1-1-1
0000
0000
2111
-1-1-1-1

问题就出在回溯的最后一步,他会把死区入口点也当作死区,导致这个问题的源头在于我手动将死区终点赋值为1,打断了递归,相当于在死区内又用另外一个递归
这个事情最大的教训就是递归就得一条顺下来,越多的手动重置就会产生越多的后续调整

第二次思考

根据上一个教训的反思,修改一下一路顺下来


递归输入点 A
	if A 有出路点 B
		YES 
			A 置 2
			递归输入点 B
		No
			A 置 -1
			递归输入 A 的上一点 C

但是,死区如何输入 A 的上一点 C ?
如果采用一个数据存储上一点,由于递归的特性,压根没有节省内存的意义
但是好像可以不需要存储,只需要前进和后退存在某种关系即可

地图

00000
01111
01000
01000
00000

以左下为起点,右上为终点,假设查找顺序为 → \rightarrow ↑ \uparrow ← \leftarrow ↓ \downarrow , 那么前进路径图就是

00000
01111
01 ↓ \downarrow ← \leftarrow ← \leftarrow
01 → \rightarrow -1 ↑ \uparrow
→ \rightarrow → \rightarrow → \rightarrow → \rightarrow ↑ \uparrow

而必要的回溯路径图就是

00000
01111
01 → \rightarrow → \rightarrow ↓ \downarrow
01 ↑ \uparrow ← \leftarrow ↓ \downarrow
2 ← \leftarrow ← \leftarrow ← \leftarrow ← \leftarrow

设置这个迷宫图是因为死区终点情况最复制,可以从4个方向返回,但是为了包装回溯的正确性,必须是 左向优先级最高,同时得到的第二个信息,前进是顺时针,回溯就是逆时针。

根据这两个信息和原始的前进顺序对比,有个猜想

  1. 回溯的最高优先级必定与前进的最高优先级相反
  2. 如果前进时顺时针的,那么回溯必须时逆时针的

根据这两条规则,得到的回溯顺序为 ← \leftarrow ↑ \uparrow → \rightarrow ↓ \downarrow
回溯的顺序时符合这个要求的,但是为了检测这个猜想的正确性,再设计一个地图验证一下

00000
01111
00000
01110
00100
00100
00101

以左下为起点,右上为终点,假设前进查找顺序为 → \rightarrow ↑ \uparrow ← \leftarrow ↓ \downarrow , 对于的回溯顺序 ← \leftarrow ↑ \uparrow → \rightarrow ↓ \downarrow ,那么前进路径图就是

00000
01111
→ \rightarrow → \rightarrow → \rightarrow → \rightarrow ↓ \downarrow
↑ \uparrow 111 ↓ \downarrow
↑ \uparrow ← \leftarrow 1 ↓ \downarrow ← \leftarrow
0 ↑ \uparrow 1 → \rightarrow -1
→ \rightarrow ↑ \uparrow 101

这个地图比较奇怪的一点是走到了死区终点,居然在整个大死区内还有一个未访问点,安装前进与回溯的要求,回溯部分区域后会再次访问未访问点

00000
01111
22222
21112
22122
021 ↓ \downarrow (2) ← \leftarrow
221-11

注意到那个点可以前进访问0值点,所以被置2,后续回溯过程

00000
01111
22222
21112
22122
021 ↓ \downarrow (2) ← \leftarrow
221-11

后续的回溯

00000
01111
2 ← \leftarrow 2 ← \leftarrow 2 ← \leftarrow ← \leftarrow
2111 ↑ \uparrow
221 → \rightarrow ↑ \uparrow
021 ↑ \uparrow -1
221 ↑ \uparrow 1

明显是可以的

程序设计

使用递归的话就取消了堆栈,没必要定义堆栈了出栈入栈了

/*
 *  深度优先算法解决迷宫问题

 *  直接使用堆栈存储路线即可
 */

#include <stdio.h>

#define MAX_ROW 5
#define MAX_COL 5

/*
 * 迷宫图, 1表示障碍, 0表示路径, 只允许上下左右移动
 */
int maze[MAX_ROW][MAX_COL] = {
    0, 1, 0, 0, 0, //
    0, 1, 0, 1, 0, //
    0, 0, 0, 0, 0, //
    0, 1, 1, 1, 0, //
    0, 0, 0, 1, 0, //
};

struct point {
  int row;
  int col;
};

void print_maze(void) {
  for (int i = 0; i < MAX_ROW; i++) {
    for (int j = 0; j < MAX_COL; j++)
      printf("%d\t", maze[i][j]);
    printf("\n");
  }
  printf("**********\n");
}

void recurrent_search(struct point p) {

  struct point p_next;

  if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1) {
    maze[p.row][p.col] = 2;
    print_maze();
    return;
  } else {
    /* go ahead */
    if (maze[p.row][p.col] != 1 &&
        ((p.row + 1 <= MAX_ROW - 1 && maze[p.row + 1][p.col] == 0) ||
         (p.row - 1 >= 0 && maze[p.row - 1][p.col] == 0) ||
         (p.col + 1 <= MAX_COL - 1 && maze[p.row][p.col + 1] == 0) ||
         (p.col - 1 >= 0 && maze[p.row][p.col - 1] == 0))) {
      /* mark the current point 1 */
      maze[p.row][p.col] = 2;
      print_maze();
      if (p.row + 1 <= MAX_ROW - 1 && maze[p.row + 1][p.col] == 0) {
        p_next.row = p.row + 1;
        p_next.col = p.col;
        recurrent_search(p_next);
      } else if (p.col + 1 <= MAX_COL - 1 && maze[p.row][p.col + 1] == 0) {
        p_next.row = p.row;
        p_next.col = p.col + 1;
        recurrent_search(p_next);
      } else if (p.row - 1 >= 0 && maze[p.row - 1][p.col] == 0) {
        p_next.row = p.row - 1;
        p_next.col = p.col;
        recurrent_search(p_next);
      } else if (p.col - 1 >= 0 && maze[p.row][p.col - 1] == 0) {
        p_next.row = p.row;
        p_next.col = p.col - 1;
        recurrent_search(p_next);
      }
    } else if (maze[p.row][p.col] != 1 &&
               (p.row + 1 > MAX_ROW - 1 || maze[p.row + 1][p.col] != 0) &&
               (p.row - 1 < 0 || maze[p.row - 1][p.col] != 0) &&
               (p.col + 1 > MAX_COL - 1 || maze[p.row][p.col + 1] != 0) &&
               (p.col - 1 < 0 || maze[p.row][p.col - 1] != 0)) {
      /* go back */
      maze[p.row][p.col] = -1;
      print_maze();
      if (p.row - 1 >= 0 && maze[p.row - 1][p.col] == 2) {
        p_next.row = p.row - 1;
        p_next.col = p.col;
        recurrent_search(p_next);
      } else if (p.col + 1 <= MAX_COL - 1 && maze[p.row][p.col + 1] == 2) {
        p_next.row = p.row;
        p_next.col = p.col + 1;
        recurrent_search(p_next);
      } else if (p.row + 1 <= MAX_ROW - 1 && maze[p.row + 1][p.col] == 2) {
        p_next.row = p.row + 1;
        p_next.col = p.col;
        recurrent_search(p_next);
      } else if (p.col - 1 >= 0 && maze[p.row][p.col - 1] == 2) {
        p_next.row = p.row;
        p_next.col = p.col - 1;
        recurrent_search(p_next);
      }
    }
  }
}

int main(int argc, char *argv[]) {
  struct point p = {0, 0};

  recurrent_search(p);

  return 0;
}

但是使用递归的话就无法保留路径了,除非说从终点再次逆序递归打印到起点,很麻烦着实很麻烦

第二个问题就是写这几个函数的时候很多重复的工作,有没有那种方式能减少这种工作量?OOP吗

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值