遍历以二维数组存储的图

==主题:遍历以二维数组存储的图==

给出如下的图:
0 0
0 0
0 0
这是一个3行2列的图,用二维数组map[2][3]存储,规定:数组的第1个下标表示行,第2个下标表示列,
假设起点是map[0][0]点,即左上角,终点是map[2][1]点,即右下角,回答下列问题:

1.从起点出发,遍历整个图,但只遍历一次,按每个点被遍历的顺序,依次打印出这些点:
-----Code begin-----
#include <stdio.h>

/*
变量说明:
C语言中,具有文件作用域的变量都是静态存储类别,编译器会自动给它们赋默认值,int类型的默认值是0
map:存储图
book:标记图中的哪个点已经走过,1表示已经走过,0表示还未走过
recordY:保存已经走过的点的Y坐标(所在行)
recordX:保存已经走过的点的X坐标(所在列)
next:方向数组,用于指定下一步去往的点的坐标,方向的顺序是:上、右、下、左
*/
int map[3][2];
int book[3][2];
int recordY[6];
int recordX[6];
int next[4][2]={{-1,0},{0,1},{1,0},{0,-1}};

void dfs(int y, int x, int step){
int i,tx,ty;
if (step==5){ //遍历结束,因为整个图一共6个点,起点的step是0,所以遍历的最后一个点的step是5
printf("Traverse complete\n");
for (i=0;i<=step;i++){ //打印出保存在recordY和recordX中的点的坐标
printf("step: %d, at y=%d and x=%d\n",i,recordY[i],recordX[i]);
}
return;
}
for (i=0;i<4;i++){ //还没遍历完,继续枚举各个方向并进行下次递归
ty=y+next[i][0]; //下个点所在的行
tx=x+next[i][1]; //下个点所在的列
if (tx>-1 && tx<2 && ty>-1 && ty<3 && book[ty][tx]!=1){ //这个点必须在图内,且没有被标记
book[ty][tx]=1; //标记这个点
recordY[step+1]=ty; //保存这个点所在的行
recordX[step+1]=tx; //保存这个点所在的列
dfs(ty,tx,step+1); //对这个点继续DFS
}
}
}

int main(){
book[0][0]=1; //从起点出发,所以起点已经走过了,把它标记为1
recordY[0]=0; //先把起点所在的行保存下来
recordX[0]=0; //并把起点所在的列保存下来
dfs(0,0,0); //从第0行,第0列,即起点,开始遍历图,当前步数是0

return 0;
}
-----Code end-----
输出:
Traverse complete
step: 0, at y=0 and x=0
step: 1, at y=0 and x=1
step: 2, at y=1 and x=1
step: 3, at y=2 and x=1
step: 4, at y=2 and x=0
step: 5, at y=1 and x=0

Process returned 0 (0x0) execution time : 0.623 s
Press any key to continue.

2.从起点出发,遍历整个图,要求得到全部的遍历结果,每种遍历结果都要打印出来:
只需加一行代码在(1)中的 "dfs(ty,tx,step+1); //对这个点继续DFS" 的后面:
book[ty][tx]=0; //取消对这个点的标记,以便其他遍历能访问这个点
输出:
Traverse complete
step: 0, at y=0 and x=0
step: 1, at y=0 and x=1
step: 2, at y=1 and x=1
step: 3, at y=2 and x=1
step: 4, at y=2 and x=0
step: 5, at y=1 and x=0
Traverse complete
step: 0, at y=0 and x=0
step: 1, at y=0 and x=1
step: 2, at y=1 and x=1
step: 3, at y=1 and x=0
step: 4, at y=2 and x=0
step: 5, at y=2 and x=1
Traverse complete
step: 0, at y=0 and x=0
step: 1, at y=1 and x=0
step: 2, at y=2 and x=0
step: 3, at y=2 and x=1
step: 4, at y=1 and x=1
step: 5, at y=0 and x=1

Process returned 0 (0x0) execution time : 0.327 s
Press any key to continue.

PS:发现,最终的遍历结果只有3种,而不是预想的4种,少了下面这种,
从起点出发,先去y=1,x=0点,再去y=1,x=1点,再去y=0,x=1点,这时,对y=0,x=1点进行所有方向的枚举都是失败的,往上走超出了边界,往右走也超出了边界,往下走的点已经被标记,往左走的点也已经被标记,这时,枚举方向的for循环结束,整个函数也随之结束,递归返回,又回到了y=1,x=1点,此时的step值是2,再去y=2,x=1点,再去y=2,x=0点,而这个点已经是访问的最后一个点了,可是此时的step值是4,不满足等于5的条件,进不去if语句,然后进入了for循环,但是没有一次循环能符合内部的if条件,最后for循环结束,整个函数也结束,递归开始返回,最终导致这次遍历的结果没有被打印,
这也是在所有4种遍历结果中,唯一一个在中途就发生递归返回的特例,其余的3种都是在访问最后一个点时才发生递归返回,这直接导致step值少1,必然输出不了这次的遍历结果,其实,这次遍历结果的路径不是一条简单路径,因为y=1,x=1点重复了,路径是:(0,0)->(1,0)->(1,1)->(0,1)->(1,1)->(2,1)->(2,0),点(1,1)重复出现了,
最后,要解决这个问题,需要改进这个程序。

扫描二维码关注公众号,回复: 9067738 查看本文章

