「新」动计划 · 编程入门 Python版本

「新」动计划 · 编程入门


1. 两整数相加

给你两个整数 num1 和 num2,返回这两个整数的和。

  • 示例 1

输入:num1 = 12, num2 = 5

输出:17

  • 示例 2

输入:num1 = -10, num2 = 4

输出:-6

  • 提示

-100 <= num1, num2 <= 100

代码及注释

class Solution:
    def sum(self, num1: int, num2: int) -> int:
        # num1:第一个整数,范围 [-100, 100]
        # num2:第二个整数,范围 [-100, 100]
        # 直接返回两数相加的结果
        return num1 + num2

知识点讲解

  • LeetCode 类/方法格式:所有解答封装在 class Solution 中,线上由框架调用对应方法。
  • Python 类型提示num1: int, num2: int-> int 提升可读性,但不影响运行。
  • 算术运算:使用 + 运算符对整数执行加法,支持正数和负数。
  • 输入约束[-100, 100] 范围内计算,无需考虑溢出或大数处理。

代码详解

定义类与方法

class Solution:
    def sum(self, num1: int, num2: int) -> int:
  • Solution:题解类名。
  • sum:方法名,与题目要求一致。
  • 参数 num1, num2 均为整数,返回值类型为整数。

核心逻辑

return num1 + num2
  • 直接计算并返回两数之和,没有额外操作。

复杂度分析

  • 时间复杂度\(O(1)\)
  • 空间复杂度\(O(1)\)

2. 温度转换

给你一个 四舍五入到小数点后两位 的非负浮点数 celsius 表示温度,以摄氏度(Celsius)为单位。

将其分别转换为 开氏度(Kelvin)和 华氏度(Fahrenheit),并以数组 ans = [kelvin, fahrenheit] 的形式返回结果。

与实际答案误差不超过 \(10⁻⁵\) 的会视为正确答案。

  • 开氏度 = 摄氏度 + 273.15
  • 华氏度 = 摄氏度 * 1.80 + 32.00

示例 1
输入:celsius = 36.50
输出:[309.65000,97.70000]

示例 2
输入:celsius = 122.11
输出:[395.26000,251.79800]

提示
0 <= celsius <= 1000

代码及注释

class Solution:
    def convertTemperature(self, celsius: float) -> List[float]:
        # celsius:摄氏度,输入范围 [0,1000]
        k: float = celsius + 273.15    # 计算开氏度(Kelvin)
        h: float = celsius * 1.80 + 32.00  # 计算华氏度(Fahrenheit)
        return [k, h]  # 返回 [开氏度, 华氏度]

知识点讲解

  • 浮点数运算:Python float 支持小数计算,满足题目精度要求。
  • 类型提示celsius: float-> List[float] 仅作可读性提升,不影响运行。
  • 列表返回:直接将两个计算结果组成列表返回,LeetCode 自动比对元素误差。
  • 常数运算:加法与乘法均为 \(O(1)\) 级别操作。

代码详解

class Solution:

  • 定义解题类,LeetCode 后台实例化并调用其方法。

def convertTemperature(self, celsius: float) -> List[float]:

  • 方法名与题目要求一致,接收一个浮点数 celsius

k: float = celsius + 273.15

  • 根据公式计算开氏度。

h: float = celsius * 1.80 + 32.00

  • 根据公式计算华氏度。

return [k, h]

  • 将两种单位的温度值封装到列表中返回。

复杂度分析

  • 时间复杂度\(O(1)\)
  • 空间复杂度\(O(1)\)

3. 最小偶倍数

给你一个正整数 n,返回 2 和 n 的最小公倍数(正整数)。

  • 示例 1
     输入:n = 5
        输出:10
        解释:5 和 2 的最小公倍数是 10。

  • 示例 2
     输入:n = 6
        输出:6
        解释:6 和 2 的最小公倍数是 6。注意数字会是它自身的倍数。

  • 提示
    1 <= n <= 150

代码及注释

class Solution:
    def smallestEvenMultiple(self, n: int) -> int:
        # n: 正整数,范围 [1, 150]
        # 如果 n 为奇数 (n % 2 != 0),最小偶倍数是 n * 2
        # 如果 n 为偶数,n 本身就是最小偶倍数
        return n * 2 if n % 2 != 0 else n

