题目
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board =
[
['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']
]
给定 word = "ABCCED", 返回 true
给定 word = "SEE", 返回 true
给定 word = "ABCB", 返回 false
思路
基于回溯法,找到矩阵中和首字母相等的字符,深度优先上下左右遍历矩阵。
题目的描述中要求同一个单元格内的字母不允许被重复使用。于是很容易想到用boolean[][]数组来标记矩阵单元格的状态。基本实现如下边的代码1
代码1
class Solution {
public boolean exist(char[][] board, String word) {
char start = word.charAt(0);
boolean[][] visited = new boolean[board.length][board[0].length];
result = false;
for(int i=0;i<board.length;i++){
for(int j=0;j<board[i].length;j++){
if(board[i][j]==start){
visited[i][j] = true;
dfs(board,visited,i,j,1,word);
visited[i][j] = false;
if(result) return result;
}
}
}
return result;
}
public boolean result;
public void dfs(char[][] board,boolean[][] visited,int i,int j,int k,String word){
if(k>=word.length()){
result = true;
return;
}
char c = word.charAt(k);
if(i+1<board.length&&c==board[i+1][j]&&!visited[i+1][j]){
visited[i+1][j] = true;
dfs(board,visited,i+1,j,k+1,word);
visited[i+1][j] = false;
}
if(j+1<board[i].length&&c==board[i][j+1]&&!visited[i][j+1]){
visited[i][j+1] = true;
dfs(board,visited,i,j+1,k+1,word);
visited[i][j+1] = false;
}
if(i-1>=0&&c==board[i-1][j]&&!visited[i-1][j]){
visited[i-1][j] = true;
dfs(board,visited,i-1,j,k+1,word);
visited[i-1][j] = false;
}
if(j-1>=0&&c==board[i][j-1]&&!visited[i][j-1]){
visited[i][j-1] = true;
dfs(board,visited,i,j-1,k+1,word);
visited[i][j-1] = false;
}
}
}
分析:代码1能够基本满足题目需求,但对于leetcode中一个用例超时了,说明代码还可以优化。
优化点:
1、可以发现boolean[][]数组的作用可直接用原数组board来完成,如果board[i][j]已经被访问,则将board[i][j]赋值为某一非英文字符,并保存board[i][j]的原字符用于恢复现场。
2、代码1中有许多重复的逻辑判断(如 if(i+1<board.length&&c==board[i+1][j]&&!visited[i+1][j]) ),可以想办法将这些逻辑判断在一个地方处理(主要是考虑代码的简洁性,对时间空间复杂度并无影响)
3、在已经发现有符合条件的结果后应立即返回,而不是继续寻求其他解,可以在适当的地方判断是否已有了合适的解
于是有了下边的代码2:
代码2
class Solution {
public boolean exist(char[][] board, String word) {
char start = word.charAt(0);
result = false;
for(int i=0;i<board.length;i++){
for(int j=0;j<board[i].length;j++){
if(board[i][j]==start){
dfs(board,i,j,0,word);
if(result) return result;
}
}
}
return result;
}
public boolean result;
public void dfs(char[][] board,int i,int j,int k,String word){
if(result) return;
if(k>=word.length()){
result = true;
return;
}
char c = word.charAt(k);
if(i<0||i>=board.length||j<0||j>=board[i].length||c!=board[i][j]){
return;
}
char temp = board[i][j];
board[i][j] = '-';
dfs(board,i-1,j,k+1,word);
dfs(board,i+1,j,k+1,word);
dfs(board,i,j-1,k+1,word);
dfs(board,i,j+1,k+1,word);
board[i][j] = temp;
}
}
亮点:采用原数组来同时完成状态标记任务,降低空间复杂度