背包问题进阶版,商品可无限选择直至选择到某固定金额
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-change
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
使用深度优先搜索直接超时
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
#使用深度优先搜索
if amount==0:
return 0
min_cnt,n=[amount+1],len(coins)
def dfs(sum_,i,cnt):
if sum_==amount:
min_cnt[0]=min(min_cnt[0],cnt)
return
if sum_>amount or i>=n:
return
dfs(sum_,i+1,cnt)
if sum_+coins[i]<=amount:
dfs(sum_+coins[i],i,cnt+1)
dfs(0,0,0)
return min_cnt[0] if min_cnt[0]!=amount+1 else -1
感觉自己厉害的一刻
本想半小时之内解决,从0开始思考到解决只用了大概5分钟,而且一次通过
给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。
示例:
输入: "sea", "eat"
输出: 2
解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/delete-operation-for-two-strings
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
#二维规划
m,n=len(word1),len(word2)
dp=[[0]*(n+1) for _ in range(m+1)]
for i in range(1,n+1):
dp[0][i]=i
for j in range(1,m+1):
dp[j][0]=j
for i in range(1,m+1):
for j in range(1,n+1):
if word1[i-1]==word2[j-1]:
dp[i][j]=dp[i-1][j-1]
else:
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+1
return dp[-1][-1]
高效性之原因疑惑
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/path-sum-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# 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 pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
ans=[]
if not root:
return []
def dfs(root,sum_,per):
if sum_==targetSum and not root.left and not root.right:
ans.append([i for i in per])
return
if root.left:
per.append(root.left.val)
dfs(root.left,sum_+root.left.val,per)
per.pop()
if root.right:
per.append(root.right.val)
dfs(root.right,sum_+root.right.val,per)
per.pop()
dfs(root,root.val,[root.val])
return ans
return []
数据结构知识点:
搜索二叉树不一定是平衡二叉树。
搜索二叉树最重要的性质:中序遍历是非递减序列
主要算法寻找该子树中有没有含有给出的节点
二叉树两节点的最近公共祖先
- 如果含有,返回True,否则返回False
- 最近公共祖先是
1.该节点得左右子树都返回True
2.左右子树有一个返回True,而节点本身是给出的两个节点中的一个。
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
ans=[]
def judge(root):
if not root :
return False
a,b=False,False
if root.left:
a=judge(root.left)
if root.right:
b=judge(root.right)
if a and b or (root==p or root==q) and (a or b):
#print('hi')
ans.append(root)
else:
return a or b or root==p or root==q
judge(root)
return ans[0]
算法之一大选择,能够记忆化的尽量浪费一点空间以换取时间的高效。
实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:
BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。
int next()将指针向右移动,然后返回指针处的数字。
注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。
你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。
示例:
输入
["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"]
[[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
输出
[null, 3, 7, true, 9, true, 15, true, 20, false]
解释
BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
bSTIterator.next(); // 返回 3
bSTIterator.next(); // 返回 7
bSTIterator.hasNext(); // 返回 True
bSTIterator.next(); // 返回 9
bSTIterator.hasNext(); // 返回 True
bSTIterator.next(); // 返回 15
bSTIterator.hasNext(); // 返回 True
bSTIterator.next(); // 返回 20
bSTIterator.hasNext(); // 返回 False
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-search-tree-iterator
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class BSTIterator:
def __init__(self, root: TreeNode):
'''
self.last=root
while self.last.left:
self.last=self.last.left
'''
self.deque=collections.deque()
def inorder(root):
if not root:
return
inorder(root.left)
self.deque.append(root)
inorder(root.right)
inorder(root)
def next(self) -> int:
t=self.deque.popleft()
return t.val
def hasNext(self) -> bool:
return len(self.deque)!=0
你可以设计一个满足下述条件的解决方案吗?next() 和 hasNext() 操作均摊时间复杂度为 O(1) ,并使用 O(h) 内存。其中 h 是树的高度。
该要求应该使用栈重写
class BSTIterator {
private:
TreeNode* cur;
stack stk;
public:
BSTIterator(TreeNode* root): cur(root) {}
int next() {
while (cur != nullptr) {
stk.push(cur);
cur = cur->left;
}
cur = stk.top();
stk.pop();
int ret = cur->val;
cur = cur->right;
return ret;
}
bool hasNext() {
return cur != nullptr || !stk.empty();
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/binary-search-tree-iterator/solution/er-cha-sou-suo-shu-die-dai-qi-by-leetcod-4y0y/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
字典的删除是pop(key),集合的删除是remove()
简单题换个方法可能要重新考虑边界
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def canJump(self, nums: List[int]) -> bool:
right,n,i=nums[0],len(nums),0
while i=n-1:
return True
right=max(right,nums[i]+i)
return True if right>=n-1 else False
'''
is_tra_from_fir=[False]*len(nums)
is_tra_from_fir[0]=True
for i in range(len(nums)):
#flag=False
for j in range(1,nums[i]+1):
if i+j
\
小变量问题,不应使用随机数字的sorted,边遍历边移动数字
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-colors
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def sortColors(self, nums: List[int]) -> None:
#O(N)>Sort O(N^2)
n,ptr=len(nums),0
for i in range(n):
if nums[i]==0:
nums[i],nums[ptr]=nums[ptr],nums[i]
ptr+=1
for i in range(ptr,n):
if nums[i]==1:
nums[i],nums[ptr]=nums[ptr],nums[i]
ptr+=1
1976. 到达目的地的方案数
你在一个城市里,城市由 n 个路口组成,路口编号为 0 到 n - 1 ,某些路口之间有 双向 道路。输入保证你可以从任意路口出发到达其他任意路口,且任意两个路口之间最多有一条路。
给你一个整数 n 和二维整数数组 roads ,其中 roads[i] = [ui, vi, timei] 表示在路口 ui 和 vi 之间有一条需要花费 timei 时间才能通过的道路。你想知道花费 最少时间 从路口 0 出发到达路口 n - 1 的方案数。
请返回花费 最少时间 到达目的地的 路径数目 。由于答案可能很大,将结果对 109 + 7 取余 后返回。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-ways-to-arrive-at-destination
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
一模一样的算法,python加cache与不加cache的区别
关于python中cache的用法
涉及最短路,最短路之后的路径选择dfs,路径选择条数的动态规划,记忆化搜索cache
class Solution:
def countPaths(self, n: int, roads: List[List[int]]) -> int:
mod = 10**9 + 7
dist = [[float("inf")] * n for _ in range(n)]
for i in range(n):
dist[i][i] = 0
for x, y, z in roads:
dist[x][y] = dist[y][x] = z
# Floyd 算法求解最短路
# 完成后,dist[0][i] 即为正文部分的 dist[i]
# for k in range(n):
# for i in range(n):
# for j in range(n):
# dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
# Dijkstra 算法求解最短路
# 完成后,dist[0][i] 即为正文部分的 dist[i]
seen = set()
for _ in range(n - 1):
u = None
for i in range(n):
if i not in seen and (not u or dist[0][i] < dist[0][u]):
u = i
seen.add(u)
for i in range(n):
dist[0][i] = min(dist[0][i], dist[0][u] + dist[u][i])
# 构造图 G
g = defaultdict(list)
for x, y, z in roads:
if dist[0][y] - dist[0][x] == z:
g[x].append(y)
elif dist[0][x] - dist[0][y] == z:
g[y].append(x)
@cache
#有点类似记忆化搜索了
def dfs(u: int) -> int:
if u == n - 1:
return 1
ret = 0
for v in g[u]:
ret += dfs(v)
return ret % mod
ans = dfs(0)
dfs.cache_clear()
return ans
python中defaultdict用法详解
刷题降低时间复杂度
1.数学
例如和相等的最大连乘、1014. 最佳观光组合
2.数位运算,快速幂,模式匹配,批量加和(状态转移),排列组合,全排列。。。
全排列
存在一个未知数组需要你进行还原,给你一个整数 n 表示该数组的长度。另给你一个数组 sums ,由未知数组中全部 2n 个 子集的和 组成(子集中的元素没有特定的顺序)。
返回一个长度为 n 的数组 ans 表示还原得到的未知数组。如果存在 多种 答案,只需返回其中 任意一个 。
如果可以由数组 arr 删除部分元素(也可能不删除或全删除)得到数组 sub ,那么数组 sub 就是数组 arr 的一个 子集 。sub 的元素之和就是 arr 的一个 子集的和 。一个空数组的元素之和为 0 。
注意:生成的测试用例将保证至少存在一个正确答案。
示例 1:
输入:n = 3, sums = [-3,-2,-1,0,0,1,2,3]
输出:[1,2,-3]
解释:[1,2,-3] 能够满足给出的子集的和:
[]:和是 0
[1]:和是 1
[2]:和是 2
[1,2]:和是 3
[-3]:和是 -3
[1,-3]:和是 -2
[2,-3]:和是 -1
[1,2,-3]:和是 0
注意,[1,2,-3] 的任何排列和 [-1,-2,3] 的任何排列都会被视作正确答案。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-array-given-subset-sums
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def recoverArray(self, n: int, sums: List[int]) -> List[int]:
BIAS = -min(sums)
st = SortedList()
for x in sums:
st.add(x+BIAS)
st.discard(st[0])
ans = []
ans.append(st[0])
for i in range(1, n):
for msk in range(1<>(i-1)) & 1: # 避免重复删除(保证ans最新的一位被取到)
sm = 0
for j in range(i):
if (msk>>j) & 1:
sm += ans[j]
st.discard(sm)
ans.append(st[0])
for i in range(1<>j) & 1:
sm += ans[j]
if sm == BIAS:
for j in range(n):
if (i>>j) & 1:
ans[j] = -ans[j]
break
return ans
代码顺序较大影响代码高效性
def productExceptSelf(self, nums: List[int]) -> List[int]:
#设计一个O(1)时间复杂度
ans=[1]
L=1
for i in range(1,len(nums)):
L*=nums[i-1]#1
ans.append(L)#2
R=1
for i in range(len(nums)-1,-1,-1):
ans[i]*=R#3
R*=nums[i]#4
return ans
调整了一下#1和#2的顺序,提高了8ms
相同算法书写时的严谨性差,导致多处错误
给你两个版本号 version1 和 version2 ,请你比较它们。
版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。
比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。
返回规则如下:
如果 version1 > version2 返回 1,
如果 version1 < version2 返回 -1,
除此之外返回 0。
示例 1:
输入:version1 = "1.01", version2 = "1.001"
输出:0
解释:忽略前导零,"01" 和 "001" 都表示相同的整数 "1"
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/compare-version-numbers
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def compareVersion(self, version1: str, version2: str) -> int:
l1=[int(i) for i in version1.split('.')]
l2=[int(i) for i in version2.split('.')]
t=0
n,m=len(l1),len(l2)
for i in range(n-1,-1,-1):
if l1[i]==0:
l1.pop()
t+=1#warning
else:
n-=t
t=0
break#error1
for i in range(m-1,-1,-1):
if l2[i]==0:
l2.pop()
t+=1
else:
m-=t
break
ans=0
#print(l1)
#print(l2)
for i in range(min(n,m)):
if l1[i]>l2[i]:
return 1
elif l1[i]m:
return 1
elif n
其中#error1是因为当v1字符串全是0时,t记录的值没有去除。而且如果是0版本,该逻辑会清理全是0的字符串,不太合理
为了上一步不清理到第一位,应重写else的判断,在if中使用n-=1,否则退出,代码更严谨,且不容易出错。
同上一个错误,当code没有耐心时,简单题也会一直错
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-consecutive-sequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我还是有一定实力的嘛
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。
你设计的解决方案必须不修改数组 nums 且只用常量级 O(1) 的额外空间。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-the-duplicate-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二进制编码方式
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
#都是整数
t=1<>i&1:
return i
else:
t|=1<