知识点讲解

  • 最小公倍数(LCM)
      - 若 a | b(a 能整除 b),则 LCM(a, b) = b;否则 LCM(a, b) = a * b / GCD(a, b)。
      - 本题中特殊化为 a=2:
        - 当 n 为偶数时,2 | n → LCM(2, n)=n;
              - 当 n 为奇数时,GCD(2,n)=1 → LCM=2*n。
  • 取模运算判断奇偶
      - n % 2 != 0 判断奇数。
  • 条件表达式
      - X if 条件 else Y,用于在一行内返回不同结果。

代码详解

  1. 执行 n % 2,判断 n 是奇数还是偶数。
  2. 条件表达式
      - 若 n % 2 != 0 为真,返回 n * 2
      - 否则返回 n

复杂度分析

  • 时间复杂度\(O(1)\) — 仅做一次取模与乘法/返回操作。
  • 空间复杂度\(O(1)\) — 只使用常数级额外空间。

4. 判断根结点是否等于子结点之和

给你一个二叉树的根结点 root,该树恰好包含三个结点:根结点、左子结点和右子结点。如果根结点的值等于两个子结点值之和,返回 true,否则返回 false

  • 示例 1
     输入:root = [10,4,6]
        输出:true

  • 示例 2
     输入:root = [5,3,1]
        输出:false

代码及注释

# Definition for a binary tree node.
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def checkTree(self, root: Optional[TreeNode]) -> bool:
        # root.left.val    ← 左子结点的值
        # root.right.val   ← 右子结点的值
        # root.val         ← 根结点的值
        # 判断左右子结点之和是否等于根结点的值
        return root.left.val + root.right.val == root.val

知识点讲解

  • 二叉树节点结构TreeNode 包含 valleftright 属性。
  • 类型提示Optional[TreeNode] 表明 root 可能为 None(本题保证存在两个子结点),使用类型提示有助于可读性。
  • 属性访问root.left.valroot.right.val 直接获取子结点的值。
  • 运算顺序:Python 中先执行加法 root.left.val + root.right.val,再进行等值比较 ==,返回 TrueFalse

代码详解

  1. 取出左右子结点的值:L = root.left.valR = root.right.val
  2. 计算 L + R
  3. 与根结点 root.val 比较,表达式 L + R == root.val 即为最终布尔结果。
  4. 将该布尔值返回。

复杂度分析

  • 时间复杂度\(O(1)\) — 固定次数的属性访问与算术/比较运算。
  • 空间复杂度\(O(1)\) — 使用常量级额外空间。

5. 数组异或操作

给你两个整数 nstart

数组 nums 定义为:nums[i] = start + 2*i(下标从 0 开始),且 n == nums.length

请返回 nums 中所有元素按位异或(XOR)后得到的结果。

  • 示例 1
     输入:n = 5, start = 0
        输出:8

  • 示例 2
     输入:n = 4, start = 3
        输出:8

  • 示例 3
     输入:n = 1, start = 7
        输出:7

  • 示例 4
     输入:n = 10, start = 5
        输出:2

  • 提示
    1 <= n <= 1000
    0 <= start <= 1000
    n == nums.length

代码及注释

class Solution:
    def xorOperation(self, n: int, start: int) -> int:
        # nums[0] = start,将其作为初始异或结果
        res = start
        # 从 i = 1 到 n-1,生成 nums[i] 并与 res 进行异或
        for i in range(1, n):
            # nums[i] = start + 2*i
            res = res ^ (start + 2 * i)
        # 返回所有元素异或后的结果
        return res

知识点讲解

  • 数组生成:无需显式存储整个数组,通过公式 start + 2*i 动态计算每个元素。
  • 异或运算性质
      - a ^ a = 0,a ^ 0 = a
      - 异或具备交换律和结合律,可任意顺序累积。
  • 位运算效率:异或操作在硬件层面高效,单次为 \(O(1)\)

代码详解

  1. res = start:将 nums[0] 设为初始值。
  2. for i in range(1, n): 遍历剩余下标。
  3. start + 2 * i:按题意计算当前元素值。
  4. res = res ^ (...):将当前元素与已有结果异或,更新 res
  5. 循环结束后,res 即为所有 nums 元素的异或结果,直接返回。

复杂度分析

  • 时间复杂度\(O(n)\) — 需遍历 n 次。
  • 空间复杂度\(O(1)\) — 仅使用常数级额外变量。

6. 好数对的数目

