DFS(深度优先搜索) 求迷宫最短路径 C / java代码实现分析


问题描述:小哈在m行n列的矩形迷宫里迷了路,小哼来解救小哈。已知小哈在迷宫的坐标为(p, q),问小哼最少要走多少步才能走到小哈的所在位置。

使用DFS解决该问题需要了解:

Question1. 递归时结束的条件

Question2.递去的过程应该做什么

Question3.归来的过程应该做什么

Answer1.很显然,可以用一个二维数组来初始化迷宫,当小哼的坐标和小哈的坐标一致时,递归结束

void dfs(int x, int y, int step) {
	if (x == p&& y == q)
	{
		total++;
		if (step < min)		//如果step比min小,就修改min的值
			min = step;     
		return;				//如果到达了终点就回溯
	}

Answer2.递去的过程中,需要首先规定一个顺序 本文章代码规定顺时针顺序移动,即 右下左上

可以用多个if 语句并列实现,也可以使用方向数组实现,本文章代码将用c语言展示if语句实现 ,方向数组的用java实现。规定完顺序后 考虑按照顺序改变后坐标是否满足 1.未被标记 和 2.是空地 3.未越界的问题。

Answer3. 归来的过程中,需要把当前坐标 重新标记为未标记

else {					
		if ( y + 1 <= n && a[x][y+1] == 0 && book[x][y+1]==0)	//判断边界以及该坐标是否被标记和是否是空地
		{
			book[x][y+1] = 1;		// 递去阶段:当走到当前位置,标记已走过
			dfs(x , y+1, step + 1);	//	进行该位置的深度优先搜索
			book[x][y+1] = 0;		// 回溯阶段: 取消该标记,并按顺序继续移动
		}
		if ( x + 1 <= m && a[x+1][y] == 0 && book[x+1][y ] == 0)
		{
			book[x+1][y] = 1;
			dfs(x+1, y , step + 1);
			book[x+1][y] = 0;
		}
		if (0 < y -1  && a[x][y -1] == 0 && book[x][y - 1] == 0)
		{
			book[x][y -1] = 1;
			dfs(x, y - 1, step + 1);
			book[x][y - 1] = 0;
		}
		if (0 < x -1  && a[x - 1][y] == 0 && book[x -1][y] == 0)
		{
			book[x - 1][y] = 1;
			dfs(x -1, y, step + 1);
			book[x -1][y] = 0;
		}
		return;		//当无路可走时 回溯上一个位置;
	}

完整C语言代码如下:

#include <stdio.h>
int p, q,m, n,min=888;  //设置行数m 与列数n   (p,q)为终点的坐标
int total = 0;			//路径总数
int book[51][51];		//标记数组 用于判断下个坐标是否走过 走过则标记为1
int a[51][51];	// 0表示空地 1表示障碍物

void dfs(int x, int y, int step) {
	if (x == p&& y == q)
	{
		total++;
		if (step < min)		//如果step比min小,就修改min的值
			min = step;     
		return;				//如果到达了终点就回溯
	}
	else {					
		if ( y + 1 <= n && a[x][y+1] == 0 && book[x][y+1]==0)	//判断边界以及该坐标是否被标记和是否是空地
		{
			book[x][y+1] = 1;		// 递去阶段:当走到当前位置,标记已走过
			dfs(x , y+1, step + 1);	//	进行该位置的深度优先搜索
			book[x][y+1] = 0;		// 回溯阶段: 取消该标记,并按顺序继续移动
		}
		if ( x + 1 <= m && a[x+1][y] == 0 && book[x+1][y ] == 0)
		{
			book[x+1][y] = 1;
			dfs(x+1, y , step + 1);
			book[x+1][y] = 0;
		}
		if (0 < y -1  && a[x][y -1] == 0 && book[x][y - 1] == 0)
		{
			book[x][y -1] = 1;
			dfs(x, y - 1, step + 1);
			book[x][y - 1] = 0;
		}
		if (0 < x -1  && a[x - 1][y] == 0 && book[x -1][y] == 0)
		{
			book[x - 1][y] = 1;
			dfs(x -1, y, step + 1);
			book[x -1][y] = 0;
		}
		return;		//当无路可走时 回溯上一个位置;
	}
	
}
int main()
{
	int st, ed;
	printf("请分别输入迷宫的行数和列数 m 和 n (两者均<=50)\n");
	scanf_s("%d %d", &m, &n);

	

	printf("请读入迷宫,可走的路径设置为0 障碍物设置为1\n");
	for (int i = 1; i <= m; i++)
		for (int j = 1; j <= n; j++)
			scanf_s("%d", &a[i][j]);

	printf("请输入起点坐标\n");
	scanf_s("%d %d", &st, &ed);
	book[st][ed] = 1;

	printf("请输入终点坐标\n");
	scanf_s("%d %d", &p, &q);
	printf("NO problem\n");
	dfs(st, ed, 0);
	printf("找到了%d条路径\n最短路径为%d\n",total, min);
}

结果展示:

为计算方便,本算法所有数组从1开始

下面展示java代码:

        与c的代码大同小异,使用了方向数组,做这类DFS的题目时经常用到,建议掌握

        

import java.util.Scanner;

public class DFS2 {
    private int m,n,p,q;
    private boolean book[][]=new boolean[51][51];
    private int a[][]=new int[51][51];
    private int min=8888,total=0;
    private int Dire[][]={{0,1},{1,0},{0,-1},{-1,0}};       //方向数组,用于改变小哼当前的坐标(相比于分支结构if减少了代码的冗余性并增加了美观)

    public void dfs(int x,int y,int step){
        int tx,ty;
        
        if(x==p&&y==q) {
            total++;
            if(min>step) min=step;
         return;
        }
        else {
            for(int i=0;i<4;i++){
                tx=x+Dire[i][0];        //改变坐标
                ty=y+Dire[i][1];

                if(tx<=m && tx>0 && ty>0 && ty<=n &&a[tx][ty]==0&&book[tx][ty]==false){  //判断当前即将移动的坐标是否满足条件
                    book[tx][ty]=true;              //递去过程中 标记当前位置已走过
                    dfs(tx,ty,step+1);
                    book[tx][ty]=false;             //归来过程中 取消标记
                }

            }
            return;
        }
    }

    public static void main(String[] args) {
        DFS2 d=new DFS2();

        Scanner in=new Scanner(System.in);
        System.out.println("请输入迷宫的行数和列数");
        d.m=in.nextInt(); d.n=in.nextInt();
        System.out.println("请输入迷宫 0代表空地 1代表障碍物");

        for(int i=1;i<=d.m;i++)
            for(int j=1;j<=d.n;j++)
                d.a[i][j]=in.nextInt();
        System.out.println("请输入起点的坐标");
        int st=in.nextInt(),ed= in.nextInt();
        System.out.println("请输入终点的坐标");
        d.p= in.nextInt();d.q= in.nextInt();

        d.book[st][ed]=true;
        d.dfs(st,ed,0);
        System.out.printf("总共找到%d条路径\n其中最短路径长度为%d",d.total,d.min);
    }
}

        结果展示:

 

                

 此类题目刚开始做时很头疼,关键是找好递去归来过程中需要完成什么事情才能有效解决问题

建议现在草稿纸上好好研究模型在动手打代码也许更加容易理解

如果这篇文章有帮到大家的话,希望大家点个赞哦,谢谢啦!

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值