算法——深度优先搜索(dfs)和广度优先搜索(bfs)_(JS实现)

问题描述

有一个二维数组map[4][4],

    [0, 0, 0, 0],
    [1, 0, 1, 0],
    [1, 0, 0, 0],
    [1, 0, 1, 0],

把map[0][0]作为起点,map[4][4]作为终点,如果数组中的值0代表可以通过,1代表无法通过。求出是否能从起点到达终点,如果可以,输出ture,并且输入路径。

一、深度优先搜索(dfs)

① 概念理解:一个人迷路,遇到很多分叉路口,他只有一个人,并且想走出去,所以只能一个个尝试,一条道路走到黑,发现到头了,然后再拐回去走刚才这条路的其他分叉路口,最后发现这条路的所有分叉路口走完了,选择另外一条路继续以上操作,直到所有的路都走过了。实际上就是往深度走,走错了就回来,找没走过的路,直到走到终点。
② 实现方式:堆栈实现,
③ 优点:能找出所有解决方案;相对于bfs内存占用少。
④ 缺点:找到的路径有可能不是最短路径,且在深度较大时效率低。
⑤ 代码实现(JS):

let map = [
    [0, 0, 0, 0],
    [1, 0, 1, 0],
    [1, 0, 0, 0],
    [1, 0, 1, 0],
]
let start = [0, 0];
let end = [3, 3];

let vis = { 0: true }; // x_y表示(x,y)坐标已经不能再经过了
let dis = [];
let num = 0;
let n =0;

function dfs(x, y) {
    dis.push([x, y]);       //在dis列表最后添加一个元素
    if (x == end[0] && y == end[1]) {       //到达终点
        console.log("第", num, "条路:", dis);
        num++;
        dis.pop();      //截取列表中的最后一个元素
        return true;
    }
    let isFind = false;
    for (let info of [[0, 1],[0, -1],[-1, 0], [1, 0]]) {  // 遍历所有方向(上,下,左,右)
        let X = x + info[0];    // 计算出对应方向的坐标
        let Y = y + info[1];
        if (X >= 0 && X < 4) {  // 判断坐标是否出界
            if (Y >= 0 && Y < 4) {
                if (map[X][Y] == 0) {   // 判断坐标是否能走
                    if (!vis[X * 10 + Y]) { // 判断坐标是否已走
                        vis[X * 10 + Y] = true; // 记录坐标已走
                        if (dfs(X, Y)) {    // 进入下一个坐标,当找到终点时,dfs函数的返回值时ture
                            isFind = true;
                        }
                        vis[X * 10 + Y] = false;    // 取消上一个走的坐标标记,是在全部完成搜索完以后
                    }
                }
            }
        }
    }
    dis.pop();
    return isFind;
}
console.log(dfs(start[0], start[1]));

⑥ 实现结果:
在这里插入图片描述
1、列表dis[]中存放的是所有可行的路径,
2、isFind代表是否从起点start 到达 终点end

二、广度优先搜索(bfs)

① 概念理解:一个人迷路,但是他有技能(分身术)它遇到分叉路口,不是选一个走,而是分身多个人都试试,比如有A、B、C三个分叉路口,它A路走一步,紧接着B路也走一步,然后C路也赶紧走一步,步伐整齐统一,直到所有的路走过了。实际上就是往四周搜索,分开搜索。
② 实现方式:队列实现
③ 优点:找到的路径就是最短路径,效率高。
④ 缺点:内存耗费大。
⑤ 代码实现(JS):let map = [

    [0, 0, 0, 0],
    [1, 0, 1, 0],
    [1, 0, 0, 0],
    [1, 0, 1, 0],
]
let start = [0, 0];
let end = [3, 3];

let vis = { 0: true }; // x_y表示(x,y)坐标已经不能再经过了
let dis = [];

function bfs(s, t){
    let que = [s];
    let isFind = false;
    while (que.length > 0){ // 列表不空,循环不完
        let cur = que.shift();  // 队首元素出队
        if (cur[0] == t[0] && cur[1] == t[1]){  // 判断当前点是否为终点
            isFind = true;
            break;
        }
        for (let info of [[-1,0],[1,0],[0,-1],[0,1]]){
            let X = cur[0] + info[0];
            let Y = cur[1] + info[1];
            if (X >= 0 && X < 4) {  // 判断坐标是否出界
                if (Y >= 0 && Y < 4) {
                    if (map[X][Y] == 0) {   // 判断坐标是否能走
                        if (!vis[X * 10 + Y]) { // 判断坐标是否已走
                            vis[X * 10 + Y] = true; // 记录坐标已走
                            dis[X * 10 + Y] = cur;  // 儿子记录老子
                            que.push([X, Y]);
                        }
                    }
                }
            }
        }
    }
    return isFind;
}
console.log(bfs(start,end));
let c = end;
let list = [];
while(c){
    list.push(c);
    c = dis[c[0] * 10 +c[1]];
}
console.log(list.reverse());

⑥ 实现结果:

在这里插入图片描述

dfs和bfs的最优解情况

① 比较两种算法:广度(bfs)一般无回溯操作,即人栈和出栈的操作,所以运行速度比深度优先搜索法要快些。所以一般情况下,深度(dfs)占内存少但速度较慢,广度(bfs)占内存较多但速度较快,在距离与深度成正比的情况下能较快地求出最优解。
② 如果数据量较大,必须考虑溢出和节省内存空间的问题,使用深度优先搜索法较好,深度优先搜索法有递归以及非递归两种设计方法。一般的,当搜索深度较小、问题递归形式较明显时,用递归方法设计的较好,它可以使得程序结构更简捷易懂。但当搜索深度较大时,当数据量较大时,由于系统堆栈容量的限制,递归易产生溢出,用非递归方法设计比较好
③ 如果数据量较小,且对程序运行的效率要求较高,或者题意是需要找出最短路径,一般使用广度优先搜索法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值