给你一个整数数组 nums。如果一组数字 (i, j) 满足 nums[i] == nums[j]i < j,就可以认为是一组 好数对

返回好数对的数目。

  • 示例 1
    输入:nums = [1,2,3,1,1,3]
    输出:4
    解释:共有 4 组好数对,分别是 (0,3)、(0,4)、(3,4)、(2,5)。
  • 示例 2
    输入:nums = [1,1,1,1]
    输出:6
    解释:数组中的每组数字都是好数对,总共 C(4,2)=6 组。
  • 示例 3
    输入:nums = [1,2,3]
    输出:0

代码及注释

class Solution:
    def numIdenticalPairs(self, nums: List[int]) -> int:
        # 结果计数器
        res = 0
        # 数组长度
        n = len(nums)
        # 枚举所有 i<j 的索引对
        for i in range(n):
            for j in range(i+1, n):
                # 若值相等,则构成好数对
                if nums[i] == nums[j]:
                    res += 1
        return res

知识点讲解

  • 双重循环枚举:通过两层 for 循环穷举所有 (i,j) 且 i<j 的组合。
  • 相等判断:用 nums[i] == nums[j] 识别好数对。
  • 数学组合:当所有元素相同时,总对数等于组合数 \(C(n,2)=n*(n-1)/2\)
  • 优化思路:可用哈希表记录每个数出现次数,遍历一次时动态累加已有次数,实现 \(O(n)\) 时间。

代码详解

  1. res = 0:初始化好数对计数。
  2. n = len(nums):获得数组长度。
  3. 外层 for i in range(n):枚举左侧下标。
  4. 内层 for j in range(i+1, n):枚举右侧下标,保证 i<j。
  5. if nums[i] == nums[j]:判断当前一对值是否相等。
  6. res += 1:若相等则累计计数。
  7. 循环结束后返回 res

复杂度分析

  • 时间复杂度\(O(n²)\)
  • 空间复杂度\(O(1)\)
    可选优化
     使用哈希表,在一次遍历中对每个 x = nums[i],令 res += count[x],再执行 count[x] += 1,将时间降为 \(O(n)\),空间 \(O(n)\)

7. 统计好三元组

给你一个整数数组 arr 以及三个整数 a, b, c,请统计其中的好三元组数目。

如果三元组 (arr[i], arr[j], arr[k]) 满足以下全部条件,则认为它是一个好三元组

  • 0 ≤ i < j < k < arr.length
  • |arr[i] - arr[j]| ≤ a
  • |arr[j] - arr[k]| ≤ b
  • |arr[i] - arr[k]| ≤ c

返回好三元组的数量。

代码及注释

class Solution:
    def countGoodTriplets(self, arr: List[int], a: int, b: int, c: int) -> int:
        # arr:整数数组,长度范围 [3, 100]
        # a, b, c:阈值,范围 [0, 1000]
        n = len(arr)                   # 数组长度
        count = 0                      # 好三元组计数器
        # 枚举所有三元组下标 (i, j, k),满足 0 ≤ i < j < k < n
        for i in range(n):
            for j in range(i + 1, n):
                for k in range(j + 1, n):
                    # 同时检查三对元素差值是否均满足条件
                    if abs(arr[i] - arr[j]) <= a \
                       and abs(arr[j] - arr[k]) <= b \
                       and abs(arr[i] - arr[k]) <= c:
                        count += 1       # 满足条件则累加
        return count                   # 返回结果

知识点讲解

  • 三重循环枚举
     通过三层嵌套 for 循环,生成所有满足 i < j < k 的下标组合。
  • 绝对值运算
     使用 abs(x - y) 计算两个元素值之差的绝对值,分别与 a, b, c 进行比较。
  • 条件累加
     在判断语句中同时检查三组差值,当且仅当三者都不超过对应阈值时,才将 count 加一。

代码详解

初始化
  - n = len(arr):获取数组长度。
  - count = 0:初始化好三元组计数器。

枚举下标
  - 第一层 for i in range(n):取第一个元素下标。
  - 第二层 for j in range(i+1, n):取第二个元素下标,保证 j > i
  - 第三层 for k in range(j+1, n):取第三个元素下标,保证 k > j

条件判断
  - abs(arr[i] - arr[j]) ≤ a
  - abs(arr[j] - arr[k]) ≤ b
  - abs(arr[i] - arr[k]) ≤ c
 三个条件均满足时,count += 1

