打印全部路径
根据上一个问题,这里还是采用堆栈比较好,纯递归太繁琐了
简单的测试
下面是一个多通道图,S表示起点,E表示终点
初步思考
一个很简单的想法,如果走到了终点那就返回到上一个多分叉口,让多分口走其他分路试一试
所以在到终点回溯时:
- 为了确保该路径以后能重复用,必须重置为0
- 为了确保回溯后再前进时,不会再次走入相同的前进路,还需要专门的方向记录
- 专门的标记,确定点是否有多个前进方向
根据以上条件先写出来伪代码
p 表示当前点,Start表示起点,End 表示终点,next_search()表示下一论迭代搜索
is_multy() 表示是否为多头点,multy_count 表示多头的数量
maze[p], 表示地图maze中p点的值,p_next 表示前进或后退后的达到的点
思路:
前线搜索,如果出现出现多头点,多头点数+1,出现死区就回溯
如果一口气回溯到了起点,那么就说明这是个死地图,没有出路
如果达到终点,但是多头点数量不为0,那么说明可能又其他路线
回溯到上一个多头点,并禁止多头点向已经回溯路径搜索
死区回溯maze[p] = -1,终点回溯maze[p] = 0,前向搜索maze[p] = 2
对于最麻烦的各点搜索方向在死区回溯、终点回溯时如何修改必须,
只能一边分析,一边填补规则
if (p == End) {
if (multy_count == 0) {
printf("search is end, there are not any other ways");
return 0;
} else {
while(!is_multy(p)) {
maze[p] = 0;
pop(p);
}
next_search(p);
}
} else {
multy_count += is_multy(p);
if (is_ahead(p)) {
push(p);
maze[p] = 2;
next_search(p_next);
} else {
if (p == Start) {
printf("there is no way");
return 0;
} else {
pop(p);
maze[p] = -1;
next_search(p_next);
}
}
}
搜索方向的变动
每个点最多有3个前进方向,但是方向的总类型为4个,再加上一个是否为多头的判断,
实际上这都需要额外存储5个数据了,那有没有更低内存消耗的办法?
回想一下之前的2进制表示,我们其实可以用一个4位的2进制数取表示前进方向
0000b == 0表示无前进点
0001b == 1表示
→
\rightarrow
→
0010b == 2表示
↑
\uparrow
↑
0100b == 4表示
←
\leftarrow
←
1000b == 8表示
↓
\downarrow
↓
这样就可以只用一个ahead_statu表示前进方向和是否多头了,
不过对于这个数据需要注意初始化
而在终点回溯的过程中,想要回溯到多头点并禁止其向已经搜索过的方向再次前进
让其前进时方向有序性,这样就可以消除掉第一个前进状态即可
禁止p重复搜索,同时在终点回溯最后强行进入到下一个p_next
同时要注意到 p_statu 应该最先确定,并通过 p_statu 进行前进搜索
还有一个小问题,之前前进时判断条件之一maze[p_next]==0
但是对于这个地图来说
先走
A -0- B -1- C -4- D
终点回溯到C
那么会进入到2,3通道
并把2,3 通道当作死区 置为-1
但是实际终点回溯到B 时,还是能通过2、3通道到终点的
所以前向收缩时只要前向点不为1(障碍)、2(以访问点)即可
即前进点可为0或-1
那么补全全部细节
if (p == End) {
if (multy_count == 0) {
printf("search is end, there are not any other ways");
return 0;
} else {
while(!is_multy(p.statu)) {
maze[p] = 0;
p = pop();
}
p.statu = change_status(p);
if (!is_multy(p))
multy_count -= 1;
p_next = go_ahead(p.statu);
next_search(p);
}
} else {
p.statu = get_statu(p);
multy_count += is_multy(p);
if (p.statu != 0) {
push(p);
maze[p] = 2;
p_next = go_ahead(p.statu);
next_search(p_next);
} else {
if (p == Start) {
printf("there is no way");
return 0;
} else {
p_next = pop(p);
maze[p] = -1;
next_search(p_next);
}
}
}
根据伪码补全程序即可
#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;
unsigned int status;
} stack[512];
int multy_count = 0;
int top = 0;
struct point get_status(struct point p);
struct point go_ahead(struct point p);
int is_multy(struct point p);
struct point pop(void);
void push(struct point p);
void print_maze(void);
void search_path(struct point p);
int main(int argc, char *argv[]) {
struct point p = {
.row = 0,
.col = 0,
.status = 0,
};
search_path(p);
return 0;
}
struct point get_status(struct point p) {
int status = 0;
if (p.row + 1 <= MAX_ROW - 1 && maze[p.row + 1][p.col] == 0)
status = status + 1;
if (p.col + 1 <= MAX_COL - 1 && maze[p.row][p.col + 1] == 0)
status = status + 2;
if (p.row - 1 >= 0 && maze[p.row - 1][p.col] == 0)
status = status + 4;
if (p.col - 1 >= 0 && maze[p.row][p.col - 1] == 0)
status = status + 8;
p.status = status;
return p;
}
int is_multy(struct point p) {
if (p.status == 1 || p.status == 2 || p.status == 4 || p.status == 8)
return 0;
else if (p.status < 0 || p.status >= 15) {
printf("error");
return -1;
} else
return 1;
}
struct point change_status(struct point p) {
int status = p.status;
if (status & 1)
status -= 1;
else if (status & 2)
status -= 2;
else if (status & 4)
status -= 4;
else if (status & 8)
status -= 8;
p.status = status;
return p;
}
struct point go_ahead(struct point p) {
struct point next_p = {.row = p.row, .col = p.col, .status = 0};
int status = p.status;
maze[p.row][p.col] = 2;
if (status & 1)
next_p.row += 1;
else if (status & 2)
next_p.col += 1;
else if (status & 4)
next_p.row -= 1;
else if (status & 8)
next_p.col -= 1;
return next_p;
}
void push(struct point p) { stack[top++] = p; }
struct point pop(void) {
return stack[--top];
}
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 search_path(struct point p) {
struct point next_p;
if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1) {
print_maze();
if (multy_count == 0) {
printf("search is end, there are not any other ways");
return;
} else {
while (!is_multy(p)) {
maze[p.row][p.col] = 0;
p = pop();
}
p = change_status(p);
if (!is_multy(p))
multy_count -= 1;
push(p);
next_p = go_ahead(p);
search_path(next_p);
}
} else {
p = get_status(p);
if (p.status != 0) {
multy_count += is_multy(p);
push(p);
next_p = go_ahead(p);
search_path(next_p);
} else {
if (p.row == 0 && p.col == 0) {
printf("There is no way!");
return;
} else {
maze[p.row][p.col] = -1;
next_p = pop();
search_path(next_p);
}
}
}
}
但是从结果来说说是错误的
2 1 0 0 0
2 1 0 1 0
2 2 2 2 2
-1 1 1 1 2
-1 -1 -1 1 2
**********
2 1 0 0 0
2 1 0 1 0
2 2 2 2 2
-1 1 1 1 2
-1 -1 -1 1 2
**********
会一直重复打印这路径
将中间结果展现出来,过程差不多是
2 1 0 0 0
2 1 0 1 0
2 2 2 2 2
-1 1 1 1 2
-1 -1 -1 1 2
**********
2 1 -1 -1 -1
2 1 -1 1 -1
2 2 2 2 2
-1 1 1 1 0
-1 -1 -1 1 0
**********
2 1 0 0 0
2 1 0 1 0
2 2 2 2 2
-1 1 1 1 2
-1 -1 -1 1 2
**********
2 1 -1 -1 -1
2 1 -1 1 -1
2 2 2 2 2
-1 1 1 1 0
-1 -1 -1 1 0
**********
也就是说在死区回溯的时候,也应该用终点回溯的方式强制转向
同时为了方便理解,可以将1,2,4,8 定义位方向的宏
另外is_multy()
的判断范围弄丢了0
值,无方向当然是非多头了
修改后的程序
#include <stdio.h>
#define MAX_ROW 5
#define MAX_COL 5
#define DOWN 1
#define RIGHT 2
#define UP 4
#define LEFT 8
/*
* 迷宫图, 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;
int status;
} stack[512];
int multy_count = 0;
int top = 0;
struct point get_status(struct point p);
struct point go_ahead(struct point p);
int is_multy(struct point p);
int is_empty(void);
struct point pop(void);
void push(struct point p);
void print_maze(void);
void search_path(struct point p);
int main(int argc, char *argv[]) {
struct point p = {
.row = 0,
.col = 0,
.status = 0,
};
search_path(p);
return 0;
}
/*
* use the status for mark the ahead direction
*/
struct point get_status(struct point p) {
int status = 0;
if (p.row + 1 <= MAX_ROW - 1 &&
(maze[p.row + 1][p.col] == 0 || maze[p.row + 1][p.col] == -1))
status = status + DOWN;
if (p.col + 1 <= MAX_COL - 1 &&
(maze[p.row][p.col + 1] == 0 || maze[p.row][p.col + 1] == -1))
status = status + RIGHT;
if (p.row - 1 >= 0 &&
(maze[p.row - 1][p.col] == 0 || maze[p.row - 1][p.col] == -1))
status = status + UP;
if (p.col - 1 >= 0 &&
(maze[p.row][p.col - 1] == 0 || maze[p.row][p.col - 1] == -1))
status = status + LEFT;
p.status = status;
return p;
}
/*
* use the status to mark the current point is or not have multy dircetions
* only 1 or 0 dircetions will return no(0)
* otherwise return yes(1)
*/
int is_multy(struct point p) {
if (p.status == 1 || p.status == 2 || p.status == 4 || p.status == 8 ||
p.status == 0)
return 0;
else if (p.status < 0 || p.status >= 15) {
printf("error");
return -1;
} else
return 1;
}
int is_empty(void) { return top == 0; }
struct point change_status(struct point p) {
int status = p.status;
if (status & DOWN)
status -= DOWN;
else if (status & RIGHT)
status -= RIGHT;
else if (status & UP)
status -= UP;
else if (status & LEFT)
status -= LEFT;
p.status = status;
return p;
}
struct point go_ahead(struct point p) {
struct point next_p = {.row = p.row, .col = p.col, .status = 0};
int status = p.status;
maze[p.row][p.col] = 2;
if (status & DOWN)
next_p.row += 1;
else if (status & RIGHT)
next_p.col += 1;
else if (status & UP)
next_p.row -= 1;
else if (status & LEFT)
next_p.col -= 1;
return next_p;
}
void push(struct point p) { stack[top++] = p; }
struct point pop(void) {
return stack[--top];
}
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 search_path(struct point p) {
struct point next_p;
if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1) {
/*
* if get the end mark end as 2
* it means that you have pass the end
*/
maze[p.row][p.col] = 2;
print_maze();
if (multy_count == 0) {
/*
* if there are not any multy-head points
* we have travel all the divergent path
* all the ways from start to the end have been found
*/
printf("search is end, there are not any other ways");
return;
} else {
/*
* there may be another divergent path to the end
* so return to the pre multy-head point
*/
while (is_multy(p) != 1) {
maze[p.row][p.col] = 0;
p = pop();
}
/*
* change the multy-head point status
* let it travel another divergent path in force
* if it be a one-head point after changed
* there are no any other divergent paths at this point
*/
p = change_status(p);
if (is_multy(p) != 1)
multy_count -= 1;
push(p);
next_p = go_ahead(p);
search_path(next_p);
}
} else {
p = get_status(p);
if (p.status != 0) {
multy_count += is_multy(p);
push(p);
next_p = go_ahead(p);
search_path(next_p);
} else {
while (is_multy(p) != 1 && !is_empty()) {
maze[p.row][p.col] = -1;
p = pop();
}
/*
* if it returns to the start becase of the death area
* it means there is no path to the end
*/
if (p.row == 0 && p.col == 0) {
printf("There is no way!");
return;
} else {
p = change_status(p);
if (!is_multy(p))
multy_count -= 1;
push(p);
next_p = go_ahead(p);
search_path(next_p);
}
}
}
}
输出结果
2 1 0 0 0
2 1 0 1 0
2 2 2 2 2
-1 1 1 1 2
-1 -1 -1 1 2
**********
2 1 2 2 2
2 1 2 1 2
2 2 2 -1 2
-1 1 1 1 2
-1 -1 -1 1 2
**********
There is no way!
结果可用
最后那个 There is no way! 出现的原因是
在拆路口搞了一个回路,在回路中把自己堵死