【第四章】万能的搜索:深度、广度优先搜索

第一节:不撞南墙不回头—深度优先搜索

dfs 这个我以前一直不太理解

最近是通过慢慢想 + 一步步调试,才算是 get 到一点点了,不得不佩服发明这个算法的人,太巧妙了。

let a = [];
let book = new Array(100).fill(0);
let n = 3;
function dfs(step) {
    // 当下该如何做
    if (step === n + 1) {
        console.log(a);
        return;
    }
    for (let i = 1; i <= n; i++) {
        if (book[i] === 0) {
            a[step] = i;
            book[i] = 1;
            dfs(step + 1);
            book[i] = 0;
        }
    }
}
dfs(1);

还有就是第三章留的一道题:在这里插入图片描述
解法如下,跟上一个dfs 思路是一样的,只是改变了结束的条件:

let a = [];
let book = new Array(100).fill(0);
let n = 9;  // 盒子的个数
let total = 0;
function dfs(step) {
    // 当下该如何做
    if (step === n + 1) {
        if ((a[1] * 100 + a[2] * 10 + a[3]) + a[4] * 100 + a[5] * 10 + a[6] === a[7] * 100 + a[8] * 10 + a[9]) {
            total++;
            console.log(`${(a[1] * 100 + a[2] * 10 + a[3])} + ${(a[4] * 100 + a[5] * 10 + a[6])} = ${(a[7] * 100 + a[8] * 10 + a[9])}`);
        }
        return;
    }
    for (let i = 1; i <= n; i++) {
        if (book[i] === 0) {
            a[step] = i;
            book[i] = 1;
            dfs(step + 1);
            book[i] = 0;
        }
    }
    
}
dfs(1);
console.log(total);

第二节:解救小哈

是个最短路径的问题,其中迷宫a我在外圈套了层0,这样改的目的是为了尽量少的改原书上的代码。

思路就是枚举啊,把所有的情况试一遍。虽然知道,但是写的时候还是憋不出来。就很气!

// 迷宫的地图
let a = [
	[0, 0, 0, 0, 0],
	[0, 0, 0, 1, 0],
	[0, 0, 0, 0, 0],
	[0, 0, 0, 1, 0],
	[0, 0, 1, 0, 0],
	[0, 0, 0, 0, 1]
];
let n = a.length - 1;		// 迷宫的行数
let m = a[0].length - 1;	// 迷宫的列数
let p = 4, q = 3;			// 目的地(小哈的位置)

let book = new Array();
for (let i = 0; i < a.length; i++) {
	let childBook = new Array(a[0].length).fill(0);
	book[i] = childBook;
}

for (let i = 0; i < a.length; i++) {
	for (let j = 0; j < a[0].length; j++) {
		if (a[i][j] === 1) {
			book[i][j] = 3;// 给地图上的障碍物做个标记
		}
	}
}

console.log(book);

let minStep = Number.MAX_SAFE_INTEGER;

// 注,这本书的x代表行,y代表列,所以 [0,1]的表示方向是右
let next = [
	[0, 1], // 右
	[1, 0], // 下
	[0, -1], // 左
	[-1, 0] // 上
]

function dfs(x, y, step) {
	// dfs 函数是解决当下的问题
	
	// 1. 写结束的条件
	if (x === p && y === q) {
		minStep = Math.min(minStep, step);
		console.log(JSON.parse(JSON.stringify(book)));		// 可以打印这个看路径
	}

	// 2. 当前该做些什么
	for (let k = 0; k <= 3; k++) {
		// 首先算出下一步的坐标
		let tx = x + next[k][0];
		let ty = y + next[k][1];
		// 如果这个坐标出界了,那么跳过他
		if (tx < 1 || tx > n || ty < 1 || ty > m) {
			continue;
		}
		// 如果当前位置不是障碍物并且没有走过
		if (a[tx][ty] === 0 && book[tx][ty] === 0) {
			book[tx][ty] = 1;
			dfs(tx, ty, step + 1);
			book[tx][ty] = 0;
		}
	}
}
book[1][1] = 1;
dfs(1, 1, 0);
console.log(minStep); // 7

在这里插入图片描述

第三节:层层递进—广度优先搜索

用的数据是队列,书上画的图很不错了。
在这里插入图片描述