返回结果
  - 循环结束后,count 即为符合要求的好三元组总数。

复杂度分析

  • 时间复杂度\(O(n³)\),三重循环遍历所有下标组合,n 最多 100,符合题目限制。
  • 空间复杂度\(O(1)\),仅使用常数级辅助变量。

8. 转换成小写字母

给你一个字符串 s,将其中的所有大写字母转换成对应的小写字母,返回新的字符串。

代码及注释

class Solution:
    def toLowerCase(self, s: str) -> str:
        # 调用 Python 内置方法,将所有字符按 Unicode 小写映射返回新字符串
        return s.lower()

知识点讲解

  • 字符串方法str.lower() 会遍历字符串,对每个大写字母做小写转换,不影响非字母字符。
  • Unicode/ASCII 映射:在底层,.lower() 根据字符的码点表将大写字母映射到对应的小写字母。
  • 不可变性:Python 字符串是不可变对象,lower() 返回一个新字符串,原字符串不变。

代码详解

  1. s.lower():对字符串 s 中的每个字符调用小写映射,生成一个全新的字符串。
  2. 直接 return 该结果,无需额外操作。

复杂度分析

  • 时间复杂度\(O(n)\),需扫描并转换每个字符。
  • 空间复杂度\(O(n)\),返回的新字符串长度与输入相同。

9. 各位相加

给定一个非负整数 num,重复将各个位上的数字相加,直到结果为一位数,返回这个结果。

  • 示例 1
     输入:num = 38
        输出:2
        解释:38 → 3 + 8 = 11 → 1 + 1 = 2

  • 示例 2
     输入:num = 0
        输出:0

  • 提示
    0 <= num <= 2^31 - 1

代码及注释

class Solution:
    def addDigits(self, num: int) -> int:
        # 当 num 至少两位时,继续循环
        while num >= 10:
            tmp: int = 0    # 累加当前 num 每一位的和
            # 将 num 各位数字累加至 tmp
            while num > 0:
                tmp += num % 10  # 取最低位并累加
                num //= 10       # 去掉最低位
            num = tmp  # 更新 num 为各位之和
        return num     # 返回最后一位数

知识点讲解

  • 取余与整除num % 10 得到最低位,num // 10 去掉最低位。
  • 循环嵌套:外层循环判断结果是否为多位数,内层循环求当前各位之和。
  • 数字根概念:该过程即数字根迭代求和,虽可用 \(O(1)\) 公式优化,此处为直观实现。
  • 常量空间:仅用若干整型变量,无需额外数组或递归栈。

代码详解

外层判断

  • while num >= 10:只要 num 不止一位,就继续拆分求和。

内层拆分累加

tmp = 0
while num > 0:
    tmp += num % 10
    num //= 10
  • 每次取 num % 10 累加到 tmp,再 num //= 10 逐位剥离。

更新与重复

  • num = tmp:将累加结果赋回 num,可能仍为多位,回到外层继续。

返回结果

  • num < 10 时跳出,return num

复杂度分析

  • 时间复杂度\(O(\log n)\) — d 位数每次 \(O(d)\),迭代次数亦为 \(O(d)\)
  • 空间复杂度\(O(1)\) — 仅使用常数个整型变量。

10. 整数的各位积和之差

给你一个整数 n,请计算并返回该整数 各位数字之积各位数字之和 的差。

代码及注释

class Solution:
    def subtractProductAndSum(self, n: int) -> int:
        # 计算各位数字的乘积
        def pro(x: int) -> int:
            tmp = 1
            while x > 0:
                tmp *= x % 10    # 取最低位并累乘
                x //= 10         # 去掉最低位
            return tmp
        # 计算各位数字的和
        def summation(x: int) -> int:
            tmp = 0
            while x > 0:
                tmp += x % 10    # 取最低位并累加
                x //= 10         # 去掉最低位
            return tmp
        # 返回乘积与和的差
        return pro(n) - summation(n)

知识点讲解

  • 取余与整除x % 10 可得到最低位数字,x // 10 可去掉最低位。
  • 循环累积:分别使用乘法和加法在循环中累积各位的乘积与和。
  • 局部函数:在主方法中定义 prosummation 两个局部函数以提高代码可读性。
  • 返回差值:最终用乘积减去和得到结果。