3.规定,只能从起点的右侧出发(即起点的下一步必是(y=0,x=1)),只遍历一次,打印遍历结果:
只需修改(1)的main函数如下:
int main(){
book[0][0]=1; //从起点出发,所以起点已经走过了,把它标记为1
recordY[0]=0; //先把起点所在的行保存下来
recordX[0]=0; //并把起点所在的列保存下来
book[0][1]=1; //规定了只能从起点的右侧出发,那么下个点一定是y=0,x=1
recordY[0]=0; //第1步的Y坐标
recordX[1]=1; //第1步的X坐标
dfs(0,1,1); //从第0行,第0列,开始遍历图,当前步数是1

return 0;
}
输出:
Traverse complete
step: 0, at y=0 and x=0
step: 1, at y=0 and x=1
step: 2, at y=1 and x=1
step: 3, at y=2 and x=1
step: 4, at y=2 and x=0
step: 5, at y=1 and x=0

Process returned 0 (0x0) execution time : 0.967 s
Press any key to continue.

4.规定,只能从起点的右侧出发,打印所有的遍历结果:
只需在(3)的基础上,类似于(2),每次遍历之后取消标记那个点

5.规定,只能从起点的下方出发,只遍历一次,打印遍历结果:
等同于(3),只需要这样修改main函数:
book[0][0]=1; //从起点出发,所以起点已经走过了,把它标记为1
recordY[0]=0; //先把起点所在的行保存下来
recordX[0]=0; //并把起点所在的列保存下来
book[0][1]=1; //规定了只能从起点的下方出发,那么下个点一定是y=1,x=0
recordY[1]=0; //第1步的Y坐标
recordX[0]=1; //第1步的X坐标
dfs(1,0,1); //从第0行,第0列,开始遍历图,当前步数是1

6.规定,只能从起点的下方出发,打印所有的遍历结果:
等同于(4),每次遍历之后取消标记那个点

7.打印出从起点出发到终点的所有路径,并给出最短的步数:
-----Code begin-----
#include <stdio.h>

int map[3][2];
int book[3][2];
int recordY[6];
int recordX[6];
int next[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
int minStep=9999; //保存每次遍历结果的最小步数

void dfs(int y, int x, int step){
int i,tx,ty;
if (y==2 && x==1){ //到达终点
printf("Traverse complete\n");
for (i=0;i<=step;i++){ //打印出保存在recordY和recordX中的点的坐标
printf("step: %d, at y=%d and x=%d\n",i,recordY[i],recordX[i]);
}
if (minStep>step) minStep=step; //发现步数更少的遍历方式,更新最小步数
return;
}
for (i=0;i<4;i++){ //还没遍历完,继续枚举各个方向并进行下次递归
ty=y+next[i][0]; //下个点所在的行
tx=x+next[i][1]; //下个点所在的列
if (tx>-1 && tx<2 && ty>-1 && ty<3 && book[ty][tx]!=1){ //这个点必须在图内,且没有被标记
book[ty][tx]=1; //标记这个点
recordY[step+1]=ty; //保存这个点所在的行
recordX[step+1]=tx; //保存这个点所在的列
dfs(ty,tx,step+1); //对这个点继续DFS
book[ty][tx]=0; //取消标记这个点
}
}
}

int main(){
book[0][0]=1; //从起点出发,所以起点已经走过了,把它标记为1
recordY[0]=0; //先把起点所在的行保存下来
recordX[0]=0; //并把起点所在的列保存下来
dfs(0,0,0); //从第0行,第0列,开始遍历图,当前步数是1
printf("The minimum step is %d\n",minStep);

return 0;
}
-----Code end-----
输出:Traverse complete
step: 0, at y=0 and x=0
step: 1, at y=0 and x=1
step: 2, at y=1 and x=1
step: 3, at y=2 and x=1
Traverse complete
step: 0, at y=0 and x=0
step: 1, at y=0 and x=1
step: 2, at y=1 and x=1
step: 3, at y=1 and x=0
step: 4, at y=2 and x=0
step: 5, at y=2 and x=1
Traverse complete
step: 0, at y=0 and x=0
step: 1, at y=1 and x=0
step: 2, at y=1 and x=1
step: 3, at y=2 and x=1
Traverse complete
step: 0, at y=0 and x=0
step: 1, at y=1 and x=0
step: 2, at y=2 and x=0
step: 3, at y=2 and x=1
The minimum step is 3

Process returned 0 (0x0) execution time : 0.399 s
Press any key to continue.

8.由于故障,点(y=1,x=1)不能被访问,求出现在从起点到终点的所有路径:
只需修改(7)的main函数和dfs函数,具体如下,
修改main函数:
map[1][1]=-1; //值为-1表示这个点不能被访问
book[0][0]=1; //从起点出发,所以起点已经走过了,把它标记为1
recordY[0]=0; //先把起点所在的行保存下来
recordX[0]=0; //并把起点所在的列保存下来
dfs(0,0,0); //从第0行,第0列,开始遍历图,当前步数是1
printf("The minimum step is %d\n",minStep);
修改dfs函数:
if (tx>-1 && tx<2 && ty>-1 && ty<3 && book[ty][tx]!=1 && map[ty][tx]!=-1){ //这个点必须在图内,且没有被标记,还要是能能访问的点
输出:
raverse complete
tep: 0, at y=0 and x=0
tep: 1, at y=1 and x=0
tep: 2, at y=2 and x=0
tep: 3, at y=2 and x=1
he minimum step is 3

rocess returned 0 (0x0) execution time : 0.572 s
ress any key to continue.

PS:可以看出,map数组其实只在第8问中使用到了。

==主题结束==

猜你喜欢

转载自www.cnblogs.com/ryzz/p/12292755.html
今日推荐