class Queue {
    constructor() {
        this.x; 	// 横坐标
        this.y;		// 纵坐标
        this.f;		// 父亲在队列中的编号
        this.s;		// 步数
    }
}
let next = [[0, 1], [1, 0], [0, -1], [-1, 0]];

let xiaoheng = [1, 1];
let xiaoha = [4, 3];
let p = xiaoha[0];
let q = xiaoha[1];

let que = [];
for (let i = 0; i < 100; i++) {
    let qNode = new Queue();
    que[i] = qNode;
}
let head, tail;

// 存储地图
let a = [
    [0, 0, 0, 0, 0],
    [0, 0, 0, 1, 0],
    [0, 0, 0, 0, 0],
    [0, 0, 0, 1, 0],
    [0, 0, 1, 0, 0],
    [0, 0, 0, 0, 1]
];
let n = a.length - 1;		// 迷宫的行数
let m = a[0].length - 1;	// 迷宫的列数
// book 用来存储哪些点已经在队列中了,防止重复扩展
let book = new Array();
for (let i = 0; i < a.length; i++) {
    let childBook = new Array(a[0].length).fill(0);
    book[i] = childBook;
}

// 初始化队列
head = 1;
tail = 1;

// 把 1,1 加入队列中
que[tail].x = xiaoheng[0];
que[tail].y = xiaoheng[1];
que[tail].f = 0;
que[tail].s = 0;
tail++;                 // 队列长度加1
book[1][1] = 1;         // 记录1,1已经走过了

flag = 0;               // 标记是否到达目标点,0代表没有到达

while (head < tail) {
    for (let k = 0; k <= 3; k++) {
        // 计算下一个点
        let tx = que[head].x + next[k][0];
        let ty = que[head].y + next[k][1];
        if (tx < 1 || tx > n || ty < 1 || ty > m) {
            continue;
        }
        if (a[tx][ty] === 0 && book[tx][ty] === 0) {
            book[tx][ty] = 1;   // 标记这个点已经走过了

            // 尾部入队
            que[tail].x = tx;
            que[tail].y = ty;
            que[tail].f = head;
            que[tail].s = que[head].s + 1;  // 记录当前步数

            tail++;
        }
        if (tx === p && ty === q) {
            flag = 1;
            break;
        }
    }
    if (flag == 1) {
        break;
    }
    head++;     // 头节点已经扩展完了,要把当前的头节点指向队列中下一个节点。
}
console.log(que);
console.log(que[tail - 1].s);

在这里插入图片描述

第四节:再解炸弹人

书中分别以深度/广度优先搜索,来解决这个问题。

// 广度优先搜索
class Queue {
    constructor() {
        this.x;
        this.y;
    }
}
// 计算当前点能够消灭的最多的怪
function getnum(i, j) {
    let sum = 0, x = i, y = j;
    while (a[x][y] !== '#') {
        if (a[x][y] === 'G') {
            sum++;
        }
        x--;    // 向上统计
    }
    x = i; y = j;
    while (a[x][y] != '#') {
        if (a[x][y] === 'G') {
            sum++;
        }
        x++;    // 向下统计
    }
    x = i; y = j;
    while (a[x][y] != '#') {
        if (a[x][y] === 'G') {
            sum++;
        }
        y--;    // 向左统计
    }
    x = i; y = j;
    while (a[x][y] != '#') {
        if (a[x][y] === 'G') {
            sum++;
        }
        y++;    // 向右统计
    }
    return sum;
}

// 初始化一个队列
let head, tail;
let que = [];
for (let i = 0; i < 40; i++) {
    let queNode = new Queue();
    que[i] = queNode;
}

// 处理地图字符串
let mapStr = `
#############
#GG.GGG#GGG.#
###.#G#G#G#G#
#.......#..G#
#G#.###.#G#G#
#GG.GGG.#.GG#
#G#.#G#.#.#.#
##G...G.....#
#G#.#G###.#G#
#...G#GGG.GG#
#G#.#G#G#.#G#
#GG.GGG#G.GG#
#############`;
let regExp = /\n+/g;
mapStr = mapStr.replace(regExp, '');

// 初始化二维数组地图
let a = [];
let m = 13, n = 13;
for (let i = 0; i < m; i++) {
    let eachRow = [];
    for (let j = 0; j < n; j++) {
        eachRow.push(mapStr[i * 13 + j]);
    }
    a.push(eachRow)
}
console.log('地图a:>>', a);