代码详解

  1. 调用 pro(n)
      - 初始化 tmp = 1,遍历 n 的每一位,累乘到 tmp
      - 循环结束后,tmp 即为所有数字的乘积。
  2. 调用 summation(n)
      - 初始化 tmp = 0,遍历 n 的每一位,累加到 tmp
      - 循环结束后,tmp 即为所有数字的和。
  3. return pro(n) - summation(n):返回两者之差。

复杂度分析

  • 时间复杂度\(O(d)\),其中 d 是 n 的十进制位数,最多约 \(O(\log n)\)
  • 空间复杂度\(O(1)\),只使用常数级额外空间。

11. 2 的幂

给你一个整数 n,请判断该整数是否是 2 的幂次方。如果是,返回 true;否则,返回 false

如果存在一个整数 x 使得 \(n == 2^x\),则认为 n 是 2 的幂次方。

  • 示例 1
     输入:n = 1
        输出:true
  • 示例 2
     输入:n = 16
        输出:true
  • 示例 3
     输入:n = 3
        输出:false
  • 提示
    \(-2^{31} \le n \le 2^{31} - 1\)

代码及注释

class Solution:
    def isPowerOfTwo(self, n: int) -> bool:
        # 排除非正数情况
        # 2 的幂在二进制中只有一个 '1',n & (n-1) 会将最低位的 '1' 清零
        # 若结果为 0,则说明原数只有这一位 '1'
        return n > 0 and n & (n - 1) == 0

知识点讲解

  • 二进制位与幂次关系:2 的幂在二进制表示下恰好只有一位是1,其余位全为0。
  • 位运算清零技巧n & (n-1) 会将 n 二进制中最低位的1置为0,其余位保持不变。
  • 判断唯一性:如果 n 只有一个1,则 n & (n-1) 结果为0;若有多于一个1,则结果非0。
  • 非正数排除:对非正整数(包括0和负数)直接返回 false

代码详解

  1. n > 0:先确保 n 是正数。
  2. n & (n - 1):对 nn-1 进行按位与运算。
  3. == 0:当且仅当 n 二进制中只有一位1时,上述运算结果才为0。
  4. 整体表达式 n > 0 and n & (n - 1) == 0:先判断正数,再进行位运算判断,返回布尔值。

复杂度分析

  • 时间复杂度\(O(1)\),仅包含常量次位运算和比较。
  • 空间复杂度\(O(1)\),只使用固定数量的额外变量。

12. 3 的幂

给定一个整数 n,判断它是否是 3 的幂次方。如果存在整数 x 使得 \(n == 3^x\),则认为 n 是 3 的幂次方。返回 truefalse

代码及注释

class Solution:
    def isPowerOfThree(self, n: int) -> bool:
        # 只对正数有效
        # 3^19 = 1162261467,是 32 位有符号整数范围内最大的 3 的幂
        # 如果 n 是 3 的幂,则必能整除 3^19
        return n > 0 and pow(3, 19) % n == 0

知识点讲解

  • 最大幂选择:32 位有符号整型上限约为 \(2^{31}−1\),3 的最大幂是 \(3^{19} = 1162261467\)
  • 整除判定:若 n 是 3 的幂,则 n | \(3^{19}\),即 \(3^{19} \text{ % } n == 0\);反之若能整除,则 n 只能是 3 的幂。
  • 常数时间:无需循环或递归,直接常数次运算判断。

代码详解

  1. n > 0:剔除非正数(0 或负数)。
  2. pow(3, 19):计算 3 的 19 次方常量。
  3. pow(3, 19) % n == 0:检查 n 是否为该常量的约数。
  4. 结合逻辑与返回最终布尔值。

复杂度分析

  • 时间复杂度\(O(1)\) — 仅做常数次幂运算和取模。
  • 空间复杂度\(O(1)\) — 只使用固定数量的变量和常量。

13. 丑数

丑数 就是只包含质因数 235 的正整数。

给你一个整数 n,判断其是否为丑数。如果是,返回 true;否则,返回 false

代码及注释

class Solution:
    def isUgly(self, n: int) -> bool:
        # 丑数必须为正整数
        if n <= 0:
            return False
        # 依次剥除所有 2、3、5 因子
        for p in (2, 3, 5):
            # 只要能被 p 整除,就不断除以 p
            while n % p == 0:
                n //= p
        # 如果剩下 1,则只有 2、3、5 作为质因数
        return n == 1

