79. Word Search

本文介绍LeetCode 79题单词搜索的解题思路与实现方法,采用深度优先搜索算法解决二维网格中查找指定单词的问题,并提供C++及Python代码实现。

题目

Given a 2D board and a word, find if the word exists in the grid.

The word can be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.

Example

board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]

Given word = "ABCCED", return true.
Given word = "SEE", return true.
Given word = "ABCB", return false.

解法

遇到这道题的时候,想得就是应该要用递归来进行不断的搜索,但是尝试了一下,并没有做出来。还是太渣了。

于是上网去查,看了 youtube 上花花酱的视频,理解这道题应该怎么做了。

[外链图片转存失败(img-3BkEtWxo-1567534655415)(http://ozumifmjd.bkt.clouddn.com/leetcode%2079%281%29.png)]

上面是题目给出例子的图解,起点用红色标记出来,终点用绿色,过程为黄色。

本质是一道搜索题,使用深度优先搜索更合适。因为最终要找一个唯一解,且解的长度是固定的。深度不是很大额时候,虽然 DFS 和 BFS 时间复杂度相同,但 DFS 的空间复杂度要更优。

下面是伪代码:
[外链图片转存失败(img-0UiL2WsJ-1567534655417)(http://ozumifmjd.bkt.clouddn.com/leetcode79%282%29.png)]

先找一个起点,用枚举的方式。从这个起点开始递归搜索。serach(x,y,0)表示从首字母开始搜索。

下面的 search 函数搜索第 d 个点,有三种情况:

  1. (x,y)超过格子的范围,return fasle
  2. 当前格子上的值和要找的字符不匹配,return false
  3. 当前格子上的值和要找的字符匹配,且该字符是最后一个,说明完成了,return true

剩下的还没有完成,就接着上下左右递归搜索吧。

时间复杂度

每一次都会有向四个方向的搜索,且最大深度为 l(l=len(word)),最坏情况下时间复杂度为 O( 4 l 4^l 4l)。要在起点枚举所有的可能性(格子大小为 m 行 n 列),所以整个时间复杂度为 O(mn 4 l 4^l 4l)。

而实际上不会有这么复杂,像第一个例子,只有一条路径,没有其他的分支,复杂度是线性的。

空间复杂度

空间复杂度就是其递归深度,递归了 l 层,加上初始的 m 行 n 列,总共是 O(m*n+1)

代码

花花酱的 C++代码:

class Solution {
public:
    bool exist(vector<vector<char>> &board, string word) {
        if(board.size()==0) return false;
        h = board.size();
        w = board[0].size();
        for(int i=0;i<w;i++)
            for(int j=0;j<h;j++)
                if(search(board, word, 0, i, j)) return true;
        return false;
    }

    bool search(vector<vector<char>> &board,
             const string& word, int d, int x, int y) {
        if(x<0 || x==w || y<0 || y==h || word[d] != board[y][x])
            return false;

        // Found the last char of the word
        if(d==word.length()-1)
            return true;

        char cur = board[y][x];
        board[y][x] = 0;
        bool found = search(board, word, d+1, x+1, y)
                  || search(board, word, d+1, x-1, y)
                  || search(board, word, d+1, x, y+1)
                  || search(board, word, d+1, x, y-1);
        board[y][x] = cur;
        return found;
    }
private:
    int w;
    int h;
};

python 代码:

def exist(self, board, word):
        """
        :type board: List[List[str]]
        :type word: str
        :rtype: bool
        """
       
        if len(board) == 0 or len(board[0]) == 0 or len(word)==0:return False
        for i in range(len(board)):
            for j in range(len(board[0])):
                if self.test_solution(board,i,j,0,word):return True
        return False

    def test_solution(self,board,i,j,k,word):
        if i<0 or j<0 or i>=len(board) or j>=len(board[0]) or board[i][j]!=word[k]:return False
        board[i][j] = self.token
        if k==len(word)-1:return True
        if self.test_solution(board,i-1,j,k+1,word) or self.test_solution(board,i,j-1,k+1,word) or self.test_solution(board,i+1,j,k+1,word) or self.test_solution(board,i,j+1,k+1,word):return True
        board[i][j] = word[k]
        return False

花花酱的博客
我的博客

<think>首先,用户的问题是比较两个SQL查询语句的区别,特别是分页LIMIT的使用:一个是LIMIT0,20,另一个是LIMIT1,20。我需要回顾系统级指令:-所有行内数学表达式使用$...$格式。 -独立公式使用$$...$$格式并单独成段。 -LaTeX语法正确。-使用中文回答。-生成相关问题。 -在回答中引用的段落末尾自然地添加引用标识,例如[^1]。用户可见层的指令:-回答结构清晰,帮助用户逐步解决问题。 -保证回答真实可靠。参考提供的引用: -[^1]:讨论了分页插件的使用和ES查询的问题。- [^2]:解释了LIMIT的用法:select* fromtable limit10,10;表示从第11条开始取10条。 -[^3]:描述了LIMIT的两种用法:一个参数(LIMIT n)表示前n条,两个参数(LIMIT offset, count)表示从offset开始取count条。 -[^4]:给出了示例:limit0,10和limit10都表示第一页10条数据。- [^5]:展示了Java中的分页实现。用户的问题核心是比较LIMIT0,20和 LIMIT1,20的区别。关键点:- LIMIT子句用于分页查询。 -语法:LIMIToffset,count或 LIMIT countOFFSET offset。 -在LIMIToffset,count中:- offset是起始行的索引(从0开始)。-count是返回的行数。所以:- LIMIT0,20:offset=0, count=20。表示从第一行开始取20行,即第1到第20行。- LIMIT1,20:offset=1,count=20。表示从第二行开始取20行,即第2到第21行。 区别: -LIMIT0,20返回结果的前20行。- LIMIT1,20返回从第二行开始的20行,因此跳过了第一行。 在分页上下文中: -第一页通常使用 LIMIT0,20或 LIMIT20(因为LIMIT count等价于 LIMIT0,count)。-第二页使用 LIMIT20,20(offset=20),以此类推。用户提到了“比较两个SQL查询语句的不同之处”,所以我需要清晰地解释这个区别。 回答结构:1.**引言**:简要介绍LIMIT子句的作用。 2. **解释语法**:使用行内数学表达式解释offset和count。 3. **比较两个查询**:-描述LIMIT0,20。-描述LIMIT1,20。-指出关键区别:起始行的不同。 4. **示例**:提供一个简单的例子来演示。5.**分页上下文**:解释在分页中如何应用。 6. **生成相关问题**:在结尾添加相关问题。7.**引用**:如果引用信息,添加引用标识。确保使用中文。生成相关问题: -根据用户的问题,生成2-3个相关问题。 -例如:1.在SQL中,LIMIT子句的offset参数为什么从0开始计数?2.如何优化LIMIT子句在大型数据集上的性能?3.在分页查询中,如何处理最后一页的记录不足问题? 现在,整合引用: -引用[^2]和[^3]直接相关,解释了LIMIT的用法。 -在回答中自然地添加引用,比如在解释语法时引用[^3]。最终回答框架: -开头:直接回答问题。-主体:逐步解释。 -结尾:生成相关问题。</think>### SQL查询中LIMIT子句的区别分析 您的查询涉及比较两个SQL语句:`LIMIT 0,20` 和 `LIMIT 1,20`。核心区别在于`LIMIT`子句的**offset参数**(起始行索引),这会影响返回数据的起始位置。在SQL中,`LIMIT`用于分页查询,语法格式通常为`LIMIT offset, count`,其中: - `offset` 表示起始行的索引(从0开始计数)。 - `count` 表示要返回的行数。 使用行内数学表达式表示:设查询结果集的行索引为 $i = 0,1,2,\ldots,n-1$,则 `LIMIT offset, count` 会返回子集 $\{i \mid \text{offset} \leq i < \text{offset} + \text{count}\}$[^3]。 下面我逐步解释这两个查询的区别,确保回答清晰可靠。 #### 1. **LIMIT 0,20 的含义** - `offset = 0`:表示从结果集的**第一行**开始(索引为0)。 - `count = 20`:表示返回20行数据。 - 因此,`LIMIT 0,20` 返回结果集的**第1行到第20行**(总共20行)。这相当于第一页的数据,常用于分页查询的首页[^4]。 - 示例:如果表中有100条记录,执行 `SELECT * FROM table LIMIT 0,20;` 会返回最前面的20条记录。 #### 2. **LIMIT 1,20 的含义** - `offset = 1`:表示从结果集的**第二行**开始(索引为1)。 - `count = 20`:表示返回20行数据。 - 因此,`LIMIT 1,20` 返回结果集的**第2行到第21行**(总共20行)。它跳过了第一行,从第二行开始取数据。 - 示例:同样表中有100条记录,执行 `SELECT * FROM table LIMIT 1,20;` 会返回第2条到第21条记录,而不是从开头开始。 #### 3. **关键区别总结** - **起始位置不同**:`LIMIT 0,20` 从第一行开始,而 `LIMIT 1,20` 从第二行开始。这意味着: - `LIMIT 0,20` 包含结果集的起始行。 - `LIMIT 1,20` 排除了第一行,可能用于跳过表头或特定记录。 - **分页影响**:在分页场景中: - `LIMIT 0,20` 通常表示**第一页**(例如,页码1)。 - `LIMIT 1,20` 可能表示**第二页的起始**,但它会少取一行(因为offset=1),这可能导致分页逻辑错误(如总行数计算不准确)。标准分页公式应为 `LIMIT (page-1)*page_size, page_size`,例如第二页是 `LIMIT 20,20`(offset=20),而不是offset=1[^2][^5]。 - **性能考虑**:offset值越大(如 `LIMIT 1000,20`),数据库需要扫描更多行才能跳过offset,可能导致性能下降。优化时,建议结合索引或避免高offset查询[^1]。 #### 4. **简单示例演示** 假设一个表 `products` 有5条记录: ```sql SELECT * FROM products; -- 输出: -- id: 1, name: A -- id: 2, name: B -- id: 3, name: C -- id: 4, name: D -- id: 5, name: E ``` - `SELECT * FROM products LIMIT 0,2;` 返回: ``` id: 1, name: A id: 2, name: B ``` - `SELECT * FROM products LIMIT 1,2;` 返回: ``` id: 2, name: B id: 3, name: C ``` 这里,`LIMIT 1,2` 跳过了id=1的记录,从第二行开始返回两行。 #### 5. **实际应用建议** - **分页规范**:在分页查询中,始终使用 `LIMIT (page-1)*page_size, page_size` 格式。例如,第一页用 `LIMIT 0,20`,第二页用 `LIMIT 20,20`,避免使用 `LIMIT 1,20` 这类非标准offset,以防数据遗漏或逻辑混乱[^2][^5]。 - **错误避免**:如引用[^1]所述,分页插件误用可能导致LIMIT残留(如线程复用引入 `LIMIT 1000`)。建议在非SQL数据源(如Elasticsearch)时直接使用其分页机制,而非强制SQL LIMIT。 - **性能优化**:对于大数据集,考虑基于游标或条件过滤(如 `WHERE id > last_id`)来替代高offset,减少扫描开销[^1]。 如果您有具体数据库环境(如MySQL或PostgreSQL),我可以提供更针对性的示例。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值