第一节:不撞南墙不回头—深度优先搜索
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并没啥卵用 )
深度搜索主要是找停止条件,还有每一次都要干什么,把他比作成恋爱的小伙儿,那么这个人的性格就是死心塌地的跟你,直到伤透心为止,真就是不撞南墙不回头。
广度优先搜索借助数据解构,队列!把他比作成恋爱的小伙儿,那么这个人有点像 就是 花心大萝卜,大猪蹄,四周的都要喜欢一遍,并且把你当备胎,放到队列中,然后在喜欢你的闺蜜一圈儿,然后再喜欢你闺蜜的闺蜜一圈儿~,最后变成后宫王(诚哥)。