力扣第20题记录

一、题目:20. 有效的括号
有效的括号
二、题目解析:

题目解析:该题需要用到数据结构当中的stack栈 (特点:后进先出)
解题步骤:

  1. 创建一个HashMap,把括号配对放进去,维护匹配关系,{“(”:“)”}
  2. 创建一个stack,for循环遍历字符串,对于每一个字符,如果map里有这个key,那说明它是一个左括号,从map里取得相对应的右括号
    (为什么?)因为计算机不知道这些括号配对关系,需要我们自己处理,把它push进stack里。
    否则的话,它就是右括号,需要pop出stack里的第一个字符,然后看它是否等于当前的字符。如果不相符,则返回false
  3. 循环结束后,如果stack不为空,说明还剩一些左括号没有被闭合,返回false。否则返回true。

图示帮助理解:
在这里插入图片描述

三、代码如下:

public boolean isValid(String s){
        Map<Character,Character> map = new HashMap<Character, Character>(16);
        map.put('(',')');
        map.put('[',']');
        map.put('{','}');
        Stack<Character> stack = new Stack<Character>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if(map.containsKey(c)){
                stack.push(map.get(c));
            }else{
                if(stack.size() == 0 || stack.pop() != c){
                    return false;
                }
            }
        }
        // 遍历完栈里面还有元素,说明还有没匹配的元素,则返回false
        if(stack.size() != 0){
            return false;
        }
        return true;
    }

四、测试
有效的括号

五、结束