// 初始化辅助标记,用来记录哪些点已经走过了
let book = [];
for (let i = 0; i < 13; i++) {
    let bookChild = new Array(13).fill(0);
    book[i] = bookChild;
}
console.log("辅助数组:>>", book);

// 初始化能够消灭最多的怪,以及能够消灭最多的怪的点。
let max = -1, mx, my;
let next = [
    [0, 1], // 右
    [1, 0], // 下
    [0, -1], // 左
    [-1, 0] // 上
];

// 开始操作!!
// 设置起点
let startx = 3;
let starty = 3;
head = 1;
tail = 1;

// 起点入队
que[tail].x = startx;
que[tail].y = starty;
tail++;
book[startx][starty] = 1;

// 计算起点能够消灭的怪物数量
max = getnum(startx, starty);
// 记录坐标
mx = startx;
my = starty;

// 队列操作
while (head < tail) {
    for (let k = 0; k <= 3; k++) {
        let tx = que[head].x + next[k][0];
        let ty = que[head].y + next[k][1];
        if (tx < 0 || tx > n - 1 || ty < 0 || ty > m - 1) {
            // 如果越界了,那么跳过
            continue;
        }
        if (a[tx][ty] === '.' && book[tx][ty] === 0) {
            book[tx][ty] = 1;
            // 尾部入队
            que[tail].x = tx;
            que[tail].y = ty;
            tail++;
            let sum = getnum(tx, ty);
            if (sum > max) {
                max = sum;
                mx = tx;
                my = ty;
            }
        }
    }
    head++; // 队首出队
}
console.log('队列que:>>', que);
console.log(`在${mx}行,${my}列能够消灭最多${max}个怪物。`);

接着用深度优先搜索来

// 处理地图字符串
let mapStr = `
#############
#GG.GGG#GGG.#
###.#G#G#G#G#
#.......#..G#
#G#.###.#G#G#
#GG.GGG.#.GG#
#G#.#G#.#.#.#
##G...G.....#
#G#.#G###.#G#
#...G#GGG.GG#
#G#.#G#G#.#G#
#GG.GGG#G.GG#
#############`;
let regExp = /\n+/g;
mapStr = mapStr.replace(regExp, '');

// 初始化二维数组地图
let a = [];
let m = 13, n = 13;
for (let i = 0; i < m; i++) {
    let eachRow = [];
    for (let j = 0; j < n; j++) {
        eachRow.push(mapStr[i * 13 + j]);
    }
    a.push(eachRow)
}
console.log('地图a:>>', a);


// 初始化辅助标记,用来记录哪些点已经走过了
let book = [];
for (let i = 0; i < 13; i++) {
    let bookChild = new Array(13).fill(0);
    book[i] = bookChild;
}
console.log("辅助数组:>>", book);


// 计算当前点能够消灭的最多的怪
function getnum(i, j) {
    let sum = 0, x = i, y = j;
    while (a[x][y] !== '#') {
        if (a[x][y] === 'G') {
            sum++;
        }
        x--;    // 向上统计
    }
    x = i; y = j;
    while (a[x][y] != '#') {
        if (a[x][y] === 'G') {
            sum++;
        }
        x++;    // 向下统计
    }
    x = i; y = j;
    while (a[x][y] != '#') {
        if (a[x][y] === 'G') {
            sum++;
        }
        y--;    // 向左统计
    }
    x = i; y = j;
    while (a[x][y] != '#') {
        if (a[x][y] === 'G') {
            sum++;
        }
        y++;    // 向右统计
    }
    return sum;
}

const dfs = (x, y) => {
    let next = [[0, 1], [1, 0], [0, -1], [-1, 0]];
    let sum = getnum(x, y);
    if (sum > max) {
        max = sum; mx = x; my = y;
    }
    for (let k = 0; k <= 3; k++) {
        let tx = x + next[k][0];
        let ty = y + next[k][1];
        if (tx < 0 || tx > n - 1 || ty < 0 || ty > m - 1) {
            continue;
        }
        if (a[tx][ty] === '.' && book[tx][ty] === 0) {
            book[tx][ty] = 1;
            dfs(tx, ty);
        }
    }
    return;
}


let startx = 3, starty = 3;