知识点讲解

  • 质因数剥除:通过不断除以允许的质因数,将其它因子“过滤”掉,最终判断剩余是否为 1。
  • 循环/条件判断:外层遍历每个质因数,内层 while 循环剥除所有相同因子。
  • 正整数检查:非正整数(0 或负数)直接排除,不可能是丑数。

代码详解

  1. if n <= 0: return False:排除非正数。
  2. for p in (2, 3, 5):遍历可接受的质因数。
  3. while n % p == 0: n //= p:只要当前因子 p 能整除 n,就将其剔除,并更新 n
  4. 循环结束后,如果 n 被完全剥除至 1,说明原始 n 仅含 2、3、5 这三种质因数,否则包含其它质因数。
  5. return n == 1:返回布尔判断结果。

复杂度分析

  • 时间复杂度\(O(\log n)\),每次除法操作使 n 至少减半或更快,三种质因数共计操作次数为 \(O(\log n)\)
  • 空间复杂度\(O(1)\),仅使用常数级额外变量。

14. 重新排列数组

给你一个数组 nums ,数组中有 2n 个元素,按 [x₁,x₂,…,xₙ,y₁,y₂,…,yₙ] 的格式排列。请你将数组按 [x₁,y₁,x₂,y₂,…,xₙ,yₙ] 格式重新排列,返回重排后的数组。

  • 示例 1
     输入:nums = [2,5,1,3,4,7], n = 3
        输出:[2,3,5,4,1,7]
  • 示例 2
     输入:nums = [1,2,3,4,4,3,2,1], n = 4
        输出:[1,4,2,3,3,2,4,1]
  • 示例 3
     输入:nums = [1,1,2,2], n = 2
        输出:[1,2,1,2]

代码及注释

class Solution:
    def shuffle(self, nums: List[int], n: int) -> List[int]:
        li: list[int] = []                 # 存放重排结果
        for i in range(n):                 # 遍历前 n 个 xi
            li.append(nums[i])             # 依次添加 xi
            li.append(nums[i + n])         # 依次添加对应的 yi
        return li                          # 返回重排后的数组

知识点讲解

  • 列表拼接:通过 append 逐对加入元素,避免创建中间子列表。
  • 下标计算nums[i] 对应 \(x_i\)nums[i+n] 对应 \(y_i\)
  • 空间使用:额外列表 li 长度为 2n,与输入规模同量级。

代码详解

  1. 初始化空列表 li 用于存放结果。
  2. 使用 for i in range(n) 遍历索引 0 到 n-1:
      - nums[i] 为第 i+1 个前半部分元素 \(x_i\),加入 li
      - nums[i+n] 为对应的后半部分元素 \(y_i\),随后加入。
  3. 循环完成后,li 即为 [x₁,y₁,x₂,y₂,…,xₙ,yₙ]
  4. 返回 li

复杂度分析

  • 时间复杂度\(O(n)\),遍历 n 次并执行常数次 append
  • 空间复杂度\(O(n)\),额外使用长度为 2n 的列表。

15. 转置矩阵

给你一个二维整数数组 matrix,返回 matrix 的转置矩阵。

矩阵的 转置 是指将矩阵的主对角线翻转,交换矩阵的行索引与列索引。

代码及注释

class Solution:
    def transpose(self, matrix: List[List[int]]) -> List[List[int]]:
        # 原矩阵的行数
        n: int = len(matrix)
        # 原矩阵的列数(假设所有行长度相同)
        m: int = len(matrix[0])
        # 初始化 m 行 n 列的零矩阵,用来存放转置结果
        result: List[List[int]] = [[0] * n for _ in range(m)]
        # 遍历原矩阵所有位置
        for i in range(n):
            for j in range(m):
                # 将 matrix[i][j] 放到 result[j][i]
                result[j][i] = matrix[i][j]
        return result

知识点讲解

  • 矩阵转置:将原矩阵的第 i 行第 j 列元素,放到新矩阵的第 j 行第 i 列。
  • 列表推导式初始化二维数组[[0] * n for _ in range(m)] 生成 m 行 n 列的零矩阵。
  • 双重循环遍历:外层遍历行索引 i,内层遍历列索引 j,保证每个元素都被访问并赋值。
  • 索引映射:转置时行列索引互换,即 (i, j) → (j, i)。

