开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 16 天,点击查看活动详情
题目
剑指 Offer 12. 矩阵中的路径
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
分析
该题属于基础不好答案都要看半天的类型>_<
这里说下自己第一次做出来时的解题思路吧
- 先把题目简化,把复杂的逻辑判断先放一边,简单列举出边界,遍历整个矩阵,于是可得
function exist(board: string[][], word: string): boolean {
let wMax = board[0].length
let hMax = board.length
for (let i = 0; i < hMax; i ++) {
for (let j = 0; j < wMax; j ++) {
// 判断逻辑。。。
}
}
};
-
进一步分析题目要求,按顺序连接字母单词,即可转化为问题:
判断矩阵上每个出发是否满足要求,那我们在写一个方法,对每个点进行判断,只要找到满足要求的点,反回既是所求
function exist(board: string[][], word: string): boolean {
let wMax = board[0].length
let hMax = board.length
// 判断符合条件的点
function check (i, j) {
// ...
}
for (let i = 0; i < hMax; i ++) {
for (let j = 0; j < wMax; j ++) {
// 判断逻辑。。。
if (check(i, j)) {
return true
}
}
}
};
现在问题的大头就是 check 函数逻辑如何写了
- 确定需要传入的参数,位置索引必传,用于获取格子上的字母,还有就是当前的字母索引,用于两个值对比
function check(i, j, index) {
let cur = board[i][j]
let key = word[index]
let result = false
if (cur == key) {
result = true
return result
}
return result
}
这个时候一个迭代函数的雏形就出来了,我们按照迭代的方法完善这个函数
- 确定终止条件
如果 index 大于字母长度,则说明迭代完存在满足条件的字符链
如果 i 或 j 超过边界,则说明该点不满足要求
if (index == word.length) {
return true
}
if (i >= hMax || j >= wMax || i < 0 || j < 0) {
return false
}
如果该点已经访问过,那么该点也不满足
这里有个麻烦的点就是如何判断某个点是否已经访问过
在答案的启发下,我构造了一个 board 同大小的数组,用来存放每个点的访问情况
let visited = new Array(hMax)
for (let n = 0 ; n < hMax; n ++) {
visited[n] = new Array(wMax).fill(false)
}
每进入一个点,将该点值设为 true,如果迭代结果为 false,需要重新把该点改为 false
- 确定返回值
如果当前坐标满足要求,需要递归判断下一个点(上下左右)是否满足要求,直到退出
那么有
result = check(i + 1, j, index) ||
check(i - 1, j, index) ||
check(i, j + 1, index) ||
check(i, j - 1, index)
最终完善递归逻辑
let cur = board[i][j]
let key = word[index]
let result = false
if (cur == key) {
index ++
visited[i][j] = true
result = check(i + 1, j, index) ||
check(i - 1, j, index) ||
check(i, j + 1, index) ||
check(i, j - 1, index)
}
if (result) {
return true
} else {
visited[i][j] = false
return false
}
实现
function exist(board: string[][], word: string): boolean {
let wMax = board[0].length
let hMax = board.length
let visited = new Array(hMax)
for (let n = 0 ; n < hMax; n ++) {
visited[n] = new Array(wMax).fill(false)
}
// check 判断某一个点能否从头到尾连接单词
function check(i, j, index) {
if (index == word.length) {
return true
}
if (i >= hMax || j >= wMax || i < 0 || j < 0) {
return false
}
if (visited[i][j]) {
return false
}
let cur = board[i][j]
let key = word[index]
let result = false
if (cur == key) {
index ++
visited[i][j] = true
result = check(i + 1, j, index) ||
check(i - 1, j, index) ||
check(i, j + 1, index) ||
check(i, j - 1, index)
}
if (result) {
return true
} else {
visited[i][j] = false
return false
}
}
for (let i = 0; i < hMax; i ++) {
for (let j = 0; j < wMax; j ++) {
if (check(i, j, 0)) {
return true
}
}
}
return false
};