// 记录最大值以及坐标
let max = getnum(startx, starty);
let mx = startx;
let my = starty;
dfs(startx, starty);
console.log(mx, my, max);

第五节:宝岛探险

肯定有人是要来搜地图的,所以我单独提取出来给你吧。

1 2 1 0 0 0 0 0 2 3
3 0 2 0 1 2 1 0 1 2
4 0 1 0 1 2 3 2 0 1
3 2 0 0 0 1 2 4 0 0
0 0 0 0 0 0 1 5 3 0
0 1 2 1 0 1 5 4 3 0
0 1 2 3 1 3 6 2 1 0
0 0 3 4 8 9 7 5 0 0
0 0 0 3 7 8 6 0 1 2
0 0 0 0 0 0 0 0 1 0 

这个我是自己写的,之前都是抄书,有些变量名可能和书上的不一样。

// 广度优先搜索
// 处理地图
let mapStr = `
1 2 1 0 0 0 0 0 2 3
3 0 2 0 1 2 1 0 1 2
4 0 1 0 1 2 3 2 0 1
3 2 0 0 0 1 2 4 0 0
0 0 0 0 0 0 1 5 3 0
0 1 2 1 0 1 5 4 3 0
0 1 2 3 1 3 6 2 1 0
0 0 3 4 8 9 7 5 0 0
0 0 0 3 7 8 6 0 1 2
0 0 0 0 0 0 0 0 1 0 
`;
let regExp = /\s+/g;    // \s表示任何看不到的字符,等价于[ \f\n\r\t\v]。
mapStr = mapStr.replace(regExp, '');
let a = [];
for (let i = 0; i < 10; i++) {
`在这里插入代码片`    let child = mapStr.substr(i * 10, 10);
    a[i] = child.split('');
}
let m = a.length;
let n = a[0].length;

console.log('地图:>>', a);
class Queue {
    constructor() {
        this.x;
        this.y;
    }
}

// 初始化队列
let que = [];
for (let i = 0; i < 100; i++) {
    let queChild = new Queue();
    que[i] = queChild;
};
let head = 1, tail = 1;

// 辅助数组
let book = [];
for (let i = 0; i < 10; i++) {
    let bookChild = new Array(10).fill(0);
    book[i] = bookChild;
}
console.log('book:>>', book);

// 方向数组
let next = [[0, 1], [1, 0], [0, -1], [-1, 0]];

let startx = 6;
let starty = 8;
let areaSum = 0;
que[head].x = startx;
que[head].y = starty;
book[startx][starty] = 1;
tail++;
areaSum++;

while (head < tail) {
    for (let k = 0; k <= 3; k++) {
        // 寻找下一个点
        let tx = que[head].x + next[k][0];
        let ty = que[head].y + next[k][1];

        // 判断是否出界
        if (tx < 0 || ty < 0 || tx > 9 || ty > 9) {
            continue;
        }

        if (a[tx][ty] > 0 && book[tx][ty] === 0) {
            // 加入队列
            que[tail].x = tx;
            que[tail].y = ty;
            book[tx][ty] = 1;
            areaSum++;
            tail++;
        }
    }
    head++;
}
console.log(que);
console.log('地图面积:>>', areaSum);
console.log('辅助数组book:>>', book);

接着是深度优先搜索,也是我自己写的,(果然自己敲出来的感觉更棒啊),感觉深搜的代码更少些,写起来也比较简单

因为广搜的话需要建立队列,还要操作队列;而深搜直接递归回溯,不需要数据解构!

// 深度优先搜索
// 处理地图
let mapStr = `
1 2 1 0 0 0 0 0 2 3
3 0 2 0 1 2 1 0 1 2
4 0 1 0 1 2 3 2 0 1
3 2 0 0 0 1 2 4 0 0
0 0 0 0 0 0 1 5 3 0
0 1 2 1 0 1 5 4 3 0
0 1 2 3 1 3 6 2 1 0
0 0 3 4 8 9 7 5 0 0
0 0 0 3 7 8 6 0 1 2
0 0 0 0 0 0 0 0 1 0 
`;
let regExp = /\s+/g;    // \s表示任何看不到的字符,等价于[ \f\n\r\t\v]。
mapStr = mapStr.replace(regExp, '');
let a = [];
for (let i = 0; i < 10; i++) {
    let child = mapStr.substr(i * 10, 10);
    a[i] = child.split('');
}
let m = a.length;
let n = a[0].length;