代码详解

  1. n = len(matrix)m = len(matrix[0]):分别获取原矩阵的行数和列数。
  2. result = [[0] * n for _ in range(m)]:构造一个 m×n 的全零矩阵用于存放结果。
  3. 双层 for 循环:
      - for i in range(n):枚举原矩阵的行索引;
      - for j in range(m):枚举原矩阵的列索引;
  4. 在循环体内执行 result[j][i] = matrix[i][j],完成行列互换的赋值。
  5. 循环结束后返回 result

复杂度分析

  • 时间复杂度\(O(n × m)\),需访问并赋值原矩阵中所有 n×m 个元素。
  • 空间复杂度\(O(n × m)\),额外开辟一个尺寸为 m×n 的结果矩阵。

16. 分割字符串的最大得分

给你一个由字符 '0' 和 '1' 组成的字符串 s,请你将字符串 s 按某个位置 分割 成两个 非空 子字符串(左子字符串和右子字符串),使得得分最大。

  • 分割字符串的 得分 定义为:左子字符串中 '0' 的数量 + 右子字符串中 '1' 的数量。
  • 返回可以得到的 最大得分

示例 1

  • 输入:s = "011101"
    输出:5
    解释:  
    将 s 分成 "0" | "11101",得分 = 1 + 4 = 5  
    其他分割方式得分都不高于 5

示例 2
输入:s = "00111"
输出:5
解释:将 s 分成 "00" | "111",得分 = 2 + 3 = 5

示例 3
输入:s = "1111"
输出:3

代码及注释

class Solution:
    def maxScore(self, s: str) -> int:
        # left:当前分割点左边子串的长度,最小为 1
        # right:字符串总长度
        # score:记录遍历所有分割后能得到的最大得分
        left, right, score = 1, len(s), 0
        # 尝试所有可能的分割位置,分割点从 1 到 len(s)-1
        while left < right:
            score_tmp = 0  # 本次分割的得分
            # 统计左子串中 '0' 的个数
            for i in range(left):
                if s[i] == '0':
                    score_tmp += 1
            # 统计右子串中 '1' 的个数
            for i in range(left, right):
                if s[i] == '1':
                    score_tmp += 1
            # 更新最大得分
            score = max(score, score_tmp)
            left += 1  # 移动分割点到下一个位置
        return score

知识点讲解

  • 分割枚举:要使左右子串都非空,分割点 left 从 1 遍历到 len(s)-1
  • 计数统计:分别对左边区间和右边区间进行遍历统计,累加零和一的数量。
  • 临时和与最大值:每次分割都算出一个临时得分 score_tmp,并与当前最大值 score 比较、更新。
  • 可优化思路:利用前缀和/后缀和数组预先统计零和一的累计次数,可以将统计从 \(O(n)\) 降到 \(O(1)\),整体时间降为 \(O(n)\)

代码详解

初始化

  • left = 1:首个分割点在第一个字符和第二个字符之间;
  • right = len(s):总长度,用于确定右子串区间终点;
  • score = 0:存放迄今为止的最优得分。

分割循环

while left < right:
    score_tmp = 0
    ...
    left += 1
  • 保证左、右子串都非空;每次循环 left 向右移动一个位置。

左子串统计

for i in range(left):
    if s[i] == '0':
        score_tmp += 1
  • 遍历 [0, left-1],统计 '0' 的个数。

右子串统计

for i in range(left, right):
    if s[i] == '1':
        score_tmp += 1
  • 遍历 [left, len(s)-1],统计 '1' 的个数。

更新答案

score = max(score, score_tmp)
  • 将本次分割得分与全局最优比较,取较大者。

返回结果

  • 循环结束后,score 即为最大可得分。

复杂度分析

  • 时间复杂度\(O(n²)\),共有 \(O(n)\) 个分割点,每次需要 \(O(n)\) 遍历统计。
  • 空间复杂度\(O(1)\),只使用常数级额外变量,不依赖于输入大小。

优化提示
通过一次前缀统计 zeros[i](前 i 个字符中 '0' 的累计数)和一次后缀统计 ones[i](从 i 到结尾中 '1' 的累计数),即可在 \(O(1)\) 时间内计算每个分割点的得分,使总时间降为 \(O(n)\)


17. 统计范围内的元音字符串数

给你一个下标从 0 开始的字符串数组 words 和两个整数 leftright

如果字符串以元音字母开始并以元音字母结束,那么该字符串就是元音字符串,其中元音字母是 'a'、'e'、'i'、'o'、'u'。

返回 words[i] 是元音字符串的数目,其中 i 在区间 [left, right] 内。

