Bootstrap

【LeetCode】Top100 经典必刷题 02【2/20】

6. Z 字形变换

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

 

示例 1:

输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"

示例 2:
输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P     I    N
A   L S  I G
Y A   H R
P     I

示例 3:

输入:s = "A", numRows = 1
输出:"A"

 

提示:

  • 1 <= s.length <= 1000
  • s 由英文字母(小写和大写)、',''.' 组成
  • 1 <= numRows <= 1000

python 参考题解:

第六道题是 "ZigZag Conversion",题目描述如下: 将一个给定字符串根据给定的行数,以从上到下、从左到右进行 Z 字形排列。 比如输入字符串为 `"PAYPALISHIRING"` 行数为 `3` 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“PAHNAPLSIIGYIR”。
请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

以下是用 Python 给出的题解:

def convert(s, numRows):
    if numRows == 1 or numRows >= len(s):
        return s
    
    # 初始化一个列表,用于存储每一行的字符
    res = [''] * numRows
    index, step = 0, 1
    
    # 遍历字符串中的每个字符
    for char in s:
        # 将字符添加到对应行的字符串中
        res[index] += char
        
        # 当达到第一行或最后一行时,改变方向
        if index == 0:
            step = 1
        elif index == numRows - 1:
            step = -1
        
        # 更新当前行索引
        index += step
    
    # 将所有行的字符串拼接起来
    return ''.join(res)
# 示例
print(convert("PAYPALISHIRING", 3))  # 输出: "PAHNAPLSIIGYIR"

解题思路和步骤:

  1. 边界情况处理:如果行数 numRows 为1或者大于等于字符串长度,直接返回原字符串,因为变换后的字符串与原字符串相同。
  2. 初始化行字符串列表:创建一个列表 res,其长度等于 numRows,用于存储每一行的字符。
  3. 初始化索引和步长:定义变量 index 为当前行索引,step 为步长,初始时步长为1。
  4. 遍历字符串:使用一个循环遍历字符串中的每个字符。
  5. 填充行字符串:将当前字符添加到 res[index] 对应行的字符串中。
  6. 改变方向:当 index 达到第一行(index == 0)或最后一行(index == numRows - 1)时,改变 step 的方向,从而实现Z字形的上下移动。
  7. 更新行索引:根据 step 更新当前行索引 index
  8. 拼接结果:将列表 res 中的所有行字符串拼接起来,形成最终的变换字符串。
  9. 返回结果:返回拼接后的字符串。

这个算法的时间复杂度是 O(n),其中 n 是字符串的长度,因为我们需要遍历字符串中的每个字符一次。空间复杂度是 O(n),因为我们存储了变换后的字符串。

7. 整数反转

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