console.log('地图:>>', a);

// 辅助数组
let book = [];
for (let i = 0; i < 10; i++) {
    let bookChild = new Array(10).fill(0);
    book[i] = bookChild;
}
console.log('book:>>', book);

// 方向数组
let next = [[0, 1], [1, 0], [0, -1], [-1, 0]];

let sum = 0;
const dfs = (i, j) => {
    let x = i, y = j;
    for (let k = 0; k <= 3; k++) {
        let tx = x + next[k][0];
        let ty = y + next[k][1];
        // 判断是否出界
        if (tx < 0 || ty < 0 || tx > 9 || ty > 9) {
            continue;
        }
        if (a[tx][ty] > 0 && book[tx][ty] === 0) {
            book[tx][ty] = 1;
            sum++;
            dfs(tx, ty);
        }
    }
    return;
}
dfs(6, 8);
console.log('面积:>>', sum);
console.log('辅助函数:>>', book);

接着讲了个着色法,就是在递归的时候加个参数,用来给原先的地图着色。

// 处理地图
let mapStr = `
1 2 1 0 0 0 0 0 2 3
3 0 2 0 1 2 1 0 1 2
4 0 1 0 1 2 3 2 0 1
3 2 0 0 0 1 2 4 0 0
0 0 0 0 0 0 1 5 3 0
0 1 2 1 0 1 5 4 3 0
0 1 2 3 1 3 6 2 1 0
0 0 3 4 8 9 7 5 0 0
0 0 0 3 7 8 6 0 1 2
0 0 0 0 0 0 0 0 1 0 
`;
let regExp = /\s+/g;    // \s表示任何看不到的字符,等价于[ \f\n\r\t\v]。
mapStr = mapStr.replace(regExp, '');
let a = [];
for (let i = 0; i < 10; i++) {
    let child = mapStr.substr(i * 10, 10);
    a[i] = child.split('');
}
let m = a.length;
let n = a[0].length;

console.log('地图:>>', a);

// 辅助数组
let book = [];
for (let i = 0; i < 10; i++) {
    let bookChild = new Array(10).fill(0);
    book[i] = bookChild;
}
console.log('book:>>', book);

// 方向数组
let next = [[0, 1], [1, 0], [0, -1], [-1, 0]];

let sum = 0;
const dfs = (i, j, color) => {
    let x = i, y = j;
    a[x][y] = color;
    for (let k = 0; k <= 3; k++) {
        let tx = x + next[k][0];
        let ty = y + next[k][1];

        // 判断是否出界
        if (tx < 0 || ty < 0 || tx > 9 || ty > 9) {
            continue;
        }
        if (a[tx][ty] > 0 && book[tx][ty] === 0) {
            book[tx][ty] = 1;
            sum++;
            dfs(tx, ty, color);
        }
    }
    return;
}

dfs(6, 8, '9');
console.log('面积:>>', sum);
console.log('辅助函数book:>>', book);
console.log('着色数组a:>>', a);

Q:想知道有多少个独立的岛,那么就是深搜所有的大于1的点,这个改如何去改代码呢?

先想一想哦!别着急看答案~

A:遍历所有大于0 且没走过的点…没错,就是辣个男人,双层夹心for循环(我以为会是多么精妙的想法呢,淦原来这么暴力,不过这么暴力的方法我当时没敢想,觉得有点憨批…看来是我懦弱了…)

// 寻找共有多少个岛,其实这里的color就是独立岛屿的数量了。
// 处理地图
let mapStr = `
1 2 1 0 0 0 0 0 2 3
3 0 2 0 1 2 1 0 1 2
4 0 1 0 1 2 3 2 0 1
3 2 0 0 0 1 2 4 0 0
0 0 0 0 0 0 1 5 3 0
0 1 2 1 0 1 5 4 3 0
0 1 2 3 1 3 6 2 1 0
0 0 3 4 8 9 7 5 0 0
0 0 0 3 7 8 6 0 1 2
0 0 0 0 0 0 0 0 1 0 
`;
let regExp = /\s+/g;    // \s表示任何看不到的字符,等价于[ \f\n\r\t\v]。
mapStr = mapStr.replace(regExp, '');
let a = [];
for (let i = 0; i < 10; i++) {
    let child = mapStr.substr(i * 10, 10);
    let row = child.split('');
    a[i] = row.map(i => Number(i))
}
let m = a.length;
let n = a[0].length;
// console.log('地图:>>', a);
// 这里说明一下为什么用 JSON.parse( JSON.stringify(a))
// 因为 a 是个引用数据类型,所以下面如果改动的话,在浏览器环境下
// 看到的还是已经改变过的数据。
console.log('原地图:>>', JSON.parse( JSON.stringify(a)));

