目录
目录
[一、53 最大子数组的和](#一、53 最大子数组的和)
[二、56 合并区间](#二、56 合并区间)
前言
碎碎念:貌似,资本市场的本质就是有自制力的人赚没自制力的人的钱。昔日压迫我的,终成我的权柄,昔日伤害我的,终成我的铠甲。
我感觉,真正拿到题的时候,不推导递归数据,真的做不出来
再明确
转换一下邪修刷题思路:
第一轮(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个元素移到数组开头,其余元素依次后移)。
- 核心要求:
- 轮转是向右 的(如
nums = [1,2,3,4,5,6,7],k=3,轮转后为[5,6,7,1,2,3,4]);- 需原地修改数组(空间复杂度尽可能低),或允许创建辅助数组(暴力法);
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]之外其余所有元素的乘积。
- 核心要求:
- 不能使用除法(避免数组中有 0 时无法计算,且题目明确禁止);
- 时间复杂度需为 O(n)(不能嵌套循环);
- 进阶要求:空间复杂度为 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),核心规则:
start:遍历的起始索引(包含);stop:遍历的终止索引 (不包含,遍历到stop的前一个数为止);step:步长(每次索引的变化量,正数 = 正序,负数 = 逆序)。
简单记:range 是「左闭右开」区间 → [start, stop)。
五、560和为k的子数组
1、题目描述
你需要解决的问题是:给定一个整数数组
nums和一个整数k,请你统计并返回该数组中和为k的连续子数组的个数。
- 核心要求:
- 子数组是连续的(区别于子序列);
- 数组中可以包含正数、负数、0(这是双指针法不适用的关键原因);
- 需统计所有符合条件的子数组数量,而非仅判断是否存在;
- 示例:
- 输入:
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] = 0,pre_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] = k→pre_sum[i] = pre_sum[j] - k,统计pre_sum[i]出现的次数即可。
- 前缀和
- 步骤:
- 初始化哈希表
pre_sum_map(记录前缀和→出现次数),初始值{0:1}(处理前缀和本身等于 k 的情况 ,如pre_sum[j]=k时,pre_sum[i]=0对应子数组nums[0...j-1]); - 初始化当前前缀和
current_pre = 0,计数count = 0; - 遍历数组每个元素:
- 累加当前元素到
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 后更新哈希表,记录当前前缀和的出现次数;