1.15 LeetCode总结(基本算法)_DFS之二维平面上的搜索问题

二维搜索DFS经典题总结
本文深入探讨了迷宫和岛屿主题的算法问题,包括单词搜索、迷宫基础与进阶、岛屿周长和数量计算等。通过DFS和BFS等技术,解析了各类问题的解决策略和代码实现细节。

编程总结

每每刷完一道题后,其思想和精妙之处没有地方记录,本篇博客用以记录刷题过程中的遇到的算法和技巧

79. 单词搜索_一刷 on 2025.11.08

思路:DFS遍历下去,都是套路
1)递归终止条件:越界、之前访问过、字符不相等
2)循环退出条件:字符长度已经和输入字符长度相等,返回true
3)逻辑处理,注意回溯的变量处理,赋值后,需要再减回来,visit数组

给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

在这里插入图片描述

DFS

int gVisit[NUM_128][NUM_128] = {
   
   0};
int gDirection[4][2] = {
   
   {
   
   0, -1}, {
   
   0, 1}, {
   
   1, 0}, {
   
   -1, 0}};
int  gBoardSize;
int  *gBoardColSize;
char *gWord;
char **gBoard;
bool DFS(int i, int j, int idx)
{
   
   
    if ((i < 0) || (j < 0) || (i >= gBoardSize) || (j >= *gBoardColSize)) {
   
   
        return false;
    }
    if (gBoard[i][j] != gWord[idx]) {
   
   
        return false;
    }
    if (gVisit[i][j] == 1) {
   
   
        return false;
    }
    if ((idx + 1) == strlen(gWord)) {
   
   
        return true;
    }
    gVisit[i][j] = 1;
    for (int dir = 0; dir < 4; dir++) {
   
   
        int nextX, nextY;
        nextX = i + gDirection[dir][0];
        nextY = j + gDirection[dir][1];
        bool ret = DFS(nextX, nextY, idx + 1);
        if (ret == true) {
   
   
            return true;
        }
    }
    gVisit[i][j] = 0;
    return false;
}
bool exist(char **board, int boardSize, int *boardColSize, char *word)
{
   
   
    gBoard = board;
    gBoardSize = boardSize;
    gBoardColSize = boardColSize;
    gWord = word;
    bool ret = false;
    for (int i = 0; i < NUM_128; i++) {
   
   
        memset(gVisit[i], 0, sizeof(int) * NUM_128);
    }
    for (int i = 0; i < boardSize; i++) {
   
   
        for (int j = 0; j < *boardColSize; j++) {
   
   
            if (board[i][j] == word[0]) {
   
   
                ret = DFS(i, j, 0);
                if (ret == true) {
   
   
                    return true;
                }
            }
        }
    }
    return false;
}
int main()
{
   
   
    char **board = (char **)malloc(sizeof(char *) * 5);
    for (int i = 0; i < 4; i++) {
   
   
        board[i] = (char *)malloc(sizeof(char) * 5);
    }
    strcpy(board[0], "ABCE");
    strcpy(board[1], "SFCS");
    strcpy(board[2], "ADEE");
    int boardSize = 4;
    int boardColSize = 4; 
    char word[7] = "ABCB";
    bool ret = exist(board, boardSize, &boardColSize, word);
    return 0;
}

BFS_部分通过_此题不建议使用

本题使用 BFS 并不适用,因为当前BFS的遍历不会回溯(将 gVisit 变量重新赋值0),会有如下情况导致部分用例Failed;
BFS更偏向无脑横向去遍历

1)访问到“C”时,此时会遍历访问两个“E”进队列,并标记已经访问过(由于目标此时就是匹配字符 “E”)
在这里插入图片描述
2)由于已经进队列并做了访问标记,并不会处理回溯,导致后续无法实现答案这种访问
在这里插入图片描述

int head = 0;
int tail = 0;
int gVisit[NUM_128][NUM_128] = {
   
   0};
int gDirection[4][2] = {
   
   {
   
   0, -1}, {
   
   0, 1}, {
   
   1, 0}, {
   
   -1, 0}};
int  gBoardSize;
int  *gBoardColSize;
char *gWord;
char **gBoard;
typedef struct Node {
   
   
	int x;
	int y;
} Node;
Node queue[NUM_128] = {
   
   0};
bool BFS(int i, int j, int idx)
{
   
   
    int step = 1;
	while (head != tail) {
   
   
		int tmpX = queue[head].x;
		int tmpY = queue[head].y;
		head = (head + 1) % NUM_128;
		if ((step) == strlen(gWord)) {
   
   
			return true;
		}
		for (int dir = 0; dir < 4; dir++) {
   
   
            int nextX, nextY;
            nextX = tmpX + gDirection[dir][0];
            nextY = tmpY + gDirection[dir][1];
            // 有效则进队列
			if ((nextX >= 0) && (nextX < gBoardSize) && (nextY >= 0) && (nextY < *gBoardColSize) && (gVisit[nextX][nextY] == 0) && (gBoard[nextX][nextY] == gWord[step])) {
   
   
				queue[tail].x = nextX;
				queue[tail].y = nextY;
				printf("queue %d %d %c\n", nextX, nextY, gBoard[nextX][nextY]);
				tail = (tail + 1) % NUM_128;
				gVisit[nextX][nextY] = 1;
			}
		}
        step = step + 1;
	}
	return false;
}

