一【题目类别】
- 回溯法
二【题目难度】
- 中等
三【题目编号】
- 79.单词搜索
四【题目描述】
- 给定一个二维网格和一个单词,找出该单词是否存在于网格中。
- 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
五【题目示例】
- 示例:
board =
[
[‘A’,‘B’,‘C’,‘E’],
[‘S’,‘F’,‘C’,‘S’],
[‘A’,‘D’,‘E’,‘E’]
]
给定 word = “ABCCED”, 返回 true
给定 word = “SEE”, 返回 true
给定 word = “ABCB”, 返回 false
六【题目提示】
- b o a r d 和 w o r d 中 只 包 含 大 写 和 小 写 英 文 字 母 。 board 和 word 中只包含大写和小写英文字母。 board和word中只包含大写和小写英文字母。
- 1 < = b o a r d . l e n g t h < = 200 1 <= board.length <= 200 1<=board.length<=200
- 1 < = b o a r d [ i ] . l e n g t h < = 200 1 <= board[i].length <= 200 1<=board[i].length<=200
- 1 < = w o r d . l e n g t h < = 1 0 3 1 <= word.length <= 10^3 1<=word.length<=103
七【解题思路】
- 使用回溯+剪枝的方法,遍历整个表格,从每一个位置开始作为起点判断和word是否相等,递归的时候通过新组成的路径字符和word开始是否一样,如果不一样就返回false,这就是剪枝操作。遇到一个字符加入到路径字符,并将对应的位置置为true(表示已经访问过了),防止二次遍历,接下来就是向上下左右寻找的过程,最后回溯的时候说明上下左右都没有由当前字符组成的路径字符,那么回到上一个状态,将刚加入的字符删除,另外将对应的位置置为true(表示还没有访问过)
八【时间频度】
- 时间复杂度: M M M:表示表格的宽度, N N N:表示表格的长度, L L L:表示word的长度,另外只有第一次遍历需要走四个方向,其余最多走三个方向,因为走过的方向不能再走了,所以时间复杂度为 O ( M ∗ N ∗ 3 L ) O(M*N*3^L) O(M∗N∗3L)
九【代码实现】
- Java语言版
package ToFlashBack;
public class p79_WordSearch {
// 用于存放坐标方向,分别运动方向是上,右,下,左
private static int[][] dires = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
// 定义行数和列数
private int row, col;
// 定义是否找到的标志位
private boolean hasFind;
// 定义是否访问过的坐标的二维数组
private boolean[][] visited;
public static void main(String[] args) {
p79_WordSearch p79_wordSearch = new p79_WordSearch();
char[][] board = {
{'A', 'B', 'C', 'E'},
{'S', 'F', 'C', 'S'},
{'A', 'D', 'E', 'E'}
};
String word = "ABCCED";
boolean res = p79_wordSearch.exist(board, word);
if (res) {
System.out.println("找到!");
} else {
System.out.println("未找到!");
}
}
/**
* 判断主方法
*
* @param board 初始二维数组
* @param word 准备查找的字符串
* @return
*/
public boolean exist(char[][] board, String word) {
// 行数
row = board.length;
// 列数
col = board[0].length;
// 初始标志位为false,没有找到
hasFind = false;
// 如果行数和列数乘积小于整个字符串的长度直接返回false
if (row * col < word.length()) {
return false;
}
// 初始化是否访问的二维数组大小为board的大小
visited = new boolean[row][col];
// 把单词字符串转为对应的字符串一维数组
char[] chars = word.toCharArray();
// 双层for循环,遍历board的每一个单词
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
// 如果当前board中的单词等于word中的单词,那么就开始进入下一个方法
if (board[i][j] == chars[0]) {
backTrack(board, chars, 1, i, j);
// 如果已经判断出结果,则返回true
if (hasFind) {
return true;
}
}
}
}
// 如果进入for循环没有任何返回结果,就返回false
return false;
}
/**
* 查找的核心方法:使用递归+回溯
*
* @param board 初始的二维数组
* @param word 待查找的字符数组
* @param curIndex 判断的索引值,每次都+1
* @param x 横坐标,每次都是newX
* @param y 纵坐标,每次都是newY
*/
private void backTrack(char[][] board, char[] word, int curIndex, int x, int y) {
// 如果标志位为true,直接返回,这个也是递归的结束条件
if (hasFind) {
return;
}
// 如果临时索引值和单词数组长度相等,说明已经找到了最后一个单词,则将标志位置为true,返回
if (curIndex == word.length) {
hasFind = true;
return;
}
// 因为传进来的x和y就是当前坐标值,说明已经走过了
visited[x][y] = true;
// 使用增强for循环遍历方向数组
for (int[] dire : dires) {
// 更新坐标位置,并按照上->右->下->左的方向,依次开始递归
int newX = x + dire[0];
int newY = y + dire[1];
// 如果满足,更新后的坐标在二维数组中,并且没有访问过,并且更新后的坐标值和字符数组的下一个单词相等,则进入递归,如果不满足条件,则依次使用下面的方向继续进行递归
if (isIn(newX, newY) && !visited[newX][newY] && board[newX][newY] == word[curIndex]) {
backTrack(board, word, curIndex + 1, newX, newY); // 这里curIndex+1,意味着更新索引值,下面就判断字符数组的下一个单词
}
}
// 将判断是否访问过的二维数组恢复
visited[x][y] = false;
}
/**
* 判断当前坐标是否在二维数组中
*
* @param x 横坐标
* @param y 纵坐标
* @return
*/
private boolean isIn(int x, int y) {
return x >= 0 && x < row && y >= 0 && y < col;
}
}
- C语言版
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
bool p79_WordSearch_backTrack(char** board, int** visited, char* word, int wordIndex, char* path, int pathIndex, int x, int y, int row, int col)
{
/*剪枝操作:边界值或者当前位置已经访问过或者当前字符和待匹配字符不相等直接返回false*/
if (x < 0 || y < 0 || x >= row || y >= col || visited[x][y] || board[x][y] != word[wordIndex])
{
return false;
}
/*递归跳出条件:这步必须放在剪枝操作的后面,必须先判断当前字符和待匹配字符相等,如果相等且将待匹配字符串都已经匹配过了,递归跳出,返回true*/
if (wordIndex + 1 == strlen(word))
{
return true;
}
/*将当前字符加入到字符路径中*/
path[pathIndex++] = board[x][y];
/*将当前位置的字符置为已访问*/
visited[x][y] = 1;
/*分别向右上下左四个方向继续匹配*/
if (p79_WordSearch_backTrack(board, visited, word, wordIndex + 1, path, pathIndex, x, y + 1, row, col))
{
return true;
}
else if (p79_WordSearch_backTrack(board, visited, word, wordIndex + 1, path, pathIndex, x - 1, y, row, col))
{
return true;
}
else if (p79_WordSearch_backTrack(board, visited, word, wordIndex + 1, path, pathIndex, x + 1, y, row, col))
{
return true;
}
else if (p79_WordSearch_backTrack(board, visited, word, wordIndex + 1, path, pathIndex, x, y - 1, row, col))
{
return true;
}
/*回溯操作:如果右上下左四个方向均没有匹配成功返回到上一个状态,并且返回false,说明当前路径不通,也就是没有找到*/
else
{
path[--pathIndex] == 0;
visited[x][y] = 0;
return false;
}
}
bool exist(char** board, int boardSize, int* boardColSize, char * word)
{
/*获取表格长度和宽度*/
int row = boardSize;
int col = boardColSize[0];
/*边界条件直接返回false*/
if (row * col < strlen(word) || strlen(word) == 0)
{
return false;
}
/*设置访问二维数组,防止二次遍历*/
int** visited = (int**)malloc(row * sizeof(int*));
for (int i = 0; i < row; i++)
{
visited[i] = (int*)calloc(col, sizeof(int));
}
/*路经数组,存放满足条件的字符串*/
char* path = (char*)malloc(strlen(word) * sizeof(char));
for (int i = 0; i < strlen(word); i++)
{
path[i] = 0;
}
/*遍历每一个位置作为起点*/
for (int x = 0; x < row; x++)
{
for (int y = 0; y < col; y++)
{
/*找到一条满足条件的路径就返回true*/
if (p79_WordSearch_backTrack(board, visited, word, 0, path, 0, x, y, row, col))
{
return true;
}
}
}
/*一条满足条件的路径都没找到返回false*/
return false;
}
/*主函数省略*/
十【提交结果】
-
Java语言版
-
C语言版