leetcode_day13_普通数组_《绝境求生》

目录

目录

目录

​前言

再明确

[一、53 最大子数组的和](#一、53 最大子数组的和)

[二、56 合并区间](#二、56 合并区间)

三、189轮转数组

四、238除自身以外数组的乘积

五、560和为k的子数组


​前言

碎碎念:貌似,资本市场的本质就是有自制力的人赚没自制力的人的钱。昔日压迫我的,终成我的权柄,昔日伤害我的,终成我的铠甲。

我感觉,真正拿到题的时候,不推导递归数据,真的做不出来


再明确

转换一下邪修刷题思路:

第一轮(5天):每天20道题,过一遍思路后看答案手敲,不会就看答案,不要求拿到题能做出来。 然后总结模版到csdn。 有空就看当天的总结,养成用嘴说思路的习惯,第二轮一边敲一边念。

第二轮(一直repeat until 面试):每天10道题+5道题(前一天main),表格记录每道题做多少遍错多少次,错因是什么。用acm格式写法

左右数组储存

一、53 最大子数组的和

1、题目描述

你需要解决的问题是:给定一个整数数组 nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

  • 子数组定义:数组中连续的元素序列(区别于子序列);
  • 示例:
    • 输入:nums = [-2,1,-3,4,-1,2,1,-5,4],输出:6(最大子数组是 [4,-1,2,1],和为 6);
    • 输入:nums = [1],输出:1
    • 输入:nums = [5,4,-1,7,8],输出:23
  • 边界条件:
    • 数组长度为 1 时,直接返回该元素;
    • 数组全为负数时,返回最大的那个负数(如 nums = [-3,-1,-2],输出 -1)。

2、简单理解?

暴力法枚举所有可能的连续子数组,计算每个子数组的和,记录最大值

3、用图示意

4、暴力法

会超出时间限制

python 复制代码
def main():
    import sys
    # ACM 格式读取输入:输入为空格分隔的整数数组(如 "-2 1 -3 4 -1 2 1 -5 4")
    nums = list(map(int, sys.stdin.readline().strip().split()))
    n = len(nums)
    
    # 边界条件:数组为空(题目要求子数组至少一个元素,实际输入不会空)
    if n == 0:
        print(0)
        return
    
    max_sum = nums[0]  # 初始化最大和为第一个元素(处理全负数边界)
    
    # 枚举所有起始索引i
    for i in range(n):
        current_sum = 0  # 重置当前子数组和
        # 枚举结束索引j,从i开始累加
        for j in range(i, n):
            current_sum += nums[j]
            # 更新最大和
            if current_sum > max_sum:
                max_sum = current_sum
    
    # ACM 格式输出结果
    print(max_sum)

if __name__ == "__main__":
    main()

5、优化法

动态规划+贪心

current_sum = max(num, current_sum + num)

  • current_sum + num < num(即前序子数组和为负),则放弃前序,current_sum = num
  • current_sum 的定义是「以当前元素结尾的最大子数组和」,而非任意子数组和,这是 Kadane 算法的核心;
  • 无需额外空间存储所有 current_sum,仅需维护两个变量,空间复杂度 O(1);
  • 遍历仅需一次,时间复杂度 O(n)。

初始化

  • 暴力法:max_sum = nums[0](必须初始化为数组第一个元素,而非 0!否则全负数数组会错误返回 0);
  • 动态规划:current_sum = nums[0]max_sum = nums[0](同暴力法,避免全负数边界错误);
python 复制代码
def main():
    import sys
    # ACM 格式读取输入
    nums = list(map(int, sys.stdin.readline().strip().split()))
    n = len(nums)
    
    # 边界条件:数组长度为1,直接返回该元素
    if n == 1:
        print(nums[0])
        return
    
    # 初始化:current_sum=以当前元素结尾的最大和;max_sum=全局最大和
    current_sum = nums[0]
    max_sum = nums[0]
    
    # 从第二个元素开始遍历
    for num in nums[1:]:
        # 核心逻辑:要么加入前序子数组,要么以当前元素重新开始
        current_sum = max(num, current_sum + num)
        # 更新全局最大和
        max_sum = max(max_sum, current_sum)
    
    # ACM 格式输出结果
    print(max_sum)

if __name__ == "__main__":
    main()

6、疑惑点/新知识 ? 之前见过但没注意到的?

ACM 格式读取输入:输入为空格分隔的整数数组(如 "-2 1 -3 4 -1 2 1 -5 4")

nums = list(map(int, sys.stdin.readline().strip().split()))


二、56 合并区间

1、题目描述

你需要解决的问题是:给定一个区间集合 intervals,其中每个区间表示为 [start, end],合并所有重叠的区间,返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。

  • 核心要求:重叠 / 相邻的区间需合并(如 [1,3][2,6] 合并为 [1,6][1,4][4,5] 也需合并为 [1,5]);
  • 示例:
    • 输入:intervals = [[1,3],[2,6],[8,10],[15,18]],输出:[[1,6],[8,10],[15,18]]
    • 输入:intervals = [[1,4],[4,5]],输出:[[1,5]]
  • 边界条件:
    • 输入为空数组([]):返回空数组;
    • 输入只有一个区间([[1,2]]):直接返回原区间;
    • 区间完全包含(如 [[1,10],[2,3],[4,5]]):合并为 [[1,10]]

2、简单理解?

按区间的起始值排序,使得重叠和相邻之间连续,再用贪心思想逐个合并区间

3、能不能用图示意?

4、暴力法

python 复制代码
def main():
    import sys
    # ACM 格式读取输入:输入为二维数组,如 "[[1,3],[2,6],[8,10],[15,18]]"
    input_str = sys.stdin.readline().strip()
    # 解析输入为二维整数数组
    intervals = eval(input_str) if input_str else []
    n = len(intervals)
    
    # 边界条件:空数组直接返回
    if n == 0:
        print([])
        return
    # 边界条件:单个区间直接返回
    if n == 1:
        print(intervals)
        return
    
    # 初始化结果列表为原数组拷贝
    res = [interval.copy() for interval in intervals]
    merged = True  # 标记是否发生合并
    
    # 循环直到无合并操作
    while merged:
        merged = False
        i = 0
        while i < len(res):
            j = i + 1
            while j < len(res):
                a = res[i]
                b = res[j]
                # 判断两个区间是否重叠(核心条件)
                if a[0] <= b[1] and b[0] <= a[1]:
                    # 合并区间:取最小起始,最大结束
                    new_interval = [min(a[0], b[0]), max(a[1], b[1])]
                    # 移除原有两个区间
                    del res[j]
                    del res[i]
                    # 加入合并后的区间
                    res.insert(i, new_interval)
                    merged = True  # 标记发生合并,需重新遍历
                    break  # 跳出j循环,重新开始i遍历
                j += 1
            if merged:
                break
            i += 1
    
    # ACM 格式输出结果
    print(res)

if __name__ == "__main__":
    main()

5、优化法

python 复制代码
def main():
    import sys
    input_str=sys.stdin.readline().strip()
    intervals=eval(input_str) if input_str else []
    n=len(intervals)
    
    if n==0:
        print([])
        return
    if n==1:
        print(intervals)
        return 
    intervals.sort(key=lambda x:x[0])  #起始值升序排列,使得重叠的区间连续
    result=[intervals[0].copy()]  #用copy深拷贝避免后续修改到原数组
    for cur in intervals[1:]:
        last=result[-1]
        if cur[0]<=last[1]:
            #合并区间操作
            last[1]=max(last[1],cur[1])
        else:
            #不重叠直接加入列表
            result.append(cur.copy())
    print(result)
if __name__=='__main__':
    main()

6、疑惑点/新知识 ?之前见过但没注意到的?

升序排列 数组.sort(key=lambda x:x[0]) ,即为[[1,3],[2,5]] 中的1


三、189轮转数组

1、题目描述

你需要解决的问题是:给定一个整数数组 nums 和一个整数 k,将数组中的元素向右轮转 k 个位置(即把数组末尾的 k 个元素移到数组开头,其余元素依次后移)。

  • 核心要求:
    1. 轮转是向右 的(如 nums = [1,2,3,4,5,6,7]k=3,轮转后为 [5,6,7,1,2,3,4]);
    2. 原地修改数组(空间复杂度尽可能低),或允许创建辅助数组(暴力法);
    3. k 可能大于数组长度(如 nums 长度为 7,k=10,等价于 k=3,因为 10 % 7 = 3);
  • 示例:
    • 输入:nums = [1,2,3,4,5,6,7], k = 3,输出:[5,6,7,1,2,3,4]
    • 输入:nums = [-1,-100,3,99], k = 2,输出:[3,99,-1,-100]
  • 边界条件:
    • 数组为空([])或长度为 1:轮转后不变;
    • k=0:数组无需修改;
    • k 等于数组长度:轮转后数组不变(等价于 k=0)。

2、简单理解?

三次反转法原地修改,

通过反转数组的特性实现:向右轮转 k 个位置 = 把数组分为「前 n-k 个元素」和「后 k 个元素」,交换两部分位置。

3、能不能用图示意?

4、暴力法

5、优化法

python 复制代码
def main():
    import sys
    # ACM 格式读取输入
    nums = list(map(int, sys.stdin.readline().strip().split()))
    k = int(sys.stdin.readline().strip())
    n = len(nums)
    
    # 边界条件处理
    if n <= 1:
        print(nums)
        return
    k = k % n
    if k == 0:
        print(nums)
        return
    
    # 定义反转函数:反转nums[left...right](闭区间)
    def reverse(left, right):
        while left < right:
            # 交换左右指针元素(原地交换)
            nums[left], nums[right] = nums[right], nums[left]
            left += 1
            right -= 1
    
    # 三次反转核心逻辑
    reverse(0, n-1)       # 1. 反转整个数组
    reverse(0, k-1)       # 2. 反转前k个元素
    reverse(k, n-1)       # 3. 反转后n-k个元素
    
    # ACM 格式输出结果
    print(nums)

if __name__ == "__main__":
    main()

6、疑惑点/新知识 ?之前见过但没注意到的?

比n小的k%n结果是自身,

三次反转确实很神奇的实现了轮转数组


四、238除自身以外数组的乘积

1、题目描述

你需要解决的问题是:给定一个整数数组 nums,返回一个数组 answer,其中 answer[i] 等于 nums 中除 nums[i] 之外其余所有元素的乘积。

  • 核心要求:
    1. 不能使用除法(避免数组中有 0 时无法计算,且题目明确禁止);
    2. 时间复杂度需为 O(n)(不能嵌套循环);
    3. 进阶要求:空间复杂度为 O(1)(除输出数组外,不额外占用空间);
  • 示例:
    • 输入:nums = [1,2,3,4],输出:[24,12,8,6](1234/1=24,1234/2=12,依此类推);
    • 输入:nums = [-1,1,0,-3,3],输出:[0,0,9,0,0](注意 0 的处理);
  • 边界条件:
    • 数组长度为 1:按题意,除自身外无元素,返回 [1]
    • 数组包含 0:需正确计算(如单个 0 时,对应位置为其他元素乘积,其余位置为 0);
    • 数组包含负数:乘积需保留符号。

2、简单理解?

  • 在左右乘积数组法的基础上,利用输出数组存储左乘积,再通过一个变量动态计算右乘积,最终完成计算,无需额外辅助数组。
  • 左右乘积数组法(进阶思路,空间 O(n))核心逻辑:将「除自身外乘积」拆分为「左边所有元素乘积 × 右边所有元素乘积」,通过两个辅助数组分别存储左右乘积,最后相乘得到结果。

3、能不能用图示意?

4、暴力法

5、优化法

python 复制代码
def main():
    import sys
    # ACM 格式读取输入
    nums = list(map(int, sys.stdin.readline().strip().split()))
    n = len(nums)
    
    # 初始化结果数组(存储左乘积,初始全1)
    answer = [1] * n
    
    # 边界条件:数组长度为1,直接返回[1]
    if n == 1:
        print(answer)
        return
    
    # 第一步:计算左乘积,存入answer
    for i in range(1, n):
        # answer[i] = 前一个位置的左乘积 × 前一个元素(即nums[i-1])
        answer[i] = answer[i-1] * nums[i-1]
    
    # 第二步:逆序计算右乘积,动态更新answer
    right_prod = 1  # 初始化右乘积为1(最后一个元素右侧无元素)
    for i in range(n-1, -1, -1):
        # 当前位置结果 = 左乘积 × 右乘积
        answer[i] = answer[i] * right_prod
        # 更新右乘积:包含当前元素(为前一个位置的右乘积做准备)
        right_prod = right_prod * nums[i]
    
    # ACM 格式输出结果
    print(answer)

if __name__ == "__main__":
    main()

6、疑惑点/新知识 ?之前见过但没注意到的?

range(n-1, -1, -1) 是 Python 中逆序遍历数组索引的核心写法

Python 中 range() 的完整格式是:range(start, stop, step),核心规则:

  1. start:遍历的起始索引(包含);
  2. stop:遍历的终止索引 (不包含,遍历到 stop 的前一个数为止);
  3. step:步长(每次索引的变化量,正数 = 正序,负数 = 逆序)。

简单记:range 是「左闭右开」区间 → [start, stop)


五、560和为k的子数组

1、题目描述

你需要解决的问题是:给定一个整数数组 nums 和一个整数 k,请你统计并返回该数组中和为 k连续子数组的个数。

  • 核心要求:
    1. 子数组是连续的(区别于子序列);
    2. 数组中可以包含正数、负数、0(这是双指针法不适用的关键原因);
    3. 需统计所有符合条件的子数组数量,而非仅判断是否存在;
  • 示例:
    • 输入:nums = [1,1,1], k = 2,输出:2(子数组 [1,1](索引 0-1)、[1,1](索引 1-2));
    • 输入:nums = [1,2,3], k = 3,输出:2(子数组 [3][1,2]);
    • 输入:nums = [1,-1,0], k = 0,输出:3(子数组 [1,-1][-1,0][0]);
  • 边界条件:
    • 数组为空:返回 0;
    • 数组长度为 1:若元素等于 k 则返回 1,否则返回 0;
    • 子数组仅包含单个元素(如 nums=[5], k=5):需计入统计。

2、简单理解?

前缀和 + 哈希表(最优解法)

  • 核心逻辑:利用「前缀和」将「子数组和为 k」转化为「两个前缀和的差值为 k」,再通过哈希表快速统计前缀和出现的次数,避免重复计算。
  • 关键公式:
    • 前缀和 pre_sum[j]:表示数组前 j 个元素的和(pre_sum[0] = 0pre_sum[1] = nums[0]pre_sum[2] = nums[0]+nums[1]...);
    • 子数组 nums[i...j-1] 的和 = pre_sum[j] - pre_sum[i]
    • pre_sum[j] - pre_sum[i] = kpre_sum[i] = pre_sum[j] - k,统计 pre_sum[i] 出现的次数即可。
  • 步骤:
    1. 初始化哈希表 pre_sum_map(记录前缀和→出现次数),初始值 {0:1}(处理前缀和本身等于 k 的情况 ,如 pre_sum[j]=k 时,pre_sum[i]=0 对应子数组 nums[0...j-1]);
    2. 初始化当前前缀和 current_pre = 0,计数 count = 0
    3. 遍历数组每个元素:
      • 累加当前元素到 current_pre
      • current_pre - k 存在于哈希表中,说明存在前缀和等于 current_pre - k,将对应次数加到 count
      • 更新哈希表:将 current_pre 的出现次数加 1;
  • 优点:时间复杂度 O(n),空间复杂度 O(n),是本题最优解法(因数组含负数,双指针法不适用)。

3、能不能用图示意?

nums = [1,1,1], k = 2 为例,前缀和 + 哈希表的执行过程:

遍历元素 current_pre(当前前缀和) current_pre - k pre_sum_map 状态(前缀和→次数) count(计数) 说明
- 0(初始值) - {0:1} 0 初始化哈希表
nums[0]=1 1 1-2=-1 {0:1}(-1 不存在) 0 无符合条件的前缀和
{0:1, 1:1} 更新哈希表
nums[1]=1 2 2-2=0 {0:1}(0 出现 1 次) 0+1=1 找到 1 个符合条件的子数组
{0:1, 1:1, 2:1} 更新哈希表
nums[2]=1 3 3-2=1 {1:1}(1 出现 1 次) 1+1=2 找到第 2 个符合条件的子数组
{0:1, 1:1, 2:1, 3:1} 更新哈希表

最终计数为 2,与示例结果一致。

nums = [1,-1,0], k=0 为例:

遍历元素 current_pre current_pre - k pre_sum_map 状态 count
- 0 - {0:1} 0
1 1 1-0=1 {0:1} 0
{0:1,1:1}
-1 0 0-0=0 {0:1} 1
{0:2,1:1}
0 0 0-0=0 {0:2} 3
{0:3,1:1}

最终计数为 3,符合示例结果。

4、暴力法

5、优化法

python 复制代码
def main():
    import sys
    # ACM 格式读取输入
    nums = list(map(int, sys.stdin.readline().strip().split()))
    k = int(sys.stdin.readline().strip())
    n = len(nums)
    
    # 边界条件1:数组为空
    if n == 0:
        print(0)
        return
    
    # 初始化:
    # pre_sum_map:哈希表,记录前缀和→出现次数,初始{0:1}(处理pre_sum=k的情况)
    pre_sum_map = {0: 1}
    current_pre = 0  # 当前前缀和,初始为0
    count = 0        # 符合条件的子数组计数
    
    # 遍历数组每个元素
    for num in nums:
        # 1. 更新当前前缀和
        current_pre += num
        
        # 2. 核心:查找是否存在pre_sum = current_pre - k,存在则累加次数
        target = current_pre - k
        if target in pre_sum_map:
            count += pre_sum_map[target]
        
        # 3. 更新哈希表:当前前缀和的出现次数+1(不存在则默认0)
        pre_sum_map[current_pre] = pre_sum_map.get(current_pre, 0) + 1
    
    # ACM 格式输出结果
    print(count)

if __name__ == "__main__":
    main()

6、疑惑点/新知识 ?之前见过但没注意到的?

pre_sum_map[current_pre] = pre_sum_map.get(current_pre, 0) + 1

  • pre_sum_map.get(current_pre, 0):获取当前前缀和的现有次数(不存在则返回 0);
  • 加 1 后更新哈希表,记录当前前缀和的出现次数;

相关推荐
hetao17338372 小时前
2026-01-09~12 hetao1733837 的刷题笔记
c++·笔记·算法
过河卒_zh15667663 小时前
情感型AI被“立规矩”,AI陪伴时代进入下半场
人工智能·算法·aigc·生成式人工智能·算法备案
wefg13 小时前
【算法】动态规划
算法·动态规划
机器学习之心3 小时前
198种组合算法+优化TCN-Transformer+SHAP分析+新数据预测+多输出!深度学习可解释分析,强烈安利,粉丝必备!
深度学习·算法·transformer·shap分析·新数据预测
狐573 小时前
2026-01-12-LeetCode刷题笔记-1266-访问所有点的最小时间.md
笔记·算法·leetcode
Gorgous—l3 小时前
数据结构算法学习:LeetCode热题100-栈篇(有效的括号、最小栈、字符串解码、每日温度、柱状图中最大的矩形)
数据结构·学习·算法
小郭团队3 小时前
教育公平的探索
大数据·人工智能·嵌入式硬件·算法·硬件架构
瑞雨溪3 小时前
力扣题解:740.删除并获得点数
算法·leetcode·职场和发展
Watermelo6173 小时前
探究TOON的价值边界:比JSON更优的大模型友好数据格式?
数据结构·人工智能·语言模型·自然语言处理·数据挖掘·数据分析·json