bool exist(char **board, int boardSize, int *boardColSize, char *word)
{
   
   
    gBoard = board;
    gBoardSize = boardSize;
    gBoardColSize = boardColSize;
    gWord = word;
    bool ret = false;
    for (int i = 0; i < NUM_128; i++) {
   
   
        memset(gVisit[i], 0, sizeof(int) * NUM_128);
    }
    for (int i = 0; i < boardSize; i++) {
   
   
        for (int j = 0; j < *boardColSize; j++) {
   
   
            if (board[i][j] == word[0]) {
   
   
                queue[tail].x = i;
                queue[tail].y = j;
                gVisit[i][j] = 1;
                tail = (tail + 1) % NUM_128;
                printf("queue %d %d %c\n", i, j, gBoard[i][j]);
                ret = BFS(i, j, 0);
                if (ret == true) {
   
   
                    return true;
                }
            }
        }
    }
    return false;
}
int main()
{
   
   
    char **board = (char **)malloc(sizeof(char *) * 5);
    for (int i = 0; i < 4; i++) {
   
   
        board[i] = (char *)malloc(sizeof(char) * 5);
    }
    strcpy(board[0], "ABCE");
    strcpy(board[1], "SFES");
    strcpy(board[2], "ADEE");
    int boardSize = 4;
    int boardColSize = 4; 
    char word[11] = "ABCESEEEFS";
    bool ret = exist(board, boardSize, &boardColSize, word);
    return 0;
}

迷宫_基础

玩走迷宫的游戏.在一个n*m格子状的迷宫中,有一个入口S和多个出口E(一个迷宫中可能有多个出口,都用E表示).0表示这个格子可以走,1表示这个格子不可以走.小华希望可以最快的走出迷宫.每次只能走一步(在格子可以走的情况下,向上下左右移动一个格子), 他想让你计算最少的步数.
如:
1 0 1
0 S 1
0 0 E
输出2
如果无法到达出口输出 “No way”

思路:DFS遍历下去,都是套路
1)递归终止条件:越界、之前访问过、字符不相等
2)循环退出条件:字符长度已经和输入字符长度相等,返回true
3)逻辑处理,注意回溯的变量处理,赋值后,需要再减回来,visit数组

思路:通过DFS四个方向来做,gStep使用全局变量更新状态,step随递归深度进入到形参里
同时,加了gLen用于剪枝,gLen不为0时,同时超过之前存储的步数直接返回。

#define MAX_N   101
#define MAX_M   101
#define MAX_W   101
#define NUM_BIG 0x7FFFFFFF
char gMaze[MAX_N][MAX_M]  = {
   
   0}; // 二维矩阵,存储矩阵字符串
int  gVisit[MAX_N][MAX_M] = {
   
   0}; // 记录已经访问过的点,用int类型存储
int  gLen[MAX_N][MAX_M]   = {
   
   0}; // 记录访问过的点的步数
int  gResult = 0;
int  gStep = NUM_BIG;
int  gDirection[4][2] = {
   
   {
   
   1,0}, {
   
   -1,0}, {
   
   0,1}, {
   
   0,-1}};

void DFS(int x, int y, int step, int n, int m)
{
   
   
    // 如果已经满足条件,到达出口E,直接返回1,退出
    if (gMaze[x][y] == 'E') {
   
   
        gResult = step;
        gStep = gStep < gResult ? gStep : gResult; // 更新step值
        return ;
    }
    // 1.已经访问过该点,直接返回0,退出
    if (gVisit[x][y] == 1) {
   
   
        return ;
    }
    // 2.坐标越界,直接返回0,退出
    if (x < 0 || y < 0 || x >= n || y >= m) {
   
   
        return ;
    }
    // 3.格子不能走,直接返回0,退出
    if (gMaze[x][y] == '1') {
   
   
        return ;
    }
    // 4.剪枝,通过构造数组记录之前达到每一点需要的步数,如果超过可以步骤直接返回,不用再遍历
    if (gLen[x][y] && gLen[x][y] <= step) {
   
    // gLen不为空且step大于gLen
        return ;
    }
    // 存贮步数点
    gLen[x][y] = step;
    // 标记为已访问点
    gVisit[x][y] = 1;
    // 以下上右左的顺序进行遍历查找
    for (int i = 0; i < 4; i++) {
   
   
        DFS(x + gDirection[i][
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值