代码及注释

class Solution:
    def vowelStrings(self, words: List[str], left: int, right: int) -> int:
        # words:字符串数组
        # left, right:统计范围左右索引(包含)
        res: int = 0
        vowels: list[str] = ['a', 'e', 'i', 'o', 'u']  # 元音字母集合
        # 遍历索引 left..right
        for i in range(left, right + 1):
            w = words[i]

            # 判断首尾字符是否都是元音
            if w[0] in vowels and w[-1] in vowels:
                res += 1
        return res

知识点讲解

  • 索引访问words[i] 获取第 i 个字符串,w[0]w[-1] 分别取首尾字符。
  • 列表成员判断x in vowels 用于判断字符 x 是否为元音。
  • 循环遍历区间for i in range(left, right+1) 枚举所有待检查索引。
  • 计数累加:符合条件时 res += 1,最终返回累计值。

代码详解

初始化

  • res = 0:用于记录元音字符串数量;
  • vowels = ['a','e','i','o','u']:列出所有元音字符。

遍历索引区间

for i in range(left, right + 1):
  • 枚举从 leftright(包含)的每个下标。

取出当前字符串

w = words[i]

判断首尾元音

if w[0] in vowels and w[-1] in vowels:
    res += 1
  • w[0] 是首字符,w[-1] 是尾字符,二者都在 vowels 中即为元音字符串。

返回结果

  • 遍历结束后,res 即为所求,直接返回。

复杂度分析

  • 时间复杂度\(O(m)\),其中 m = right − left + 1。每个字符串只做常数次索引和成员判断。
  • 空间复杂度\(O(1)\),仅使用常数级额外空间(计数器与元音列表)。

18. 山脉数组的峰顶索引

给定一个长度为 n 的整数 山脉 数组 arr,其中的值先严格递增到一个峰值元素,然后严格递减。返回峰值元素的下标。你必须设计并实现时间复杂度为 \(O(\log n)\) 的解决方案。

示例 1
输入:arr = [0,1,0]
输出:1

示例 2
输入:arr = [0,2,1,0]
输出:1

示例 3
输入:arr = [0,10,5,2]
输出:1

提示

  • 3 ≤ arr.length ≤ \(10^5\)
  • 0 ≤ arr[i] ≤ \(10^6\)
  • 题目数据保证 arr 是一个山脉数组(先严格递增到峰顶,再严格递减)。

代码及注释

class Solution:
    def peakIndexInMountainArray(self, arr: List[int]) -> int:
        # 在 0 .. len(arr)-2 范围内遍历每个下标 i
        # 找到第一个满足 arr[i] > arr[i+1] 的位置,即从增转降的转折点
        return next(i for i in range(len(arr) - 1)
                    if arr[i] > arr[i + 1])

知识点讲解

  • 山脉数组性质:数组先严格递增到峰顶,再严格递减。峰顶左侧 arr[i] < arr[i+1],峰顶右侧 arr[i] > arr[i+1]
  • 转折点即峰顶:第一个满足 arr[i] > arr[i+1] 的位置就是峰顶的下标。
  • 生成器表达式(i for i in … if …) 能按需惰性生成符合条件的下标,不用遍历完所有元素。
  • next() 取第一个next(...) 直接返回生成器的第一个元素,遇到第一个符合条件的位置立即停止,避免多余遍历。
  • 时间复杂度要求:虽然此写法最坏为 \(O(n)\),但题目鼓励使用二分查找将时间降至 \(O(\log n)\)

代码详解

range(len(arr) - 1)

  • 枚举所有可能的 “切换点” 下标 i,最后一个可比较位置是 len(arr)-2

条件判断 arr[i] > arr[i+1]

  • 当且仅当到达峰顶时,当前元素大于下一个元素,表示从增转降。

next(...)

  • 会遍历生成器得到的下标序列,返回第一个满足条件的 i,并终止迭代。

整体逻辑

  • 不需要显式写循环和中断,借助生成器和 next 一行搞定峰顶定位。

复杂度分析

  • 时间复杂度\(O(n)\) — 在最坏情况下需扫描到峰顶位置。但理想的二分查找解法可达到 \(O(\log n)\)
  • 空间复杂度\(O(1)\) — 只使用常数级额外空间。
posted @ 2025-10-11 16:14  二见原莉莉子  阅读(4)  评论(0)    收藏  举报