// 辅助数组
let book = [];
for (let i = 0; i < 10; i++) {
    let bookChild = new Array(10).fill(0);
    book[i] = bookChild;
}
console.log('book:>>', book);

// 方向数组
let next = [[0, 1], [1, 0], [0, -1], [-1, 0]];

let sum = 0;
const dfs = (i, j, color) => {
    let x = i, y = j;
    a[x][y] = color;
    for (let k = 0; k <= 3; k++) {
        let tx = x + next[k][0];
        let ty = y + next[k][1];

        // 判断是否出界
        if (tx < 0 || ty < 0 || tx > 9 || ty > 9) {
            continue;
        }
        if (a[tx][ty] > 0 && book[tx][ty] === 0) {
            book[tx][ty] = 1;
            sum++;
            dfs(tx, ty, color);
        }
    }
    return;
}
// 遍历每一个大于0且没有走过的点
let color = 0;
for (let i = 0; i < 9; i++) {
    for (let j = 0; j < 9; j++) {
        if (a[i][j] > 0 && book[i][j] === 0) {
            color++;
            book[i][j] = 1;
            dfs(i, j, color);
        }
    }
}
console.log('面积:>>', sum);
console.log('辅助函数book:>>', book);
console.log('着色数组a:>>', a);

在这里插入图片描述

第六节:水管工游戏

居然说有点难,可以跳过,我王某人曾几何时怕过捏?
emm先看个题目哈…

算了,接着不讲武德吧我抽象不出来啊🙄…

// 没有加栈的
let n = 5, m = 4;
let a = [
    [0, 0, 0, 0, 0],
    [0, 5, 3, 5, 3],
    [0, 1, 5, 3, 0],
    [0, 2, 3, 5, 1],
    [0, 6, 1, 1, 5],
    [0, 1, 5, 5, 4]
]
let book = [];
for (let i = 0; i <= n; i++) {
    let bookChild = new Array(m + 1).fill(0);
    book[i] = bookChild;
}
console.log('book:>>', book);
// 规定 1代表左进水入口,以顺时针为规则,2为上进水入口
const dfs = (x, y, entrance) => {
    if (x === n && y === m + 1) {
        console.log('找到解决方案', JSON.parse(JSON.stringify(book)));
        return;
    }
    if (x < 0 || y < 0 || x > n || y > m) {
        return;
    }
    if (book[x][y] === 1) {
        return;
    }
    book[x][y] = 1;
    // 说明是直管
    if (a[x][y] >= 5) {
        if (entrance === 1) {
            dfs(x, y + 1, 1);
        }
        if (entrance === 2) {
            dfs(x + 1, y, 2);
        }
        if (entrance === 3) {
            dfs(x, y - 1, 3);
        }
        if (entrance === 4) {
            dfs(x - 1, y, 4);
        }
    } else {
        // 如果是弯管儿的话
        if (entrance === 1) {
            // 进水口是在左面,可以用3、4旋转状态的管
            dfs(x + 1, y, 2);   // 尝试用3,向下走
            dfs(x - 1, y, 4)    // 尝试用4,向上走
        }
        if (entrance === 2) {
            // 进水口在上面,可以用1、4 状态的管
            dfs(x, y + 1, 1);   // 尝试用1,向右走
            dfs(x - 1, y, 3)    // 尝试用4,向上走
        }
        if (entrance === 3) {
            // 进水口在右边,可以用1、2状态的管
            dfs(x - 1, y, 4);   // 尝试用1,向上走
            dfs(x + 1, y, 2)    // 尝试用2,向下走
        }
        if (entrance === 4) {
            // 进水口在下边,可以用2、3状态的管
            dfs(x, y + 1, 1)    // 尝试用2,向右走
            dfs(x, y - 1, 3);   // 尝试用3,向左走
        }
    }
    book[x][y] = 0;
    return;
}
dfs(1, 1, 1);