<think>我们正在解决LeetCode第10:正则表达式匹配。目要求实现一个支持 '.' 和 '*' 的正则表达式匹配。其中 '.' 匹配任意单个字符,'*' 匹配零个或多个前面的元素。我们将使用动态规划方法解决。 动态规划思路: 1. 定义状态:dp[i][j] 表示字符串 s 的前 i 个字符和模式 p 的前 j 个字符是否匹配。 2. 初始化:dp[0][0]为True,表示两个空字符串匹配。然后需要处理模式串中可能出现的能够匹配空串的情况(比如'a*'可以出现0次,从而匹配空串)。 3. 状态转移: a. 如果当前模式字符是'*'(注意,目保证'*'前面一定有一个有效字符,所以模式串中'*'不会出现在开头): - 我们可以选择匹配0次:即忽略模式串中'*'和它前面的那个字符,此时dp[i][j] = dp[i][j-2] - 或者匹配一次或多次:此时需要满足模式中'*'前面的字符能够匹配当前s的字符(即相等或为'.'),并且dp[i-1][j]为True(即s的前i-1个字符和当前模式的前j个字符匹配,那么加上当前字符仍然匹配,因为'*'可以匹配多个) - 所以:dp[i][j] = dp[i][j-2] or ( (s[i-1]匹配p[j-2]) and dp[i-1][j] ) b. 如果当前模式字符不是'*',那么直接检查当前字符是否匹配(即相等或模式为'.'),并且前面的部分也匹配:dp[i][j] = (s[i-1]匹配p[j-1]) and dp[i-1][j-1] 4. 最终结果:dp[m][n],其中m为s的长度,n为p的长度。 下面是对代码的逐行解释,同时会穿插讲解Python语法。 ```python class Solution: def isMatch(self, s: str, p: str) -> bool: # 获取字符串s和模式串p的长度 m, n = len(s), len(p) # 创建一个二维列表dp,维度为(m+1)行×(n+1)列,初始值全为False dp = [[False] * (n+1) for _ in range(m+1)] # 初始化:两个空字符串匹配 dp[0][0] = True # 处理模式串p中可能使得空字符串匹配的情况,即模式串中有类似"a*"的组合,可以匹配0次 # 注意:这里j从2开始,因为'*'前面必须有一个字符,所以至少两个字符才能出现'*' for j in range(2, n+1): # 如果当前模式字符是'*',那么我们可以忽略它和它前面的字符(即匹配0次) if p[j-1] == '*': # 那么dp[0][j]的值就等于忽略这两个字符(即j-2)的值 dp[0][j] = dp[0][j-2] # 开始填充dp表,i从1到m,j从1到n for i in range(1, m+1): for j in range(1, n+1): # 如果当前模式字符是'*' if p[j-1] == '*': # 匹配0次:直接跳过模式串中'*'和它前面的字符,即状态转移到dp[i][j-2] # 或者匹配1次或多次:需要满足'*'前面的字符(即p[j-2])能够匹配s[i-1](即当前字符) # 并且,s的前i-1个字符和当前模式的前j个字符要匹配(因为'*'可以匹配多个,所以这里j不变,而i-1表示去掉当前字符) dp[i][j] = dp[i][j-2] or ( (p[j-2] == '.' or p[j-2] == s[i-1]) and dp[i-1][j] ) # 如果当前模式字符不是'*',则检查当前字符是否匹配 else: # 当前字符匹配:即模式字符是'.'或者与字符串字符相等 # 并且,前面的部分(dp[i-1][j-1])也要匹配 if p[j-1] == '.' or p[j-1] == s[i-1]: dp[i][j] = dp[i-1][j-1] # 否则,dp[i][j]保持为False(由于初始化就是False,所以这里可以不写else分支) # 返回整个字符串和模式串的匹配结果 return dp[m][n] ``` ### Python语法知识点讲解 1. **类定义**:`class Solution:` 定义了一个名为`Solution`的类。在LeetCode中,通常要求以类的形式提交代码。 2. **方法定义**:`def isMatch(self, s: str, p: str) -> bool:` - `def` 是定义函数(或方法)的关键字。 - `isMatch` 是方法名。 - `self` 是类方法的第一个参数,表示类的实例(在调用时不需要传入,由Python自动传入)。 - `s: str` 和 `p: str` 是参数的类型注解,表示`s`和`p`是字符串类型(但Python是动态语言,这只是一个提示,不会强制检查)。 - `-> bool` 表示该函数返回一个布尔值(`True`或`False`)。 3. **变量赋值**:`m, n = len(s), len(p)` - 同时为两个变量赋值,`len()`函数返回字符串的长度。 4. **二维列表的创建**: ```python dp = [[False] * (n+1) for _ in range(m+1)] ``` - 这里使用了列表推导式(list comprehension)来创建二维列表。 - `[False] * (n+1)` 创建一个长度为`n+1`的列表,每个元素都是`False`。 - 外层`for _ in range(m+1)`表示循环`m+1`次,每次产生一行。`_`是一个常用的占位符,表示我们不关心循环变量的值。 5. **循环语句**: ```python for j in range(2, n+1): ``` - `range(start, stop)` 生成一个整数序列,从`start`开始,到`stop-1`结束。例如,`range(2,5)`生成`[2,3,4]`。 6. **条件语句**: ```python if p[j-1] == '*': ``` - 注意:Python中字符串索引从0开始,所以`p[j-1]`表示模式串的第`j`个字符(因为j从1到n,而字符串索引从0到n-1)。 7. **逻辑运算符**: - `or`:逻辑或,如果左边的表达式为`True`,则整个表达式为`True`,否则计算右边的表达式。 - `and`:逻辑与,只有左右两边都为`True`,结果才为`True`。 8. **布尔运算的短路特性**:在表达式`A or B`中,如果`A`为`True`,则不再计算`B`;在`A and B`中,如果`A`为`False`,则不再计算`B`。 9. **列表索引**:`dp[i][j]`表示二维列表`dp`的第`i`行第`j`列的元素。 10. **返回值**:`return dp[m][n]` 返回二维列表右下角的值,即整个字符串和整个模式串的匹配结果。 ### 动态规划状态转移方程 用数学公式表示状态转移: $$ dp[i][j] = \begin{cases} dp[i][j-2] & \text{如果 } p_{j-1} = '*' \text{ 且选择匹配 } 0 \text{ 次} \\ \text{或} \\ (dp_{j-2} \text{ 匹配 } s_{i-1} \text{ 且 } dp[i-1][j]) & \text{如果 } p_{j-1} = '*' \text{ 且选择匹配 } 1 \text{ 次或多次} \\ dp[i-1][j-1] & \text{如果 } p_{j-1} \neq '*' \text{ 且当前字符匹配} \\ \text{False} & \text{其他情况} \end{cases} $$ 其中,$p_{j-2} \text{ 匹配 } s_{i-1}$ 表示 $p_{j-2} = '.'$ 或 $p_{j-2} = s_{i-1}$。 ### 相关问 1. 动态规划中如何优化空间复杂度至$O(n)$? 2. 如果添加`'+'`操作符(匹配1次以上)需要修改哪些代码? 3. 递归解法在什么情况下会超时?如何优化? 4. Python中`range()`函数的具体工作机制是什么? 5. 二维列表在内存中是如何存储的?</think>### LeetCode 第10 "正则表达式匹配" 详细解 #### 目说明 实现支持 `.` 和 `*` 的正则表达式匹配: - `.` 匹配任意单个字符 - `*` 匹配零个或多个前面的元素 要求实现函数 `isMatch(s: str, p: str) -> bool` #### 动态规划解法(附逐行解释) ```python class Solution: def isMatch(self, s: str, p: str) -> bool: # 获取字符串长度:len()返回字符串字符数量 m, n = len(s), len(p) # 创建二维DP表:(m+1)行×(n+1)列,初始值全False # [False]*(n+1) 创建含n+1个False的列表 # for _ in range(m+1) 循环m+1次(_表示不关心循环变量名) dp = [[False] * (n + 1) for _ in range(m + 1)] # 空字符串匹配空模式 dp[0][0] = True # 处理模式开头如"a*"匹配空字符串的情况 # range(2, n+1) 生成数字序列[2,3,...,n] for j in range(2, n + 1): # p[j-1]是当前字符(Python索引从0开始) if p[j - 1] == '*': # 匹配0次:跳过"x*"组合 dp[0][j] = dp[0][j - 2] # 双层循环处理所有字符 for i in range(1, m + 1): # 遍历主字符串 for j in range(1, n + 1): # 遍历模式串 # 遇到'*'的情况(需确保前面有字符) if j >= 2 and p[j - 1] == '*': # 匹配0次 或 匹配多次(需前字符匹配且前状态为真) # \ 是行连接符,用于换行继续写代码 dp[i][j] = dp[i][j - 2] or \ ((p[j - 2] == '.' or p[j - 2] == s[i - 1]) and dp[i - 1][j]) # 普通字符或'.'匹配 elif p[j - 1] == '.' or p[j - 1] == s[i - 1]: # 当前字符匹配且前子串也匹配 dp[i][j] = dp[i - 1][j - 1] # 返回最终匹配结果 return dp[m][n] ``` #### 关键语法解析 1. **类与方法定义** `class Solution:` 定义类(代码模板) `def isMatch(...):` 定义方法,`self`指代类实例(Python类方法必需) 2. **类型注解** `s: str` 指定参数类型为字符串 `-> bool` 指定返回值为布尔类型(True/False) 3. **列表生成式** `[[False]*(n+1) for _ in range(m+1)]` - 外层:创建 `m+1` 个元素 - 内层:每个元素是含 `n+1` 个False的列表 4. **循环结构** `for j in range(2, n+1):` - `range(start, stop)` 生成从start到stop-1的整数序列 - 示例:`range(2,5)` → [2,3,4] 5. **逻辑运算符** `or`:任一条件为真则返回真 `and`:所有条件为真才返回真 ```python # 示例 a = True or False # a=True b = True and False # b=False ``` 6. **索引访问** `s[i-1]` 获取字符串第i-1个字符(Python索引从0开始) #### 状态转移方程 核心逻辑用数学公式表示为: $$ dp[i][j] = \begin{cases} dp[i][j-2] & \text{匹配 } 0 \text{ 次} \\ \text{或} \\ (p_{j-2} \text{ 匹配 } s_{i-1}) \land dp[i-1][j] & \text{匹配 } 1+ \text{ 次} \\ dp[i-1][j-1] & \text{普通匹配} \end{cases} $$ 其中 $\land$ 表示逻辑"与"操作[^1] #### 算法复杂度 - **时间复杂度**:$O(m \times n)$(双重循环) - **空间复杂度**:$O(m \times n)$(DP表存储) ### 相关问 1. **动态规划中如何优化空间复杂度至$O(n)$?** 可改用滚动数组,只保留前一行的状态值,将二维DP压缩为一维数组[^1]。 2. **如果添加`'+'`操作符(匹配1次以上)需要修改哪些代码?** 需增加分支处理`+`的情况: ```python if p[j-1] == '+': # 必须匹配至少1次 dp[i][j] = (匹配当前字符) and (dp[i-1][j] or dp[i-1][j-2]) ``` 3. **递归解法在什么情况下会超时?如何优化?** 当出现大量重复子问(如`a*a*a*`匹配长字符串)时,递归树指数级膨胀。优化方法:添加记忆化缓存(Memoization)[^3]。 4. **Python中`range()`函数的具体工作机制是什么?** 生成惰性序列(不立即存储所有值),语法: `range(start, stop, step)` 示例:`range(1,10,2)` → 1,3,5,7,9 5. **二维列表在内存中是如何存储的?** 在Python中是"列表的列表": - 外层列表存储各行地址 - 每行是独立的内存块 ```python # 内存结构示例 dp = [ [False, False, False], # 行0的内存地址 [False, False, False] # 行1的内存地址 ] ``` [^1]: 关于动态规划空间优化的实现方法 [^2]: 状态转移方程的逻辑表达形式 [^3]: 递归优化与记忆化技术
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Run,boy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值