5.3
题目链接
53. 最大子序和
很喜欢的解法(DP)
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
maxSum=nums[0]
tmp=0
for i in nums:
tmp=max(tmp+i,i)
maxSum=max(maxSum,tmp)
return maxSum
官方解(分治)
参考题解:最大子序和
但是仔细观察「方法二」,它不仅可以解决区间 [0,n−1],还可以用于解决任意的子区间 [l,r] 的问题。如果我们把 [0,n−1] 分治下去出现的所有子区间的信息都用堆式存储的方式记忆化下来,即建成一颗真正的树之后,我们就可以在 O(logn) 的时间内求到任意区间内的答案,我们甚至可以修改序列中的值,做一些简单的维护,之后仍然可以在 O(logn) 的时间内求到任意区间内的答案,对于大规模查询的情况下,这种方法的优势便体现了出来。这棵树就是上文提及的一种神奇的数据结构——线段树。
参考题解:【五种解法三种语言】(Java, JavaScript, Python)
import sys
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
return self.helper(nums, 0, len(nums) - 1)
def helper(self, nums, l, r):
if l > r:
return -sys.maxsize
mid = (l + r) // 2
left = self.helper(nums, l, mid - 1)
right = self.helper(nums, mid + 1, r)
left_suffix_max_sum = right_prefix_max_sum = 0
sum = 0
for i in reversed(range(l, mid)):
sum += nums[i]
left_suffix_max_sum = max(left_suffix_max_sum, sum)
sum = 0
for i in range(mid + 1, r + 1):
sum += nums[i]
right_prefix_max_sum = max(right_prefix_max_sum, sum)
cross_max_sum = left_suffix_max_sum + right_prefix_max_sum + nums[mid]
return max(cross_max_sum, left, right)
5.4
题目链接
45. 跳跃游戏 II
官方解(贪心)
如果我们「贪心」地进行正向查找,每次找到可到达的最远位置,就可以在线性时间内得到最少的跳跃次数。
例如,对于数组 [2,3,1,2,4,2,3],初始位置是下标 0,从下标 0 出发,最远可到达下标 2。下标 0 可到达的位置中,下标 1 的值是 3,从下标 1 出发可以达到更远的位置,因此第一步到达下标 1。
class Solution:
def jump(self, nums: List[int]) -> int:
n = len(nums)
maxPos, end, step = 0, 0, 0
for i in range(n - 1):
if maxPos >= i:
maxPos = max(maxPos, i + nums[i])
if i == end:
end = maxPos
step += 1
return step
自己有参考的解法
# 之前看评论,说用if比用max好像要快些……
class Solution:
def jump(self, nums: List[int]) -> int:
count=0
begin,end=0,0
while endnowmax:
nowmax=i+nums[i]
begin,end=end+1,nowmax
count+=1
return count
5.6
题目链接
983. 最低票价
很喜欢的解法(DP,从前向后)
参考题解:【熊猫刷题Python3】一维动态规划,易懂(附视频题解)
对于一年中的任意一天:
首先考虑当前天数是否在 days 中,如果不在,那我们可以贪心地选择不买。这是因为如果今天不用出行,那么也不必购买通行证,并且通行证越晚买越好。
如果在的话,我们就要从三种购买方式中选择一种花费费用最少的,即如果想到达第 i 天,就需要从 i 的前1或7或30天的后一位置花费对应cost[0]、cost[1]、cost[2]的钱才能到第 i 天。
class Solution:
def mincostTickets(self, days: List[int], costs: List[int]) -> int:
dp=[0 for i in range(days[-1]+1)]
index=0
for i in range(days[0],len(dp)):
if i!=days[index]:
dp[i]=dp[i-1]
else:
dp[i]=min(dp[max(0,i-1)]+costs[0],dp[max(0,i-7)]+costs[1],dp[max(0,i-30)]+costs[2])
index+=1
return dp[-1]
官方解(DP,从后向前)
我们用 dp(i) 来表示从第 i 天开始到一年的结束,我们需要花的钱。考虑到一张通行证可以让我们在「接下来」的若干天进行旅行,所以我们「从后往前」倒着进行动态规划。
但是观察方法一的递推式,我们可以看到,如果我们查询 dp(i),而第 i 天我们又不需要出行的话,那么 dp 函数会一直向后计算 dp(i+1)=dp(i+2)=dp(i+3) 一直到一年结束或者有一天我们需要出行为止。那么我们其实可以直接跳过这些不需要出行的日期,直接找到下一个需要出行的日期。
class Solution:
def mincostTickets(self, days: List[int], costs: List[int]) -> int:
N = len(days)
durations = [1, 7, 30]
@lru_cache(None)
def dp(i):
if i >= N:
return 0
ans = 10**9
j = i
for c, d in zip(costs, durations):
while j < N and days[j] < days[i] + d:
j += 1
ans = min(ans, dp(j) + c)
return ans
return dp(0)
5.7
题目链接
572. 另一个树的子树
很喜欢的解法(递归判断子树)
具体操作=官方题解1,即DFS暴力匹配,但是逻辑很清楚所以很喜欢。
参考题解:对称美!判断子树 vs 判断相等!
判断两个树是否相等的三个条件是与的关系,即:
1.当前两个树的根节点值相等;
2.并且,s 的左子树和 t 的左子树相等;
3.并且,s 的右子树和 t 的右子树相等。
而判断 t 是否为 s 的子树的三个条件是或的关系,即:
1.当前两棵树相等;
2.或者,t 是 s 的左子树;
3.或者,t 是 s 的右子树。
class Solution:
def isSubtree(self, s: TreeNode, t: TreeNode) -> bool:
def isSame(node1,node2):
if not node1 and not node2:
return True
if not node1 or not node2:
return False
return isSame(node1.left,node2.left) and isSame(node1.right,node2.right) and node1.val==node2.val
def isSub(node1,node2):
if not node1 and not node2:
return True
if not node1 or not node2:
return False
return isSub(node1,node2.left) or isSub(node1,node2.right) or isSame(node1,node2)
return isSub(t,s)
自己的解法(前序遍历比较结果)
前序遍历对单子树而言,得出的序列是真正的子树序列
具体思路基本上=官方题解2,即DFS序列上的串匹配。不过我不会写KMP……这个算法我就只能脑内跑结果(。以后有空试着写下……
class Solution:
def isSubtree(self, s: TreeNode, t: TreeNode) -> bool:
def helper(node,r):
if not node:
r.append('null')
return
r.append(str(node.val))
helper(node.left,r)
helper(node.right,r)
ans1,ans2=[''],['']
helper(t,ans1)
helper(s,ans2)
ans1=' '.join(ans1)
ans2=' '.join(ans2)
return True if ans1 in ans2 else False
官方解(树哈希)
参考题解:另一个树的子树
考虑把每个子树都映射成一个唯一的数,如果 t 对应的数字和 s 中任意一个子树映射的数字相等,则 t 是 s 的某一棵子树。
……我有点儿不懂……先马住,慢慢学习(
5.8
题目链接
221. 最大正方形
很喜欢的解法(DP)
参考题解:理解 三者取最小+1
上面详解了 三者取最小 的含义:
图1:受限于左上的0
图2:受限于上边的0
图3:受限于左边的0
数字表示:以此为正方形右下角的最大边长
黄色表示:格子 ? 作为右下角的正方形区域
class Solution:
def maximalSquare(self, matrix: List[List[str]]) -> int:
if matrix==[]:
return 0
matrix=[[0]*(len(matrix[0])+1)]+[[0]+list(map(int,i)) for i in matrix]
s=0
for i in range(1,len(matrix)):
for j in range(1,len(matrix[0])):
if matrix[i][j]==1:
matrix[i][j]=min(matrix[i-1][j-1],matrix[i-1][j],matrix[i][j-1])+1
s=max(s,matrix[i][j])
return s**2
5.15
题目链接
560. 和为K的子数组
很喜欢的解法(前缀和+哈希表)
参考题解:【熊猫刷题Python3】前缀和+字典,易懂 (附视频题解)
比如我们到某一个位置 i 得到前缀和为 9,也就说从 0 位置到 i 位置的所有数字的和为 9, 如果目标 k 为 3,那么我们只需要找到当前状态下,前面出现了几次 6,就知道以 nums[i] 结尾的和为 3 的连续子数组的个数有多少个。
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
tmp=defaultdict(int)
tmp[0]=1
cur_sum = 0
res = 0
for i in nums:
cur_sum += i
if cur_sum - k in tmp:
res += tmp[cur_sum - k]
tmp[cur_sum] += 1
return res
5.18
题目链接
152. 乘积最大子数组
官方解(DP)
参考题解:乘积最大子数组
class Solution:
def maxProduct(self, nums: List[int]) -> int:
maxf=nums[0]
minf=nums[0]
ans=nums[0]
for i in range(1,len(nums)):
tmp1,tmp2=maxf,minf
maxf=max(tmp1*nums[i],max(nums[i],tmp2*nums[i]))
minf=min(tmp2*nums[i],min(nums[i],tmp1*nums[i]))
ans=max(ans,maxf)
return ans