杨辉三角

经典dp, 没什么好说的

class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        dp = [[0] * i for i in range(1, numRows + 1)]

        for i in range(numRows):
            for j in range(len(dp[i])):
                if j == 0 or j == len(dp[i]) - 1:
                    dp[i][j] = 1
                else:
                    dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]

        return dp

最大子数组和

初见像滑窗, 但是没有找到好的方法作为维护条件, 再看像前缀和, 直接动态维护当前最小前缀和做差就好

突然发现有点像选不选问题, 有最优特性, 重叠特性, 而且状态无后效性, 也可以枚举, 直接一维 dp

# 前缀和遍历
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        ans = max(nums) # 这里注意初始化为最大的单个元素, 特判都是负数的情况
        min_pre = cur = 0
      
        for i in nums:
            cur += i
            # 这里注意先计算ans再计算min-pre, 不然会出现 0
            ans = max(ans, cur - min_pre)
            min_pre = min(cur, min_pre)

        return ans
# dp 写法
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        cur = ans = nums[0]
      
        # cur 作为 dp[i], 状态转移方程为 dp[i] = max(i, dp[i - 1] + i), 也就是单独一个 i, 还是把 i 进入前一个数组
        for i in nums[1:]:
            cur = max(i, cur + i)
            ans = max(ans, cur)
          
        return ans
      

合并区间

难点在于, 如果直接暴力设置一个布尔数组判断区间重叠, 数据量会超时, 得寻找一个方便的合并方式

可以先按区间起始点排序, 这样重叠只会出现在相邻两个区间之间, 然后动态维护ans的最后一个区间就好

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        intervals.sort(key = lambda x: x[0])
        ans = []
        for i, j in intervals:
            # ans 为空或者尾元素比当前区间严格小, 直接添加
            if not ans or ans[-1][1] < i:
                ans.append([i, j])
            else:
                # 有重叠需判断是吞并还是合并
                ans[-1][1] = max(ans[-1][1], j)
      
        return ans

轮换数组

需注意, 没有任何地方有说明 k < n

# for 写法三次反转法 O(1) 空间复杂度, 我也不知道为什么我要脑抽硬写 for
class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        k %= n
        mid = n - k
        for i in range(mid // 2):
            nums[i], nums[mid -  i - 1] = nums[mid - i - 1], nums[i]

        for i in range(k // 2):
            nums[mid + i], nums[n - i - 1] = nums[n - i - 1], nums[mid + i]

        for i in range(n // 2):
            nums[i], nums[n - i - 1] = nums[n - i - 1], nums[i]
          
# while 封装函数
class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        k %= n
        mid = n - k

        def rev(l: int, r: int) -> None:
            while l < r:
                nums[l], nums[r] = nums[r], nums[l]          
                l += 1
                r -= 1
          
        rev(0, mid - 1)
        rev(mid, n - 1)
        rev(0, n - 1)
# 直接切片法, 空间复杂度O(n), 但是最简单
class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        k %= n

        nums[:] = nums[-k:] + nums[:-k]
# 还有一种循环置换法, 没想出来, 看着很复杂, 跑路了

除自身以外数组的乘积

题目要求不能使用除法, 且时间复杂度为 O(n), 那么就只能线性遍历, 先做一遍前缀左乘积,再然后后缀右乘积,把结果原地乘进去

# 简单明了的版本(也就是初版)
class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        l = nums.copy()
        r = nums.copy()
        n = len(nums)
        ans = [0] * n

        for i in range(1, n):
            l[i] *= l[i - 1]

        ans[-1] = l[-2]
  
        for i in range(n - 2, 0, -1):
            r[i] *= r[i + 1]
            ans[i] = l[i - 1] * r[i + 1]
          
        ans[0] = r[1]

        return ans
# 空间优化版(O(1))
class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        n = len(nums)
        ans = [1] * n
		# 直接保存左前缀乘积, 而不是当前乘积
        for i in range(1, n):
            ans[i] = ans[i - 1] * nums[i - 1]
	
        r = 1
      
        for i in range(n - 1, -1, -1):
            ans[i] *= r # 先 *r, 这样才是后缀
            r *= nums[i] # 更新后缀 

        return ans

一天是牛马, 一辈子是牛马