做这个提的时候我还遇到个问题
声明top 吧,报错,告诉我已经声明过了,在13行
在这里插入图片描述

我找到13行,发现个寂寞
在这里插入图片描述
然后我不声明吧,他给我打印一些奇怪的东西

在这里插入图片描述在这里插入图片描述
解决的办法是在把代码在外层包上一层花括号。并且用let声明一下,让top变成块级变量。

以下是加了栈的。

<script>
// 加了栈的
    {
        class Stack {
            constructor() {
                this.x;
                this.y;
            }
        };
        let n = 5, m = 4;
        let a = [
            [0, 0, 0, 0, 0],
            [0, 5, 3, 5, 3],
            [0, 1, 5, 3, 0],
            [0, 2, 3, 5, 1],
            [0, 6, 1, 1, 5],
            [0, 1, 5, 5, 4]
        ]

        // 辅助函数
        let book = [];
        for (let i = 0; i <= n; i++) {
            let bookChild = new Array(m + 1).fill(0);
            book[i] = bookChild;
        }
        console.log('book:>>', book);

        // 初始化栈
        let s = [];
        for (let i = 0; i < 30; i++) {
            let stackChild = new Stack();
            s[i] = stackChild;
        }
        let top = 0;
        console.log('top:>>', top);

        // 规定 1代表左进水入口,以顺时针为规则,2为上进水入口
        const dfs = (x, y, entrance) => {
            if (x === n && y === m + 1) {
                for (let i = 1; i <= top; i++) {
                    console.log(`(${s[i].x},${s[i].y})`);
                }
                console.log('找到解决方案', JSON.parse(JSON.stringify(book)));
                return;
            }
            if (x < 0 || y < 0 || x > n || y > m) {
                return;
            }
            if (book[x][y] === 1) {
                return;
            }
            // 标记已走过
            book[x][y] = 1;

            // 将当前点入栈
            top++;
            s[top].x = x;
            s[top].y = y;

            // 说明是直管
            if (a[x][y] >= 5) {
                if (entrance === 1) {
                    dfs(x, y + 1, 1);
                }
                if (entrance === 2) {
                    dfs(x + 1, y, 2);
                }
                if (entrance === 3) {
                    dfs(x, y - 1, 3);
                }
                if (entrance === 4) {
                    dfs(x - 1, y, 4);
                }
            } else {
                // 如果是弯管儿的话
                if (entrance === 1) {
                    // 进水口是在左面,可以用3、4旋转状态的管
                    dfs(x + 1, y, 2);   // 尝试用3,向下走
                    dfs(x - 1, y, 4)    // 尝试用4,向上走
                }
                if (entrance === 2) {
                    // 进水口在上面,可以用1、4 状态的管
                    dfs(x, y + 1, 1);   // 尝试用1,向右走
                    dfs(x - 1, y, 3)    // 尝试用4,向上走
                }
                if (entrance === 3) {
                    // 进水口在右边,可以用1、2状态的管
                    dfs(x - 1, y, 4);   // 尝试用1,向上走
                    dfs(x + 1, y, 2)    // 尝试用2,向下走
                }
                if (entrance === 4) {
                    // 进水口在下边,可以用2、3状态的管
                    dfs(x, y + 1, 1)    // 尝试用2,向右走
                    dfs(x, y - 1, 3);   // 尝试用3,向左走
                }
            }
            book[x][y] = 0;
            top--;
            return;
        }
        dfs(1, 1, 1);
    }
</script>

总结

这一章总算是结束啦,很长啊,同学们慢慢看,也要手敲一边吧(copy并没啥卵用

深度搜索主要是找停止条件,还有每一次都要干什么,把他比作成恋爱的小伙儿,那么这个人的性格就是死心塌地的跟你,直到伤透心为止,真就是不撞南墙不回头。

广度优先搜索借助数据解构,队列!把他比作成恋爱的小伙儿,那么这个人有点像 就是 花心大萝卜,大猪蹄,四周的都要喜欢一遍,并且把你当备胎,放到队列中,然后在喜欢你的闺蜜一圈儿,然后再喜欢你闺蜜的闺蜜一圈儿~,最后变成后宫王(诚哥)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值