如果反转后整数超过 32 位的有符号整数的范围 [−231,  231 − 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。

 

示例 1:

<pre><strong>输入:</strong>x = 123
<strong>输出:</strong>321
</pre>

示例 2:

<pre><strong>输入:</strong>x = -123
<strong>输出:</strong>-321
</pre>

示例 3:

<pre><strong>输入:</strong>x = 120
<strong>输出:</strong>21
</pre>

示例 4:

<pre><strong>输入:</strong>x = 0
<strong>输出:</strong>0
</pre>

 

提示:

  • -231 <= x <= 231 - 1

python 参考题解:

第七道题是 "Reverse Integer",题目描述如下: 给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

示例 1:

输入: 123
输出: 321

示例 2:

输入: -123
输出: -321

示例 3:

输入: 120
输出: 21

注意:
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围是 [−2^31, 2^31 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。

以下是用 Python 给出的题解:

def reverse(x):
    # 初始化符号变量,用于记录正负
    sign = -1 if x < 0 else 1
    # 取绝对值并转换为字符串
    x_str = str(abs(x))
    # 反转字符串
    reversed_str = x_str[::-1]
    # 转换回整数并应用符号
    reversed_int = sign * int(reversed_str)
    # 检查是否溢出
    if reversed_int < -2**31 or reversed_int > 2**31 - 1:
        return 0
    return reversed_int
# 示例
print(reverse(123))    # 输出: 321
print(reverse(-123))   # 输出: -321
print(reverse(120))    # 输出: 21

解题思路和步骤:

  1. 符号处理:首先,我们需要处理整数的符号。如果输入的整数是负数,我们将符号存储在一个变量 sign 中,并在后续步骤中应用这个符号。
  2. 取绝对值:为了简化处理,我们将整数取绝对值,这样就可以不考虑符号,直接反转数字。
  3. 反转字符串:将整数的绝对值转换为字符串,然后使用 Python 的字符串切片功能 [::-1] 来反转字符串。
  4. 转换回整数:将反转后的字符串转换回整数,并乘以之前存储的符号变量 sign,以恢复整数的正负。
  5. 检查溢出:在返回反转后的整数之前,我们需要检查它是否在 32 位有符号整数的范围内(即是否在 [-2^31, 2^31 - 1] 范围内)。如果溢出,则返回 0。
  6. 返回结果:如果没有溢出,则返回反转后的整数。

这个算法的时间复杂度是 O(n),其中 n 是整数的位数,因为我们只需要遍历整数每一位一次。空间复杂度也是 O(n),因为我们存储了整数的字符串表示。在实际情况中,由于整数位数固定,所以时间复杂度和空间复杂度都可以视为 O(1)。

8. 字符串转换整数 (atoi)

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数。

函数 myAtoi(string s) 的算法如下:

  1. 空格:读入字符串并丢弃无用的前导空格(" "
  2. 符号:检查下一个字符(假设还未到字符末尾)为 '-' 还是 '+'。如果两者都不存在,则假定结果为正。
  3. 转换:通过跳过前置零来读取该整数,直到遇到非数字字符或到达字符串的结尾。如果没有读取数字,则结果为0。
  4. 舍入:如果整数数超过 32 位有符号整数范围 [−231,  231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被舍入为 −231 ,大于 231 − 1 的整数应该被舍入为 231 − 1

返回整数作为最终结果。

 

示例 1:

输入:s = "42"

输出:42

解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。

带下划线线的字符是所读的内容,插入符号是当前读入位置。
第 1 步:"42"(当前没有读入字符,因为没有前导空格)
         ^
第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
         ^
第 3 步:"42"(读入 "42")
           ^

示例 2:

	输入: " -042"
	输出:-42

解释:

第 1 步:"<u><strong>   </strong></u>-042"(读入前导空格,但忽视掉)
            ^
第 2 步:"   <u>-</u>042"(读入 '-' 字符,所以结果应该是负数)
             ^
第 3 步:"   <u>-042</u>"(读入 "042",在结果中忽略前导零)
               ^

示例 3:

输入:s = "1337c0d3"

输出:1337

解释:

第 1 步:"1337c0d3"(当前没有读入字符,因为没有前导空格)
         ^
第 2 步:"1337c0d3"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
         ^
第 3 步:"1337c0d3"(读入 "1337";由于下一个字符不是一个数字,所以读入停止)
         ^

示例 4:

输入:s = "0-1"

输出:0

解释:

第 1 步:"0-1" (当前没有读入字符,因为没有前导空格)
         ^
第 2 步:"0-1" (当前没有读入字符,因为这里不存在 '-' 或者 '+')
         ^
第 3 步:"<u>0</u>-1" (读入 "0";由于下一个字符不是一个数字,所以读入停止)
          ^

示例 5:

输入:s = "words and 987"

输出:0

解释:

读取在第一个非数字字符“w”处停止。

 

提示:

  • 0 <= s.length <= 200
  • s 由英文字母(大写和小写)、数字(0-9)、' ''+''-''.' 组成

python 参考题解:

第八道题是 "String to Integer (atoi)",题目描述如下: 请你来实现一个 `atoi` 函数,使其能将字符串转换成整数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来,如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−2^31, 2^31 − 1]。如果数值超过这个范围,请返回 INT_MAX (2^31 − 1) 或 INT_MIN (−2^31) 。

以下是用 Python 给出的题解:

def myAtoi(s):
    # 初始化结果和索引
    result, i, sign = 0, 0, 1
    
    # 跳过字符串开头的空白字符
    while i < len(s) and s[i] == ' ':
        i += 1
    
    # 检查正负号
    if i < len(s) and (s[i] == '+' or s[i] == '-'):
        sign = -1 if s[i] == '-' else 1
        i += 1
    
    # 将字符转换为整数
    while i < len(s) and s[i].isdigit():
        result = result * 10 + int(s[i])
        i += 1
        
        # 检查是否溢出
        if result * sign > 2**31 - 1:
            return 2**31 - 1
        if result * sign < -2**31:
            return -2**31
    
    return result * sign
# 示例
print(myAtoi("42"))         # 输出: 42
print(myAtoi("   -42"))     # 输出: -42
print(myAtoi("4193 with words"))  # 输出: 4193
print(myAtoi("words and 987"))     # 输出: 0
print(myAtoi("-91283472332"))       # 输出: -2147483648 (溢出情况)

解题思路和步骤:

  1. 初始化变量:首先,我们初始化结果 result 为0,索引 i 为0,符号 sign 为1(默认为正数)。
  2. 跳过空格:使用一个循环跳过字符串开头的所有空格字符。
  3. 处理符号:如果当前字符是正号或负号,我们更新符号 sign 并将索引向前移动一位。
  4. 转换数字:在另一个循环中,我们遍历字符串的剩余部分,将数字字符转换为整数。每次循环中,我们将当前数字字符乘以10加上新数字,实现数字的累加。
  5. 检查溢出:在每次转换后,我们检查结果是否溢出32位有符号整数的范围。如果溢出,则根据符号返回 2**31 - 1-2**31
  6. 返回结果:循环结束后,返回结果乘以符号 sign

这个算法的时间复杂度是 O(n),其中 n 是字符串的长度,因为我们最多遍历整个字符串一次。空间复杂度是 O(1),因为我们只使用了常数级别的额外空间。

9. 回文数

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false

回文数
是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

  • 例如,121 是回文,而 123 不是。

 

示例 1:

输入:x = 121
输出:true

示例 2:

输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

输入:x = 10
输出:false
解释:从右向左读, 为 01 。因此它不是一个回文数。

 

提示:

  • -231 <= x <= 231 - 1

 

进阶:你能不将整数转为字符串来解决这个问题吗?

python 参考题解:

第九道题是 "Palindrome Number",题目描述如下: 判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

示例 1:

输入: 121
输出: true

示例 2:

输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。

以下是用 Python 给出的题解:

def isPalindrome(x):
    # 判断负数不是回文数
    if x < 0:
        return False
    
    # 将整数转换为字符串
    s = str(x)
    
    # 检查字符串是否是回文
    return s == s[::-1]
# 示例
print(isPalindrome(121))  # 输出: True
print(isPalindrome(-121))  # 输出: False
print(isPalindrome(10))    # 输出: False

解题思路和步骤:

  1. 判断负数:首先,如果输入的整数是负数,直接返回 False,因为负数不是回文数。
  2. 转换整数为字符串:将整数转换为字符串,以便于进行比较。
  3. 检查字符串是否是回文:使用 Python 的字符串切片功能 [::-1] 来反转字符串,然后比较原字符串和反转后的字符串是否相等。
  4. 返回结果:如果字符串是回文,返回 True;否则返回 False

这个算法的时间复杂度是 O(n),其中 n 是整数的位数,因为我们只需要遍历整数每一位一次。空间复杂度也是 O(n),因为我们存储了整数的字符串表示。在实际情况中,由于整数位数固定,所以时间复杂度和空间复杂度都可以视为 O(1)。

10. 正则表达式匹配

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。

  • '.' 匹配任意单个字符
  • '*' 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

 

示例 1:

输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:s = "aa", p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

示例 3:

输入:s = "ab", p = ".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。

 

提示:

  • 1 <= s.length <= 20
  • 1 <= p.length <= 20
  • s 只包含从 a-z 的小写字母。
  • p 只包含从 a-z 的小写字母,以及字符 . 和 *
  • 保证每次出现字符 * 时,前面都匹配到有效的字符

python 参考题解:

第十道题是 "Regular Expression Matching",题目描述如下: 给定一个字符串 (s) 和一个字符模式 (p),实现一个支持 '.' 和 '*' 的正则表达式匹配。 '.' 匹配任意单个字符。 '*' 匹配零个或多个前面的那一个元素。 所谓匹配,是要涵盖整个输入字符串 (不是部分)。

示例 1:

输入: s = "aa" p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

示例 2:

输入: s = "aa" p = "a*"
输出: true
解释: '*' 可以匹配零个或多个前面的那一个元素, 即可以匹配字符串 "aa" 中的前一个 "a"。

示例 3:

输入: s = "ab" p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。

以下是使用Python编写的解题代码:

def isMatch(s: str, p: str) -> bool:
    # 初始化两个指针,分别指向s和p
    i, j = 0, 0
    # 初始化两个标志位,分别表示s和p是否需要匹配字符
    s_match, p_match = True, True
    
    # 创建一个二维数组,用于存储s和p的匹配状态
    dp = [[False] * (len(p) + 1) for _ in range(len(s) + 1)]
    
    # 初始化第一行和第一列,dp[0][0]始终为False
    dp[0][0] = True
    for j in range(1, len(p) + 1):
        dp[0][j] = dp[0][j - 1] and (p[j - 1] == '*' or p[j - 1] == '.')
    
    for i in range(1, len(s) + 1):
        for j in range(1, len(p) + 1):
            # 如果p当前字符是'*',则有三种情况:
            # 1. p[j - 1] == s[i - 1],此时dp[i][j] = dp[i - 1][j - 1]
            # 2. p[j - 1] == '.',此时dp[i][j] = dp[i - 1][j - 1]
            # 3. p[j - 1] == '*',此时dp[i][j] = dp[i][j - 2]
            if p[j - 1] == '*':
                dp[i][j] = dp[i][j - 2] or (s[i - 1] == p[j - 2] or p[j - 2] == '.')
            else:
                dp[i][j] = dp[i - 1][j - 1] and (s[i - 1] == p[j - 1] or p[j - 1] == '.')
    
    # dp[len(s)][len(p)]为True时,s和p匹配
    return dp[len(s)][len(p)]
# 示例
print(isMatch("aa", "a"))  # 输出: False
print(isMatch("aa", "a*"))  # 输出: True
print(isMatch("ab", ".*"))  # 输出: True

解题思路和步骤:

  1. 初始化变量:首先,我们初始化两个指针 ij,分别指向字符串 s 和字符模式 p。我们还需要两个标志位 s_matchp_match,分别表示 sp 是否需要匹配字符。
  2. 创建二维数组:创建一个二维数组 dp,用于存储 sp 的匹配状态。
  3. 初始化第一行和第一列:由于 dp[0][0] 始终为 False,我们不需要为其赋值。对于dp[0][j]dp[i][0],我们根据题目要求进行初始化。dp[0][j] 表示字符串 s 为空时,模式 p 能否匹配,这取决于模式 p 的第一个字符。如果 p[0]. 或者 *,则 dp[0][j]True,因为 . 匹配任意单个字符,* 匹配零个或多个前面的字符。
  4. 遍历字符串和模式:使用两个循环遍历字符串 s 和模式 p 的每一个字符。
  5. 处理星号:如果模式 p 的当前字符是 *,则有两种情况:
    • 如果 p[j - 1] 匹配字符串 s[i - 1],则 dp[i][j] 等于 dp[i - 1][j - 1]
    • 如果 p[j - 1] 匹配零个字符,则 dp[i][j] 等于 dp[i][j - 2]
  6. 处理其他字符:如果模式 p 的当前字符不是 *,则 dp[i][j] 等于 dp[i - 1][j - 1]s[i - 1] == p[j - 1] 或者 p[j - 1] == '.' 的并集。
  7. 返回结果:遍历完成后,如果 dp[len(s)][len(p)]True,则字符串 s 和模式 p 匹配;否则不匹配。

这个算法的时间复杂度是 O(m * n),其中 m 是字符串 s 的长度,n 是模式 p 的长度。空间复杂度也是 O(m * n),因为我们使用了二维数组 dp 来存储中间结果。在实际情况中,由于字符串和模式的长度固定,所以时间复杂度和空间复杂度都可以视为 O(1)。

;