本篇博客旨在便于作者本人学习记录,方便翻阅。故所引用之处皆已附上链接。能力有限,也望能给诸位带来些许方便。语言:python
。
程序调用自身
用递归实现的算法。递归调用之前「做选择」,在递归调用之后「撤销选择」。
搜索算法:暴力搜索 ⟹ \Longrightarrow ⟹ 剪枝+优化。参考在(☞゚ヮ゚)☞这里
最常见
重叠子问题、最优子结构、状态转移方程:动态规划三要素
01背包:总容量有限,如何装使得背包价值最大。物体:(体积、价值)。
dp[i][capacity] = value
迭代法:队列
def BFS(root):
queue = [root]
while queue:
p = queue.pop(0)
if p.left:
queue.append(p.left)
if p.right:
queue.append(p.right)
迭代法:栈
def DFS(root):
p = root
stack = [p]
while p.left:
p = p.left
stack.append(p) # 左子树入栈
while stack:
# 栈顶元素出栈,将其右节点入栈,并重复将该将右节点的所有左节点入栈
p = stack.pop()
if p.right:
p = p.right
stack.append(p) # 栈顶元素右节点入栈
while p.left: # 并将该节点所有左子树节点入栈
p = p.left
stack.append(p)
return leaf
递归法:重复自身(一般递归效率低)
def DFS(root):
if root is None:
return
# root.val # 前序
DFS(root.left)
# root.val # 中序
DFS(root.right)
# root.val # 后序
每个节点的值大于其左侧子节点的值,小于右节点的值。
bin()
转二进制
0x
:16进制,0b
:2进制
python是无?符号二进制
& |
| |
^ |
~ |
<< |
>> |
---|---|---|---|---|---|
与 | 或 | 异或XOR ‘⊕’ | 取反 | 左移 | 右移 |
1&1为1,其余为0 | 0|0为0,其余为1 | 相同为0相异为1 |
⭐异或运算规律:转自☞这里
1482.制作 m 束花所需的最少天数
981.基于时间的键值存储
找最优值
def binaryFind(data, target):
left, right = min, max (都能取到)
mid = (left + right) // 2
while left <= right:
if is_satisfy(mid,data):
return data[mid]
elif over_satisfy(mid,data):
right = mid - 1
else:
left = mid + 1
return left or right
运用到查找最优值时:第一个不小于目标值的数,或者查找最后一个小于目标值的数。
while left <= right:
最后:left:大于等于目标值的最小值,right:小于等于目标值的最大值。
变形:❓
e.g. 用n位二进制表示n位0-1状态。
取第i位状态:(num >> i) & 1
改第i位状态为1
:num = num | (1 << i)
s
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reversePrint(self, head: ListNode) -> List[int]:
# 从尾到头反过来返回每个节点的值
# 方法1:list存储节点值,最后list翻转
result = []
p = head
while p:
result.append(p.val)
p = p.next
result.reverse() # result[::-1]
return result
# 如果需要逆读取链表
# 方法2:增加额外空间:栈,来存储节点
# 方法3:递归
def visit(p):
if not p:
return []
return visit(p.next)+[p.val]
return visit(head)
栈:先进后出 ⟶ \longrightarrow ⟶队列:先进先出。
负责进:栈1,负责出:栈2。
直接进栈1,直接出栈2,栈2若为空则将栈1 pop压入栈2。
class CQueue:
def __init__(self):
self.stack_in = []
self.stack_out = []
def appendTail(self, value: int) -> None:
# 队列尾部插入整数
self.stack_in.append(value)
def deleteHead(self) -> int:
# 队列头部删除整数,没有元素则返回 -1
if not self.stack_in and not self.stack_out:
return -1
if not self.stack_out:
while self.stack_in:
self.stack_out.append(self.stack_in.pop())
return self.stack_out.pop()
动态规划DP:F[n]=F[n-1]+F[n-2]
节约空间只使用a、b来存储数据:b = b+a; a = b-a
class Solution:
def fib(self, n: int) -> int:
# DP
if n==0:
return 0
MOD = 10**9+7
a = 0
b = 1
for _ in range(1,n):
a,b = b,(a+b) % MOD
return b
class Solution:
def numWays(self, n: int) -> int:
MOD = 10**9 + 7
a = 0
b = 1
for _ in range(n):
a,b = b,(a+b) % MOD
return b
class Solution:
def minArray(self, numbers: List[int]) -> int:
left = 0
right = len(numbers)-1
i = 1
# Ns+1 < Nright < Nleft < Ns
while left<right:
mid = (left+right)//2
if numbers[mid] > numbers[right]:
left = mid+1
elif numbers[mid] < numbers[right]:
right = mid
else:
right -= 1
return numbers[left]
count
计算1个数bin(n).count('1')
while n:
count += n & 1
n = n>>1
while n:
count += 1
n &= n-1
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteNode(self, head: ListNode, val: int) -> ListNode:
# 单向链表
p = head
if head.val == val: return head.next
while p and p.val != val:
bef,p = p,p.next # 双指针
if p:
bef.next = p.next
return head
首尾双指针
class Solution:
def exchange(self, nums: List[int]) -> List[int]:
# 快速排序,前奇后偶
i, j = 0, len(nums)-1
while i < j:
while i < j and nums[i]%2 != 0:
i += 1
while i < j and nums[j]%2 == 0:
j -= 1
if i < j:
nums[i],nums[j] = nums[j],nums[i]
return nums
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
# 输出该链表中倒数第k个节点
# 解法1:list 存储空间增加
# 解法2:先求链表长度n,再走n-k步
save = []
p = head
while p:
save.append(p)
p = p.next
return save[-k]
三指针
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
p2 = head
if head:
p3 = head.next
head.next = None
while p3:
p1,p2,p3 = p2,p3,p3.next
p2.next = p1
return p2
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
# 从外向里以顺时针的顺序依次打印出矩阵每一个数字
m = len(matrix)
if m:
n = len(matrix[0])
else:
return matrix
result = []
for i in range(min(m//2, n//2)):
result += matrix[i][i:n-i] # →
for j in range(i+1,m-i): # ↓
result.append(matrix[j][n-i-1])
result += matrix[m-i-1][n-i-2:i:-1] # ←
for j in range(m-i-1,i,-1): # ↑
result.append(matrix[j][i])
# 剩余情况
i = min(m//2, n//2)
if m - 2*i != 0:
result += matrix[i][i:n-i]
if n - 2*i != 0:
for j in range(i+1,m-i):
result.append(matrix[j][n-i-1])
return result
添加辅助栈(非严格单调递减):
class MinStack:
def __init__(self):
"""
initialize your data structure here.
"""
self.data = []
self.assist = [] # 非严格递减
def push(self, x: int) -> None:
self.data.append(x)
if not self.assist or self.assist[-1]>=x:
self.assist.append(x)
def pop(self) -> None:
if self.data:
if self.assist[-1]==self.data[-1]:
self.assist.pop()
self.data.pop()
def top(self) -> int:
if self.data:
return self.data[-1]
def min(self) -> int: # O(1)时间
# return min(self.data)
return self.assist[-1]
# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.min()
BFS
哈希字典
class Solution:
def majorityElement(self, nums: List[int]) -> int:
# 找出数组中出现的次数超过数组长度的一半的这个数字
n = len(nums)
count = {}
for item in nums:
count[item] = count.get(item,0)+1
if count[item] > n//2:
return item
暴力:两层for循环
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
# 方法1:暴力(超时)
n = len(nums)
F = [0]*(n+1)
F[0] = nums[0]
for i in range(1, n):
F[i] = F[i-1] + nums[i]
result = -101
for i in range(0,n):
for j in range(-1,i):
result = max(result, F[i]-F[j])
return result
DP: (位置i)
dp[i-1]<=0
,则dp[i]=nums[i]
;dp[i-1]>0
,则dp[i] = dp[i-1]+nums[i]
。dp
最大值。class Solution:
def maxSubArray(self, nums: List[int]) -> int:
# 时间复杂度为O(n)
# 方法2:DP
n = len(nums)
dp = [0]*n
dp[0] = nums[0]
for i in range(1,n):
dp[i] = max(dp[i-1]+nums[i], nums[i])
# 若当前数加上当前累积子数组和 < 当前数,则从当前数为子数组开端
return max(dp) # 返回所有子数组和最大值
class Solution:
def firstUniqChar(self, s: str) -> str:
# 第一个只出现一次的字符
save = {}
for item in s:
save[item] = save.get(item, 0) + 1
for key, val in save.items():
if val == 1:
return key
return ' '
俩列表存储俩链表,从尾到头判断相等。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
if not headA or not headB:
return None
list1 = [headA]
p = headA
while p.next:
p = p.next
list1.append(p)
list2 = [headB]
p = headB
while p.next:
p = p.next
list2.append(p)
i = -1
l1,l2 = len(list1),len(list2)
while i>=-min(l1,l2) and list1[i] == list2[i]:
i -= 1
if i == -1:
return None
return list1[i+1]
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums:
return 0
# 方法一:二分查找
n = len(nums)
left, right = 0, n
while left < right:
mid = (left + right) // 2
if nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
else:
break
count = 0
mid = (left + right) // 2
i = mid
while i<n and nums[i] == target:
count += 1
i += 1
i = mid - 1
while i>=0 and nums[i] == target:
count += 1
i -= 1
return count
# 2:顺序查找
i = 0
count = 0
while i<len(nums) and nums[i] <= target:
if nums[i] == target:
count += 1
i += 1
return count
class Solution:
def kthLargest(self, root: TreeNode, k: int) -> int:
# 右根左, count累加计数
stack = []
p = root
while p:
stack.append(p)
p = p.right
count = 0
while stack:
p = stack.pop()
count += 1
if count == k:
return p.val
if p.left:
p = p.left
while p:
stack.append(p)
p = p.right
BFS
class Solution:
def maxDepth(self, root: TreeNode) -> int:
# BFS
if not root:
return 0
stack = [root]
count = 0
while stack:
for i in range(len(stack)):
p = stack.pop(0)
if p.left:
stack.append(p.left)
if p.right:
stack.append(p.right)
count += 1
return count
递归
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
# 某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树
def DFS(root):
if not root:
return 0, True
l, l1 = DFS(root.left)
r, l2 = DFS(root.right)
label = l1 & l2
if not -1<=l-r<=1:
label = False
return max(l,r)+1, label
_, label = DFS(root)
return label
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
# 递增排序的数组
save = dict(zip(nums, [0]*len(nums))) # list转字典,实现O(1)的查找
for i,_ in save.items():
if target-i in save:
return [i,target-i]
class Solution:
def reverseWords(self, s: str) -> str:
i = 0
result = ''
while i < len(s):
temp = ''
while i < len(s) and s[i] != ' ':
temp += s[i]
i += 1
if temp:
result = temp + ' ' + result
i += 1
return result[:-1]
class Solution:
def reverseLeftWords(self, s: str, n: int) -> str:
# return s[n:]+s[:n]
new = list(s)
l = len(s)
for i in range(l-n):
new[i] = s[i+n]
for i in range(n):
new[l-n+i] = s[i]
return ''.join(new)
class Solution:
def isStraight(self, nums: List[int]) -> bool:
# 判断是不是一个顺子, 0 可以看成任意数字
nums.sort()
n = len(nums)
i = 0
count = 0
while i < n and nums[i] == 0:
count += 1
i += 1
while i < n-1:
temp = nums[i+1]-nums[i]-1
if temp == -1:
return False
elif temp != 0:
if count < temp:
return False
count -= temp
i += 1
return True
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
# 逆推,数字个数从2~n
count = 0
for i in range(2, n+1):
count = (count + m) % i
return count
# 直接列表,超时
data = list(range(n))
i = 0
while n > 1:
i = (i + m - 1) % n
n -= 1
del(data[i])
return data[0]
二进制加法(补码)
class Solution:
def add(self, a: int, b: int) -> int:
# 在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号
# 位运算:^当前位, &进位
x = 0xffffffff # 32位1
print(a, b)
a, b = a & x, b & x # 补码,正数补码不变,负数补码取反+1
print(bin(a), b)
while b:
a, b = a ^ b, (a & b) << 1 & x
print(bin(0x7fffffff))
return a if a <= 0x7fffffff else ~(a ^ x) # 31位1,若>31位1 ~(a ^ x)
暴力解法:横向扫描
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
if not strs: # strs空的情况
return ""
temp = strs[0]
# 双重循环
for i in range(1, len(strs)):
temp = temp[:min(len(temp), len(strs[i]))]
for j in range(len(temp)):
if temp[j] != strs[i][j]:
temp = temp[:j]
break
return temp
滑动窗+有序数组(O(1)的查、增、删)
C++中 set/multiset/map,Java中TreeSet、TreeMap,Python中sortedcontainers(转自☞这里 )。
动态规划
t e x t 1 [ : i + 1 ] text1[:i+1] text1[:i+1]和 t e x t 2 [ : j + 1 ] text2[:j+1] text2[:j+1]的最长公共子序列长度为 d p [ i + 1 ] [ j + 1 ] dp[i+1][j+1] dp[i+1][j+1],状态转移方程:
if text1[:i+1]==text2[:j+1]:
dp[i+1][j+1] = dp[i][j]+1
else:
dp[i+1][j+1] = max(dp[i][j+1], dp[i+1][j])
注:面试笔试出现概率高
同:剑指 Offer 48. 最长不含重复字符的子字符串
字符串匹配KMP
前缀表(用来记录匹配失败后该跳回的位置)
子串中当前位置前缀表值=之前(包括当前位置)的字符串,前缀后缀最大重合数。eg. “ababa”->“aba” 3
匹配失败后,子串指向前一位的前缀值的位置。
推荐视频
思路:每次对当前列表中未查找的第一个元素遍历列表,若有与该元素相同的元素存在则删除这俩元素,若无相同则保留元素。
python
class Solution(object):
def singleNumbers(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
i = 0
while len(nums) > i:
flag = 0
for j in range(i+1, len(nums)):
if nums[i] == nums[j]:
del nums[j]
del nums[i]
flag = 1
break
if flag == 0:
i += 1
print(i)
return nums
存在的问题:执行用时过长,时间复杂度没有达到O(n)
官方使用的是分组异或
异或 ^
操作:
e.g. 若找出数组中唯一一个只出现一次的数,则只需对所有元素进行一次异或操作:
a1 ^ a2 ^ a3 ^ a4 ^ a5… ^ an
两两相同的元素异或之后得0,只剩下出现一次那个数
现在题目是找出两个只出现一次的数,分组异或方法❓
如何分组?先到这里。
思路转载自这里
将十进制数转换成32位二进制数,各个位数十进制相加,结果不能被3整除的位置为1,能整除为0,得到只出现一次的元素的二进制数。
x二进制数的第i位:(x >> i) & 1
,>>
右移运算符,&
按位与运算符。
相加后若当前位不为3的倍数,则1<左移i位做
|
或运算。python没有符号位,i==31最高位时,为-2^31。
二进制表示出现三次(00,01,10)。
拓展:
思路:
class Solution:
def leastBricks(self, wall: List[List[int]]) -> int:
slice_0 = [i[0] for i in wall] # 0列切片
i = min(slice_0)
min_cross = len(wall)
while i !=wall[0][0] or len(wall[0])!=1: # i==wall[0][0] and len(wall)==1结束
count = 0 # 第i列需要穿过的砖块数量
for j in range(len(wall)): # 计算j行
if wall[j][0]>i:
count+=1
elif wall[j][0]==i:
# count 不变
# 合并 位置0和1
wall[j][0] += wall[j][1]
wall[j].pop(1)
# 更新切片
slice_0[j] = wall[j][0]
elif wall[j][0]<i:
count+=1
# 合并
wall[j][0] += wall[j][1]
wall[j].pop(1)
# 更新切片
slice_0[j] = wall[j][0]
i = min(slice_0)
min_cross = min(min_cross, count)
return min_cross
题解提供思路:哈希❓
x
数字部分反转后的结果。 − 2 31 ≤ x ≤ 2 31 − 1 -2^{31} \le x \le 2^{31} - 1 −231≤x≤231−1class Solution:
def reverse(self, x: int) -> int:
# 数字反转
# 32位有符号整数: -2^31 <= x <= 2^31 - 1
border = 2**(31)
if x<0:
multi_base = -1
x = -x
else:
multi_base = 1
count = 0
for i in str(x):
count += multi_base*int(i)
multi_base *= 10
if count > border-1 or count< -border: # 溢出判断
count = 0
return count
若不使用str(x)
和 int(i)
强制转换,数学方式:
c o u n t = c o u n t ∗ 10 + x % 10 x = x / / 10 count = count*10 + x\%10\\ x = x//10 count=count∗10+x%10x=x//10
由于题目里面说明:假设环境不允许存储 64 位整数(有符号或无符号)
所以将溢出判断加入到while
循环里:
while x!=0:
if count > border-1 or count< -border: # 溢出判断
return 0 # return count = 0
count = count*10 + x%10
x = x//10
count *= multi_base
动态规划,参考☞这里
class Solution:
def minCost(self, houses: List[int], cost: List[List[int]], m: int, n: int, target: int) -> int:
# f[i][j][k] 第i间房子,颜色j,分区数k
INF = float("inf")
# 初始化
f = [[[0]*(target+1) for j in range(n)] for i in range(m+1)]
# i =0 k!=0时INF
for j in range(n):
for k in range(target+1):
if k != 0:
f[0][j][k] = INF
for i in range(1,m+1):
if houses[i-1]==0: # 未上色
# f[i][j][k] = min(f[i−1][j][k],f[i−1][p][k−1])+cost[i][j]
for j in range(n):
f[i][j][0] = INF ##
for k in range(1,target+1):
f[i][j][k] = f[i-1][j][k]
for p in range(n):
if p != j:
f[i][j][k] = min(f[i][j][k], f[i-1][p][k-1])
f[i][j][k] += cost[i-1][j]
else: # 已上色
# f[i][houses[i]][k] = min(f[i-1][j][k], f[i-1][p][k-1])
for j in range(n):
f[i][j][0] = INF
for k in range(1,target+1):
if j+1==houses[i-1]:
f[i][j][k] = f[i-1][j][k]
for p in range(n):
if p!=j:
f[i][j][k] = min(f[i][j][k], f[i-1][p][k-1])
else:
f[i][j][k] = INF
# m屋子都涂了且有target个街区的数据里面找最小值
min_data = f[m][0][target]
for p in range(1,n):
min_data = min(min_data, f[m][p][target])
if min_data==INF:
return -1
else:
return min_data
i,j,k之间从0开始还是从1开始debug好久。叹气~
最初 i = 0 → f [ 0 ] [ ] [ k ! = 0 ] = I N F , f [ 0 ] [ ] [ 0 ] = 0 i=0\rightarrow f[0][ ][k!=0] = INF, f[0][ ][0] = 0 i=0→f[0][][k!=0]=INF,f[0][][0]=0;
后面 i ! = 0 → f [ i ] [ ] [ 0 ] = I N F i!=0\rightarrow f[i][ ][0]=INF i!=0→f[i][][0]=INF
动态规划
d p [ i ] = m a x ( d p [ i − 2 ] + n u m s [ i ] , d p [ i − 1 ] ) dp[i] = max(dp[i-2]+nums[i], dp[i-1]) dp[i]=max(dp[i−2]+nums[i],dp[i−1])
class Solution:
def rob(self, nums: List[int]) -> int:
"""
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
dp[i] = max(dp[i-2]+nums[i],dp[i-1])
"""
dp = [0]*len(nums)
dp[0] = nums[0]
if len(nums)>1:
dp[1] = max(nums[:2])
for i in range(2,len(nums)):
dp[i] = max(dp[i-2]+nums[i], dp[i-1])
return dp[len(nums)-1]
动态规划,相似题198.打家劫舍
d p [ i ] = m a x ( d p [ i − 2 ] + i ∗ b u c k e t [ i ] , d p [ i − 1 ] ) dp[i] = max(dp[i-2]+i*bucket[i], dp[i-1]) dp[i]=max(dp[i−2]+i∗bucket[i],dp[i−1]),bucket[i]为 i i i出现的次数。
class Solution:
def deleteAndEarn(self, nums: List[int]) -> int:
MAX = max(nums)
bucket = [0]*(MAX+1) # 存储每个数字出现的次数
dp = []
for i in nums:
bucket[i]+=1
i = 1
while i<=MAX and bucket[i]==0:
i += 1
dp.append(i*bucket[i]) # dp[0]
if i == MAX:
return dp[0]
i += 1
# dp[1]
if bucket[i]!=0:
dp.append(max(dp[0],i*bucket[i]))
else:
while i<=MAX and bucket[i]==0:
i += 1
dp.append(max(dp[0],dp[0]+i*bucket[i]))
i += 1
while i<=MAX:
dp.append(max(dp.pop(0)+i*bucket[i], dp[0]))
i += 1
return dp[1]
a ⊕ b = c → a ⊕ c = b → b ⊕ c = a a \oplus b=c \rightarrow a\oplus c=b \rightarrow b\oplus c=a a⊕b=c→a⊕c=b→b⊕c=a
所以: a r r [ i + 1 ] = e n c o d e d [ i ] ⊕ a r r [ i ] arr[ i+1 ] = encoded[ i ] \oplus arr[ i ] arr[i+1]=encoded[i]⊕arr[i]
class Solution:
def decode(self, encoded: List[int], first: int) -> List[int]:
a = [first]
for i in range(len(encoded)):
a.append(encoded[i]^a[i])
return a
计算结果的第i位:
nums[j] >> i & 1
做异或^
运算。temp<
for j in range(n):
temp ^= nums[j] >> i & 1
result += temp<
class Solution:
def xorOperation(self, n: int, start: int) -> int:
nums = [0]*n
for i in range(n):
nums[i] = start+2*i
i = 0
result = 0
while nums[n-1]>>i!=0:
temp = 0
for j in range(n):
temp ^= nums[j] >> i & 1
result += temp<<i
i+=1
return result
看了题解,想复杂了,直接异或即可:
class Solution:
def xorOperation(self, n: int, start: int) -> int:
# 直接异或
result = 0
for i in range(n):
result ^= start+2*i
return result
方法二:
数学思维,☞这里。(start)^(start+2)^(start+4)^(start+6)^...^(start+2(n-1))
变为:(s ^ (s+1) ^ (s+2) ^ … ^ (s+n-1)) * 2 + e
其中 s = ⌊ start 2 ⌋ s=\lfloor \frac{\textit{start}}{2} \rfloor s=⌊2start⌋, ∗ 2 *2 ∗2表示左移一位,所以 e e e 表示运算结果的最低位。
背包问题?图论? ⟹ \Longrightarrow ⟹工作量要分的平均
来自☞这里。
状态压缩:用n位二进制整数表示n个工作的分配情况(实际只需要一个变量)。
状态转移方程: f [ i ] [ j ] = m i n j ′ ∈ j { m a x ( f [ i − 1 ] [ C j j ′ ] , s u m [ j ′ ] ) } f[i][j] = min_{j' \in j}\{max(f[i-1][Cjj'], sum[j'])\} f[i][j]=minj′∈j{max(f[i−1][Cjj′],sum[j′])}。 f [ i ] [ j ] f[i][j] f[i][j]:给前 i i i个人分配工作,分配情况为 j j j时完成所有工作的最短时间。
题解python直译竟然还超时了,桑心
import math
class Solution:
def minimumTimeRequired(self, jobs: List[int], k: int) -> int:
# 题解思路:动态规划+状态压缩
MAX = float('inf')
n = len(jobs)
sums = [0]*(1 << n)
dp = [[0]*(1 << n) for i in range(k)]
for i in range(1,1<<n):
x = int(math.log(i&(-i),2)) # 2^x = i&(-i) 二进制尾0个数
y = i-(1<<x)
sums[i] = sums[y] + jobs[x]
dp[0] = sums
for i in range(1,k):
for j in range(1<<n):
minn = MAX
x = j
while x!=0:
minn = min(minn, max(dp[i-1][j-x],sums[x]))
x = (x-1)&j
dp[i][j] = minn
return dp[k-1][(1<<n)-1]
二分法:需要等待的最少天数 m i n ( b l o o m D a y ) < d a y < m a x ( b l o o m D a y ) min(bloomDay)
思路来自(☞゚ヮ゚)☞这里,讲解清晰易懂。
class Solution:
def minDays(self, bloomDay: List[int], m: int, k: int) -> int:
# m束花,每束使用相邻k朵花,day>bloomDay[i]才可以摘取
if m*k>len(bloomDay):
return -1
left = min(bloomDay)
right = max(bloomDay) # [left~mid~right)
while left<right:
mid = (left+right)//2
if is_satisfy(bloomDay,m,k,mid):
right = mid # [left,mid)
else:
left = mid+1 # [mid+1,right)
return left
def is_satisfy(bloomDay,m,k,mid):
count_m = 0
i = 0
while i < len(bloomDay):
while i < len(bloomDay) and bloomDay[i] > mid:
i+=1
count_k = 0
while i < len(bloomDay) and bloomDay[i] <= mid: # 连续花
count_k+=1
i+=1
if count_k==k: # 取了k朵重新开始计数
count_m+=1
if count_m == m:
return True
break
return False
二分法区间边界如何界定??
DFS
# 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 leafSimilar(self, root1: TreeNode, root2: TreeNode) -> bool:
# DFS
if DFS(root1)==DFS(root2):
return True
else:
return False
def DFS(root):
leaf = [] # 叶值序列
p = root
stack = [p]
while p.left:
p = p.left
stack.append(p) # 左子树入栈
while stack:
p = stack.pop()
if p.right:
p = p.right
stack.append(p) # 栈顶元素右节点入栈
while p.left: # 并将该节点所有左子树节点入栈
p = p.left
stack.append(p)
elif not p.left:
leaf.append(p.val) # 叶节点
return leaf
e n c o d e d [ 0 ] = p e r m [ 0 ] ⊕ p e r m [ 1 ] encoded[0] = perm[0] \oplus perm[1] encoded[0]=perm[0]⊕perm[1]
e n c o d e d [ 1 ] = p e r m [ 1 ] ⊕ p e r m [ 2 ] encoded[1] = perm[1] \oplus perm[2] encoded[1]=perm[1]⊕perm[2]
e n c o d e d [ 2 ] = p e r m [ 2 ] ⊕ p e r m [ 3 ] encoded[2] = perm[2] \oplus perm[3] encoded[2]=perm[2]⊕perm[3]
e n c o d e d [ 3 ] = p e r m [ 3 ] ⊕ p e r m [ 4 ] encoded[3] = perm[3] \oplus perm[4] encoded[3]=perm[3]⊕perm[4]
… \ldots …
e n c o d e d [ 1 ] ⊕ e n c o d e d [ 3 ] ⊕ e n c o d e d [ 5 ] . . . ⊕ e n c o d e d [ n − 2 ] = p e r m [ 1 ] ⊕ p e r m [ 2 ] … p e r m [ n − 1 ] encoded[1]\oplus encoded[3]\oplus encoded[5]...\oplus encoded[n-2] = perm[1]\oplus perm[2]\ldots perm[n-1] encoded[1]⊕encoded[3]⊕encoded[5]...⊕encoded[n−2]=perm[1]⊕perm[2]…perm[n−1],由此可得 p e r m [ 0 ] perm[0] perm[0].
class Solution:
def decode(self, encoded: List[int]) -> List[int]:
# n 是奇数
n = len(encoded)+1
perm = [0]
for i in range(n//4*4,n+1):
perm[0] ^= i # 1^2^3...^n
for i in range(0,n//2):
perm[0] ^= encoded[2*i+1]
for j in range(n-1):
perm.append(perm[j]^encoded[j])
return perm
F [ i ] = a r r [ 0 ] ⊕ a r r [ 1 ] ⊕ a r r [ 2 ] . . . ⊕ a r r [ i ] F[i]=arr[0]\oplus arr[1]\oplus arr[2] ... \oplus arr[i] F[i]=arr[0]⊕arr[1]⊕arr[2]...⊕arr[i]
X O R [ L , R ] = a r r [ L ] ⊕ a r r [ L + 1 ] . . . ⊕ a r r [ R ] = F [ L − 1 ] ⊕ F [ R ] XOR[L,R] = arr[L]\oplus arr[L+1] ... \oplus arr[R]=F[L-1]\oplus F[R] XOR[L,R]=arr[L]⊕arr[L+1]...⊕arr[R]=F[L−1]⊕F[R]
import numpy as np
class Solution:
def xorQueries(self, arr: List[int], queries: List[List[int]]) -> List[int]:
# 查询数组queries[i] = [Li, Ri]
result = []
n = len(arr)
F = [0]*(n+1) # 只需要一维
F[0] = arr[0]
for j in range(1,n):
F[j] = F[j-1]^arr[j]
for L,R in queries:
result.append(F[L-1]^F[R])
return result
动态规划
d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j + 1 ] dp[i][j]=dp[i−1][j−1]+dp[i−1][j]+dp[i−1][j+1] dp[i][j]=dp[i−1][j−1]+dp[i−1][j]+dp[i−1][j+1]
s t e p s = i , p r e s e n t = j steps = i, present = j steps=i,present=j
i > j i>j i>j不考虑为0, i = j i=j i=j为1。
import numpy as np
class Solution:
def numWays(self, steps: int, arrLen: int) -> int:
# 恰好执行 steps 次操作以后,指针仍然指向索引 0 处的方案数
MODULO = 10**9 + 7
MIN = min(arrLen,steps) # 不加这句空间会爆
dp = [[0]*MIN for _ in range(steps)]
dp[0][0] = 1
for i in range(1,steps):
for j in range(0,MIN):
dp[i][j]=dp[i-1][j] % MODULO # 取模,不然位数会超(溢出)
if j>=1:
dp[i][j]+=dp[i-1][j-1] % MODULO
if j<MIN-1:
dp[i][j]+=dp[i-1][j+1] % MODULO
return (dp[steps-1][0]+dp[steps-1][1]) % MODULO
没啥技术含量的代码:
class Solution:
def intToRoman(self, num: int) -> str:
result = ""
Roman_dict = {'M':1000,'CM':900,'D':500,'CD':400,'C':100,'XC':90,'L':50,'XL':40,'X':10,'IX':9,'V':5,'IV':4,'I':1}
for ro,data in Roman_dict.items():
result += ro * (num//data)
num %= data
return result
逆:12.整数转罗马数字
没啥技术含量代码:(思路:该数比后一数小,-该数,否则 +该数得结果)
class Solution:
def romanToInt(self, s: str) -> int:
# 罗马数字,将其转换成整数
roman_dict = {'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}
# IV,IX,XL,XC,CD,CM
result = 0
temp = int(roman_dict[s[0]])
for i in range(1,len(s)):
data = int(roman_dict[s[i]])
if temp < data:
result -= temp
else:
result += temp
temp = data
result += temp
return result
children : list[26], isEnd : boolean
.注:ord(letter) - 97
ASCII转 int,或者 ord(ch) - ord("a")
.
class Trie:
# 前缀树:用于高效地存储和检索字符串数据集中的键
def __init__(self):
"""
Initialize your data structure here.
"""
self.children = [None]*26 # 26位小写字母
self.isEnd = False # 判断当前是否为字符串结尾
def insert(self, word: str) -> None:
"""
Inserts a word into the trie.
"""
children = self.children
for letter in word:
p = children[ord(letter)-97]
if p:
children = p.children
else:
p = Trie()
children[ord(letter)-97] = p
children = p.children
p.isEnd = True
def search(self, word: str) -> bool:
"""
Returns if the word is in the trie.
"""
children = self.children
for letter in word:
p = children[ord(letter)-97]
if p:
children = p.children
else:
return False
if p.isEnd:
return True
else:
return False
def startsWith(self, prefix: str) -> bool:
"""
Returns if there is any word in the trie that starts with the given prefix.
"""
children = self.children
for letter in prefix:
p = children[ord(letter)-97]
if p:
children = p.children
else:
return False
return True
# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)
class Trie:
# 前缀树:用于高效地存储和检索字符串数据集中的键
def __init__(self):
"""
Initialize your data structure here.
"""
self.children = [None]*2
def insert(self, word: str) -> None:
"""
Inserts a word into the trie.
"""
p = self
i = 30
while i>=0:
num = (word >> i) & 1 # 取数
if p.children[num]:
p = p.children[num]
else:
new = Trie()
p.children[num] = new
p = new
i-=1
def check(self, word: str): # 计算树中与word异或结果最大值
p = self
i = 30
x = 0
while i>=0:
num = (word >> i) & 1 # 取数
# num:0,尽量取1;num:1,尽量取0
if num == 0:
if p.children[1]:
p = p.children[1]
x = x*2+1 # 异或结果
else:
p = p.children[0]
x = x*2
else:
if p.children[0]:
p = p.children[0]
x = x*2+1
else:
p = p.children[1]
x = x*2
i-=1
return x
class Solution:
def findMaximumXOR(self, nums: List[int]) -> int:
# nums[i] XOR nums[j] 的最大运算结果
# 最大:相异位数越多
# O(n)时间
# 创建树
root = Trie()
x = 0
for i in range(len(nums)-1):
root.insert(nums[i])
x = max(x, root.check(nums[i+1]))
return x
字典树,参考题解v(☞゚ヮ゚)☞这里
a i a_i ai枚举nums,并通过字典树快速找到能使 a i ⊕ a j a_i\oplus a_j ai⊕aj最大的 a j a_j aj。
class Trie:
# 前缀树:用于高效地存储和检索字符串数据集中的键
def __init__(self):
"""
Initialize your data structure here.
"""
self.children = [None]*2
def insert(self, word: str) -> None:
"""
Inserts a word into the trie.
"""
p = self
i = 30
while i>=0:
num = (word >> i) & 1 # 取数
if p.children[num]:
p = p.children[num]
else:
new = Trie()
p.children[num] = new
p = new
i-=1
def check(self, word: str): # 计算树中与word异或结果最大值
p = self
i = 30
x = 0
while i>=0:
num = (word >> i) & 1 # 取数
# num:0,尽量取1;num:1,尽量取0
if num == 0:
if p.children[1]:
p = p.children[1]
x = x*2+1 # 异或结果
else:
p = p.children[0]
x = x*2
else:
if p.children[0]:
p = p.children[0]
x = x*2+1
else:
p = p.children[1]
x = x*2
i-=1
return x
class Solution:
def findMaximumXOR(self, nums: List[int]) -> int:
# nums[i] XOR nums[j] 的最大运算结果
# O(n)时间
root = Trie()
x = 0
for i in range(len(nums)-1):
root.insert(nums[i])
x = max(x, root.check(nums[i+1]))
return x
广度优先变形:层次遍历(每次根据当前队列长度,将节点遍历一遍),好处:不需额外添加深度int.
# 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 isCousins(self, root: TreeNode, x: int, y: int) -> bool:
# 层次遍历
stack = [root] # 队列
while stack:
parent = []
for _ in range(len(stack)): # 当前层节点个数
p = stack.pop(0)
if p.left:
if p.left.val != x and p.left.val != y:
stack.append(p.left)
else:
parent.append(p.val)
if p.right:
if p.right.val != x and p.right.val != y:
stack.append(p.right)
else:
parent.append(p.val)
if parent: # 有俩父且不相等则为True
if len(parent) == 2 and parent[0]!=parent[1]:
return True
else:
return False
return False
求a、b值见1310.子数组异或查询
a = F [ j − 1 ] ⊕ F [ i − 1 ] b = F [ k ] ⊕ F [ j − 1 ] a = b ⇒ F [ i − 1 ] = F [ k ] 只 需 确 定 i , k ; j ∈ ( i , k ] . a = F[j-1]\oplus F[i-1]\\ b = F[k]\oplus F[j-1]\\ a=b\Rightarrow F[i-1]=F[k] \\ 只需确定i,k; j \in (i,k]. a=F[j−1]⊕F[i−1]b=F[k]⊕F[j−1]a=b⇒F[i−1]=F[k]只需确定i,k;j∈(i,k].
class Solution:
def countTriplets(self, arr: List[int]) -> int:
n = len(arr)
F = [0]*(n+1) # F[n]是为了防止找F[0]时溢出F[-1]=0
F[0] = arr[0]
for i in range(1,n):
F[i] = F[i-1]^arr[i]
count = 0
for i in range(n-1):
for k in range(i+1,n):
# a = F[j-1]^F[i-1]
# b = F[k]^F[j-1]
# F[i-1]?=F[k] 只需限定i,k; j \in (i,k].
if F[i-1]==F[k]:
count += k-i
return count
哈希字典+排序
class Solution:
def topKFrequent(self, words: List[str], k: int) -> List[str]:
# 返回前 k 个出现次数最多的单词
# O(n log k) 时间复杂度和 O(n) 空间复杂度
count = {} # (哈希表)字典统计出现次数
for i in words:
count[i] = count.get(i,0) + 1
# 排序
result = []
for key, val in count.items():
result.append((key, val))
for i in range(k):
temp = []
for j in range(i+1,len(result)):
if result[i][1] < result[j][1] or (result[i][1] == result[j][1] and result[i][0]>result[j][0]):
temp = result[i]
result[i] = result[j]
result[j] = temp
return [result[i][0] for i in range(k)]
动态规划,类似:1143.最长公共子序列
F [ i ] [ j ] F[i][j] F[i][j] 为 n u m s 1 [ : i ] nums1[:i] nums1[:i] 和 n u m s 2 [ : j ] nums2[:j] nums2[:j] 的最大不相交线个数。
class Solution:
def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int:
# 求最大连线数
l1 = len(nums1)
l2 = len(nums2)
F = [[0]*l2 for _ in range(l1)]
# 边界值
F[0][0] = 1 if nums1[0] == nums2[0] else 0
for i in range(1,l1):
if nums1[i] == nums2[0]:
F[i][0] = 1
else:
F[i][0] = F[i-1][0]
for j in range(1,l2):
if nums1[0] == nums2[j]:
F[0][j] = 1
else:
F[0][j] = F[0][j-1]
# 动态转移
for i in range(1,l1):
for j in range(1,l2):
# F[i][j] = max(F[i-1][j],F[i][j-1])
# if nums1[i] == nums2[j]:
# F[i][j] = max(F[i-1][j-1]+1,F[i][j])
# 上述改进:
if nums1[i] == nums2[j]:
F[i][j] = F[i-1][j-1]+1
else:
F[i][j] = max(F[i-1][j],F[i][j-1])
return F[l1-1][l2-1]
字典树: 208.实现Trie(前缀树)
给nums和queries排序,将nums中小于m的数先插入字典树中,再计算字典树中最大异或值。
import numpy as np
import math
class Trie():
def __init__(self):
self.left = None # 0
self.right = None # 1
class Solution:
def maximizeXor(self, nums: List[int], queries: List[List[int]]) -> List[int]:
n = len(nums)
a = len(queries)
# 排序
nums.sort()
index = (np.argsort([m for _,m in queries]))
answer = [0]*a
head = Trie()
l = int(math.log(nums[n-1],2)) # 树高度
def insert(num):
# 插入
i = l
p = head
while i >= 0:
t = num >> i & 1
i -= 1
if t == 0:
if not p.left:
new = Trie()
p.left = new
p = p.left
if t == 1:
if not p.right:
new = Trie()
p.right = new
p = p.right
def search(x):
# 计算最大异或值
# max(nums[j] XOR x), nums[j]
i = l
p = head
result = x >> (i+1)
while i >= 0:
t_x = x>>i & 1
i -= 1
if t_x == 0:
if p.right:
p = p.right
result = result*2+1
else:
p = p.left
result *= 2
else:
if p.left:
p = p.left
result = result*2+1
else:
p = p.right
result *= 2
return result
i = 0
for item in index:
# x,m: queries
x, m = queries[item]
while i<n and nums[i] <= m:
insert(nums[i])
i += 1
if i == 0:
answer[item] = -1 # 没有更小值,为-1
else:
answer[item] = search(x)
return answer
递归
待改进: 存储重复结果,后续直接调用(dp)
class Solution:
def partition(self, s: str) -> List[List[str]]:
# 所有可能的回文串分割方案
# length <= 16
# 递归
# F = {} DP没用上
n = len(s)
def is_pstring(s): # 判断是否为回文串
if s == s[::-1]:
return True
else:
return False
def sub_partition(s, n, result): # 结果不断合并并传递到最底层直接返回
if n == 1:
return [result+[s]]
if n == 0:
return [result]
temp = []
for i in range(1, n+1):
if is_pstring(s[:i]): # 提前结束, 判读不成立则不继续进行
t = sub_partition(s[i:],n-i,result+[s[:i]])
temp += t
return temp
result = sub_partition(s,n,[])
return result
DP:最少打印次数 F [ i , j ] F[i,j] F[i,j],起始位置 i i i,终点位置 j j j。
F[i, j] = F[i+1,j-1]+1 if a[i]==a[j] else min(F[i,k]+F[k+1,j]) i<=k
F F F初始化为 1 1 1,顺序计算 [ F [ j , j + i ] j = 0 n − i ] i = 0 n [F[j,j+i]_{j=0}^{n-i}]_{i=0}^{n} [F[j,j+i]j=0n−i]i=0n.
class Solution:
def strangePrinter(self, s: str) -> int:
# 最少打印次数
n = len(s)
MAX = float('inf')
F = [[1]*n for _ in range(n)]
for i in range(0,n):
for j in range(0, n-i):
# F(j, j+i)
if s[j] == s[j+i]:
F[j][j+i] = F[j][j+i-1]
else:
temp = MAX
for k in range(j,j+i):
temp = min(temp,F[j][k]+F[k+1][j+i])
F[j][j+i] = temp
return F[0][n-1]
栈
遇右括号时将栈内遇左括号前元素都取出并反转去掉括号放回栈内。
??反转操作有重复,简化?
class Solution:
def reverseParentheses(self, s: str) -> str:
# 逐层反转每对匹配括号中的字符串
# 栈
stack = []
for i in range(len(s)):
if s[i] == ')':
temp = []
while stack[-1] !='(':
temp.append(stack.pop())
stack.pop()
while temp:
stack.append(temp.pop(0))
else:
stack.append(s[i])
return ''.join(stack)
汉明距离:bin(a^b).count('1')
,暴力穷举会超时。
按位统计所有数。
import numpy as np
class Solution:
def totalHammingDistance(self, nums: List[int]) -> int:
# 数组中任意两个数之间汉明距离的总和
# def HammingDistance(a,b): # 计算汉明距离
# return bin(a^b).count('1')
# # 暴力穷举结果时间超限O(n^2)
# count = 0
# for i in range(len(nums)):
# for j in range(i+1,len(nums)):
# count += HammingDistance(nums[i], nums[j])
# return count
n = len(nums)
count = 0
for i in range(30):
temp = 0
for j in range(n):
temp += nums[j]>>i & 1
count += temp*(n-temp) # 统计所有数每一位中,1的个数*0的个数 = 该位汉明距离
return count
math.log(n,2)
,在n很大时会有很小的余数不能使用。n&(n-1) == 0
, e . g . 1000 & 0111 = = 0 e.g.1000 \& 0111 == 0 e.g.1000&0111==0class Solution:
def isPowerOfTwo(self, n: int) -> bool:
# 判断是否是 2 的幂次方
# -2^31 <= n <= 2^31 - 1
if n<=0:
return False
return n&(n-1) == 0 # 1000 & 0111 == 0
先判断是否是2的幂次方, n & ( n − 1 ) = = 0 n\&(n-1)==0 n&(n−1)==0。
4的幂次方有偶数个0,1在奇数位上。
题解:
0xaaaaaaaa = 0b10101010101010101010101010101010
,0x:16进制,0b:2进制。class Solution:
def isPowerOfFour(self, n: int) -> bool:
# 4:100; 16:10000; 64:1000,000; 偶数个0
if n&(n-1)==0 and bin(n).count('0')%2==1: # num % 3 == 1
return True
else:
return False
前缀和+哈希表
一维数组 F [ i ] F[i] F[i] 存储 0 0 0 ~ i i i 的和。
class Solution:
def checkSubarraySum(self, nums: List[int], k: int) -> bool:
# 是否有连续子数组元素总和为 k 的倍数
n = len(nums)
F = [0]*n
# 初始化
F[0] = nums[0]
for i in range(1,n):
F[i] = F[i-1]+nums[i]
if F[i]%k==0:
return True
for i in range(1,n):
temp = F[i]
if temp > k:
for j in range(1,i):
temp = F[i]-F[j-1] # i~j:F[i]-F[j-1]
if temp<k:
break
elif temp%k == 0:
return True
# 0*x = 0情况
temp = 0
for i in range(0,n):
if nums[i]==0:
if temp == 1:
return True
temp = 1
else:
temp = 0
return False
前缀和+哈希表,同523.连续的子数组和
遇0减1,遇1加1。相等值位置区间0、1数量相等。
class Solution:
def findMaxLength(self, nums: List[int]) -> int:
# 相同数量的 0 和 1 的最长连续子数组
hashmap = {0:-1}
n = len(nums)
# F = [0]*(n+1)
f = 0
count = 0
F[0] = 1 if nums[0]==1 else -1
for i in range(0, n):
if nums[i] == 0:
# F[i] = F[i-1]-1 # '1'-'0'
f -= 1
else:
# F[i] = F[i-1]+1
f += 1
if F[i] in hashmap:
count = max(count, i-hashmap[f])
else:
hashmap[f] = i
return count
用俩list存储两个链表的各个节点,从尾部节点到首节点判断相等。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
# 两个单链表相交的起始节点
p1,p2 = headA,headB
list1 = []
list2 = []
while p1:
list1.append(p1)
p1 = p1.next
while p2:
list2.append(p2)
p2 = p2.next
if not (list1 and list2 and list1[-1]==list2[-1]):
return None
while list1 and list2 and list1[-1]==list2[-1]:
list1.pop()
list2.pop()
pass
if list1:
return list1[-1].next
else:
return headA
类 背包问题
方法一:
hash_map[x,y] = z
表示 x
个0 y
个1最大子集的大小为 z
.strs
中每个字符串都遍历更新字典。方法二:
dp[i][j][k]
表示在前 i 个字符串中,使用 j 个 0 和 k 个 1 的情况下最多可以得到的字符串数量。# 方法一
import copy
class Solution:
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
# strs 的最大子集的大小,最多有 m 个 0 和 n 个 1
def count(s):
# return 字符串s中 0 和 1 的个数
count = 0
n = len(s)
for i in range(n):
if s[i] == '0':
count += 1
return count, n-count
hash_map = {}
for s in strs:
x,y = count(s)
for key, val in hash_map.copy().items():
(key_0, key_1) = key
k0, k1 = key_0 + x, key_1 + y
if k0 <= m and k1 <= n:
hash_map[k0, k1] = max(hash_map.get((k0, k1),0), 1+val) # 更新字典
if x<=m and y<=n:
hash_map[x, y] = hash_map.get((x, y), 1) # 添加当前值
if hash_map:
return max(hash_map.values())
else:
return 0
list
存储结果:超时import numpy as np
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
# 构造表达式=target个数, 只能+或-
c = nums.count(0)
while 0 in nums:
nums.remove(0)
n = len(nums)
count = np.zeros(2**n)
for i in range(n):
m = 2**i
for j in range(m):
count[j+m] = count[j]-nums[i]
count[j] += nums[i]
return int(np.sum(count == target))*2**c
import numpy as np
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
# 构造表达式=target个数, 只能+或-
# 递归
if len(nums) == 1:
count = 0
if nums[0] == target:
count += 1
if nums[0] == -1*target:
count += 1
return count
return self.findTargetSumWays(nums[1:],target+nums[0]) + self.findTargetSumWays(nums[1:],target-nums[0])
import numpy as np
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
# DP
# dp[i][j], 0~i:nums, target:j
# dp[i][j] = dp[i][j+nums[0]]+dp[i][j-nums[0]]
n = len(nums)
m = 2**n
dp = [{} for _ in range(n)]
dp[0][nums[0]] = dp[0].get(nums[0], 0)+1
dp[0][-nums[0]] = dp[0].get(-nums[0], 0)+1
for i in range(1, n):
for tar,val in dp[i-1].items():
dp[i][tar+nums[i]] = dp[i].get(tar+nums[i], 0) + val
dp[i][tar-nums[i]] = dp[i].get(tar-nums[i], 0) + val
if dp[n-1].get(target):
return dp[n-1][target]
else:
return 0
F[i,j] = k
,员工i
人,利润j
,方案个数k
。>minProfit
个数。# Plan 1
import copy
class Solution:
def profitableSchemes(self, n: int, minProfit: int, group: List[int], profit: List[int]) -> int:
# 至少产生 minProfit 利润的子集,计划数
m = len(group)
F = {(0,0):1}
if group[0]<=n:
F[group[0], profit[0]] = 1
for i in range(1,m):
for (peo,pro), plans in F.copy().items():
if peo+group[i] <= n:
F[peo+group[i], pro+profit[i]] = F.get((peo+group[i], pro+profit[i]), 0) + plans
mod = 10**9 + 7
count = 0
for (_,pro), plans in F.items():
if pro >= minProfit:
count+=plans
return count%mod
DP: F [ i ] = ∑ c o i n F [ i − c o i n ] F[i] = \sum^{coin} F[i-coin] F[i]=∑coinF[i−coin]
注意循环顺序,不要重复计算:
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
# 凑成总金额的硬币组合数
# F[n] = sum(F[n-coins[i]])
F = [0] * (amount+1)
F[0] = 1
for coin in coins:
for i in range(coin,amount+1):
F[i] += F[i-coin]
return F[amount]
DP :方法类似于:518.零钱兑换
F [ i ] = m i n ( F [ i − n u m ] ) + 1 F[i] = min(F[i-num])+1 F[i]=min(F[i−num])+1, n u m num num为平方数
class Solution:
def numSquares(self, n: int) -> int:
# 最小个数的完全平方数和等于n
# F[i] = min(F[i-num])+1, num某平方数
F = [float('inf')]*(n+1)
F[0] = 0 # 初始值
i = 1
while i*i<=n:
temp = i*i
for j in range(temp,n+1):
F[j] = min(F[j], F[j-temp]+1)
i+=1
return F[n]
DP: d p [ i ] [ j ] dp[i][j] dp[i][j]为从第i堆到第j堆,当前玩家与另一个玩家的石子数量之差的最大值。
d p [ i ] [ j ] = m a x ( p i l e s [ i ] − d p [ i + 1 ] [ j ] , p i l e s [ j ] − d p [ i ] [ j − 1 ] ) dp[i][j] = max(piles[i]-dp[i+1][j], piles[j]-dp[i][j-1]) dp[i][j]=max(piles[i]−dp[i+1][j],piles[j]−dp[i][j−1])
即:当前玩家拿了的一堆 -(另一个玩家 - 未拿当前玩家)= 当前玩家 - 另一个玩家
其实:return True # Alex总是赢
. 若存在li能赢的情况,则Alex总可以选择li的那种拿法也总是能赢。
class Solution:
def stoneGame(self, piles: List[int]) -> bool:
# 总数奇数,偶数堆,石子最多的玩家获胜
n = len(piles)
dp = [[0]*n for _ in range(n)]
for i in range(n):
dp[i][i] = piles[i]
for i in range(1,n):
for j in range(n-i):
dp[j][j+i] = max(piles[j]-dp[j+1][j+i], piles[j+i]-dp[j][j+i-1])
return dp[0][n-1]>0
turnedOn
,求所有可能表示的时间。class Solution:
def readBinaryWatch(self, turnedOn: int) -> List[str]:
# 当前亮着的 LED 的数量, 返回二进制手表可以表示的所有可能时间
def Binary(count, total, current):
"""
统计当前计数list并返回
count:亮灯数
total:总灯数
current:当前计数
"""
if count == 0:
return [current]
elif total == 0:
return []
temp = []
current *= 2
for i in range(total-count+1):
temp += Binary(count-1, total-i-1, (current+1)*2**i)
return temp
save = []
for i in range(max(0,turnedOn-6),min(4,turnedOn+1)):
save1 = Binary(i, 4, 0)
save2 = Binary(turnedOn-i, 6, 0)
for s1 in save1:
for s2 in save2:
if s1<=11 and s2<=59:
save.append('{}:'.format(s1)+"%02d" % s2)
return save
result
列表去重:list(set(result))
(注:set()
无序不重复元素集)class Solution:
def permutation(self, s: str) -> List[str]:
# 打印出该字符串中字符的所有排列
if len(s)==1: # 终止条件
return [s]
result = []
for i in range(len(s)):
# 每次固定列表中一个位(s[i])为首位, 递归得到剩下结果 t
result += [s[i]+t for t in self.permutation(s[0:i]+s[i+1:])]
return list(set(result)) # 去重
算所有两点间的直线。
from fractions import Fraction
class Solution:
def maxPoints(self, points: List[List[int]]) -> int:
# 该方法有重复,暴力
points.sort() # 排序
n = len(points)
line = [{} for _ in range(n-1)]
for i in range(n):
for j in range(i+1,n):
# i,j两点确定直线 y = kx + b
if points[j][0] == points[i][0]:
k,b = 0, points[j][0]
else:
k = Fraction(points[j][1]-points[i][1], points[j][0]-points[i][0]) # 分数表示
b = points[j][1] - k*points[j][0]
line[i][k,b] = line[i].get((k,b), 1) + 1
temp = 1
for i in range(n-1):
temp = max(list(line[i].values())+[temp])
return temp
class Solution:
def convertToTitle(self, columnNumber: int) -> str:
# 返回正整数在 Excel 表中相对应的列名称
# 1~26:A~Z (每位1~26; 每位权重:26^2, 26, 1)
result = ''
while columnNumber:
# 题解:
# a0 = (columnNumber - 1) % 26 + 1 # 取1~26位
# result = chr(a0 - 1 + ord("A")) + result # 1~26位字符
# columnNumber = (columnNumber - a0) / 26
if columnNumber % 26 == 0: # 单独判断26
result = chr(ord("Z"))+result
columnNumber -= 26
else:
result = chr(ord("@") + columnNumber%26) + result # 1~25:取26余数
columnNumber //= 26 # 整除26
return result
广度优先搜索
其余方法:深度优先搜索,dp
class Solution:
def numWays(self, n: int, relation: List[List[int]], k: int) -> int:
# 方案数: k次从0~(n-1)
save = {}
for r in relation:
save[r[0]] = save.get(r[0], [])
save[r[0]].append(r[1])
present = [0]
for i in range(k):
temp = []
for l in present:
if save.get(l):
temp.extend(save[l])
present = temp
count = 0
for i in present:
if i == n-1:
count += 1
return count
排序+贪心
class Solution:
def maxIceCream(self, costs: List[int], coins: int) -> int:
# 雪糕的最大数量,价格数组 costs 和现金量 coins
# 贪心,选最小
costs.sort()
i = 0
while i<len(costs) and costs[i] <= coins:
coins -= costs[i]
i += 1
return i
非右括号入栈,遇右括号把前面最后一个左括号之后的出现次数都乘括号后的数,去掉左括号。循环到字符串尾部。
栈内元素加入字典,排序,变字符串输出。
class Solution:
def countOfAtoms(self, formula: str) -> str:
result = {}
n = len(formula)
i = 0
stack = []
while i < n:
if formula[i] == '(':
stack.append([formula[i], 1])
i += 1
elif formula[i] == ')':
# num = int(formula[i+1])
num = 0
j = i + 1
while j<n and '0'<=formula[j]<='9': # 数字
num = num*10 + int(formula[j])
j += 1
if not num:
num = 1
i = j
j = -1
while stack[j][0] != '(':
stack[j][1] *= num
j -= 1
stack.pop(j)
else:
if i+1<n and 'a'<=formula[i+1]<='z': # 小写字母
count = 0
j = i + 2
while j<n and '0'<=formula[j]<='9': # 数字
count = count*10 + int(formula[j])
j += 1
if not count:
count = 1
stack.append([formula[i:i+2], count])
i = j
else:
count = 0
j = i + 1
while j<n and '0'<=formula[j]<='9': # 数字
count = count*10 + int(formula[j])
j += 1
if not count:
count = 1
stack.append([formula[i], count])
i = j
while stack:
p = stack.pop()
result[p[0]] = result.get(p[0], 0) + p[1]
temp = ''
for key, val in sorted(result.items()):
temp += key
if val > 1:
temp += str(val)
return temp
暴力会超时
0 < = d e l i c i o u s n e s s [ i ] < = 2 20 0 <= deliciousness[i] <= 2^{20} 0<=deliciousness[i]<=220
哈希表,穷举幂指数,若 2 n − d e l i c i o u s n e s s [ i ] ∈ d e l i c i o u s n e s s [ 0 : n ] 2^n-deliciousness[i] \in deliciousness[0:n] 2n−deliciousness[i]∈deliciousness[0:n],则计数加一。
import math
class Solution:
def countPairs(self, deliciousness: List[int]) -> int:
count = 0
MOD = 10**9 + 7
n = len(deliciousness)
de = {}
maximum = max(deliciousness)
m = int(math.log(2*maximum,2)) + 1
for i in range(n):
temp = 0 if not deliciousness[i] else int(math.log(deliciousness[i], 2))
for j in range(temp, m): # m->22
if 2**j - deliciousness[i] in de:
count += de[2**j - deliciousness[i]]
de[deliciousness[i]] = de.get(deliciousness[i] ,0) + 1
count %= MOD
return count % MOD
方法一:F为按位统计和,超时
class Solution:
def numSubarraysWithSum(self, nums: List[int], goal: int) -> int:
n = len(nums)
F = [0]*(n+1)
F[0] = nums[0]
count = 0
for i in range(1, n):
F[i] = F[i-1] + nums[i]
for i in range(n):
if F[i] >= goal:
j = -1
while F[i]-F[j] > goal:
j += 1
while j < i and F[i]-F[j] == goal:
print(i, j)
j += 1
count += 1
return count
方法二:用哈希表记录每一种前缀和出现的次数,假设我们当前枚举到元素 nums [ j ] \textit{nums}[j] nums[j],我们只需要查询哈希表中元素 sum [ j ] − goal \textit{sum}[j]-\textit{goal} sum[j]−goal 的数量。
来源:这里
方法三:考虑 nums[i] 不是 0 就是 1,记录1的位置
class Solution:
def numSubarraysWithSum(self, nums: List[int], goal: int) -> int:
# 和为 goal 的 非空 子数组(连续)个数, nums[i] 不是 0 就是 1
n = len(nums)
count = 0
# F[i] = j: 第i个1下标为j
# F[0] = -1, F[-1] = n
F = [-1]
for i in range(n):
if nums[i] == 1:
F.append(i)
F.append(n)
if goal == 0: # 目标0单独计算,累加
for i in range(1, len(F)):
# 累加 F[i]-F[i-1]
count += sum(range(1, F[i]-F[i-1]))
return count
for i in range(goal, len(F)-1):
j = i - goal + 1
count += (F[j] - F[j-1]) * (F[i+1] - F[i]) # 两边0个数*
return count
哈希表+二分查找
class TimeMap:
def __init__(self):
"""
Initialize your data structure here.
"""
self.time_map = defaultdict(list)
def set(self, key: str, value: str, timestamp: int) -> None:
# 输入已经是升序排列
self.time_map[key].append([value, timestamp])
def get(self, key: str, timestamp: int) -> str:
# 二分查找,不大于timestamp的最大值
data = self.time_map[key]
left = 0
right = len(data) - 1
while left <= right:
mid = (left + right) // 2
if data[mid][1] == timestamp:
right = mid
break
elif data[mid][1] > timestamp:
right = mid - 1
else:
left = mid + 1
return data[right][0] if right>=0 else ""
共有 h 篇论文分别被引用了至少 h 次, 其余的 N - h 篇论文每篇被引用次数 不超过 h 次
class Solution:
def hIndex(self, citations: List[int]) -> int:
citations.sort(reverse=True) # 排序
n = len(citations)
if not n or citations[0] < 1:
return 0
elif n == 1:
return 1
for i in range(1, n):
if citations[i] <= i:
return i
return n
class Solution:
def hIndex(self, citations: List[int]) -> int:
if not citations: # 空情况
return 0
n = len(citations)
i = 1
while i <= n and citations[n-i] >= i: # 只要第i个位置数>=i才考虑下一位
i += 1
return i-1
思路:参考☞这里
class Solution:
def getSkyline(self, buildings: List[List[int]]) -> List[List[int]]:
heights = [] # (坐标,高度)列表
for left, right, h in buildings:
heights.append([left, -h])
heights.append([right, h])
heights.sort(key=lambda x:(x[0], x[1])) # 坐标递增高度递增
points = []
hightest = [0]
for pos, h in heights:
if h < 0: # 增加
i = 0
n = len(hightest)
while i<n and -h<=hightest[i]:
i += 1
hightest.insert(i, -h)
# 高度增高
if i == 0:
points.append([pos, hightest[0]])
else: # 删除
# 二分查找
left, right = 0, len(hightest)-1
while left <= right:
mid = (left + right) // 2
if hightest[mid] == h:
del(hightest[mid])
break
elif hightest[mid] < h:
right = mid - 1
else:
left = mid + 1
# 高度降低
if hightest[0] < h:
points.append([pos, hightest[0]])
return points
import numpy as np
class Solution:
def minAbsoluteSumDiff(self, nums1: List[int], nums2: List[int]) -> int:
n = len(nums1)
total = [0]*n
count = 0
MOD = 10**9 + 7
for i in range(n):
total[i] = abs(nums1[i]-nums2[i])
index = np.argsort(np.array(total)) # 排序下标从小到大
for i in index[::-1]: #从大到小
if count >= total[i]: # 提前终止条件
break
for j in range(n):
if i != j:
count = max(count, total[i] - abs(nums2[i]-nums1[j])) # 保留最大的数
return (sum(total) - count)% MOD
图的问题
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
n = len(strs)
dic = defaultdict(list)
for word in strs:
p = list(word)
p.sort()
dic[''.join(p)].append(word)
return list(dic.values())
class Solution:
def maxFrequency(self, nums: List[int], k: int) -> int:
# 加一
nums.sort()
n = len(nums)
frequency = 1 # 最大可能频数
operand = 0 # 操作数
# 滑动窗口
for i in range(1, n):
j = i - frequency # 窗口尾部,窗口大小为freauency
operand += (nums[i]-nums[i-1]) * frequency # 当前窗口前端加一位之后的总操作数
if operand > k: # 操作次数多了,不考虑更小窗口情况
operand = operand - nums[i] + nums[j]
else:
frequency += 1
while j > 0:
j -= 1 # 窗口尾部增长
operand += nums[i] - nums[j]
if operand > k:
operand -= nums[i] - nums[j]
break
frequency += 1
return frequency
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
if not head:
return None
save = {None:None} # node : new_node
# 头节点
new_head = Node(head.val)
save[head] = new_head
# 节点生成,next串起来
p = new_head
h = head.next
while h:
q = Node(h.val)
save[h] = q
p.next = q
p = q
h = h.next
p.next = None
# random串起来
p = new_head
while head:
p.random = save[head.random]
head = head.next
p = p.next
return new_head
class Solution:
def kthLargestValue(self, matrix: List[List[int]], k: int) -> int:
# 坐标(a,b) : matrix[i][j]异或得到(0<=i<=a ,0<=j<=b)
m, n = len(matrix), len(matrix[0])
dp = [0]*(n*(m+1))
for i in range(m):
count = 0
for j in range(n):
count ^= matrix[i][j]
dp[i*n+j] = dp[(i-1)*n+j]^count
dp.sort(reverse=True)
return dp[k-1]
非空特殊的二叉树:每个节点的子节点数量只能为 2 或 0,节点的值等于两个子节点中较小的一个。
想法:第一小的节点根节点,若左右节点和根节点值相同则存起来(栈或者队列都行),否则比较最小值。
e.g. 第一小的值1,第二小的值2。
class Solution:
def findSecondMinimumValue(self, root: TreeNode) -> int:
stack = [root]
result = float('inf')
while stack:
p = stack.pop()
if p.left:
if p.left.val == p.val: # 相等点入栈
stack.append(p.left)
else:
result = min(result, p.left.val) # 不等点找最小值
if p.right.val == p.val: # 相等点入栈
stack.append(p.right)
else:
result = min(result, p.right.val) # 不等点找最小值
if result < float('inf'):
return result
else:
return -1
class Solution:
def verticalTraversal(self, root: TreeNode) -> List[List[int]]:
stack = [[root,0,0]]
save = defaultdict(list)
while stack: # 层次遍历
p = stack.pop(0)
save[p[2]].append((p[1],p[0].val))
if p[0].left:
stack.append([p[0].left, p[1]+1, p[2]-1])
if p[0].right:
stack.append([p[0].right, p[1]+1, p[2]+1])
result = []
for key, val in sorted(save.items()): # 按列排序
val.sort(key=lambda x:(x[0], x[1])) # 按行排序,相同行按值排序
result.append([i for _,i in val])
return result
class Solution:
def triangleNumber(self, nums: List[int]) -> int:
# 统计其中可以组成三角形三条边的三元组个数
def binary(left, right, target):
while left <= right:
mid = (left + right)//2
if nums[mid] < target:
left = mid+1
else:
right = mid-1
return right
nums.sort() # 排序
n = len(nums)
count = 0
for i in range(n-2):
for j in range(i+1, n-1):
# 二分查找 从j+1~n-1:nums[i] + nums[j]
count = count + binary(j+1, n-1, nums[i] + nums[j]) - j
return count
参考:here
class Solution:
def shortestPathLength(self, graph: List[List[int]]) -> int:
# 能够访问所有节点的最短路径的长度——>类旅行商(TSP)问题
# BFS
# 状态压缩:用n位二进制数
n = len(graph)
stack = [[i, 1<<i, 0] for i in range(n)] # 队列,存储 (当前点, 已访问节点, 所有访问次数)
state = {(i, 1<<i) for i in range(n)} # 时间复杂度O(1)
while stack:
p = stack.pop(0)
if p[1] == 2 ** n - 1:
return p[2]
for next_point in graph[p[0]]:
temp = p[1] | (1<<next_point)
if (next_point, temp) not in state:
stack.append([next_point, temp, p[2]+1])
state.add((next_point, temp))
return 0
求最长回归子序列长度,子序列可以不连续。
DP:
d p [ i ] [ j ] = d p [ i + 1 ] [ j − 1 ] , if s [ i ] = s [ j ] ; dp[i][j] = dp[i+1][j-1],\text{if }s[i]=s[j]; dp[i][j]=dp[i+1][j−1],if s[i]=s[j];
d p [ i ] [ j ] = m a x ( d p [ i ] [ j − 1 ] , d p [ i + 1 ] [ j ] ) , otherwise . dp[i][j] = max(dp[i][j-1], dp[i+1][j]),\text{otherwise}. dp[i][j]=max(dp[i][j−1],dp[i+1][j]),otherwise.
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
n = len(s)
dp = [[0]*n for _ in range(n)]
for i in range(n):
dp[i][i] = 1
for i in range(1, n):
for j in range(n-i):
x,y = j, i+j
if s[x] == s[y]:
dp[x][y] = dp[x+1][y-1] + 2
else:
dp[x][y] = max(dp[x][y-1], dp[x+1][y])
return dp[0][n-1]
题解:这里
class Solution:
def maxProduct(self, words: List[str]) -> int:
# 不含有公共字母两个单词长度相乘的最大值
words_set = [set(i) for i in words]
n = len(words_set)
flag = 0
for i in range(n):
for j in range(i+1,n):
temp = 0
for k in words_set[i]:
if k in words_set[j]:
temp = 1
break
if temp == 0:
flag = max(flag, len(words[i]*len(words[j])))
return flag
内部函数(函数内函数)能访问函数外函数不能直接修改函数外变量。
若需要修改则需要加关键字nonlocal
,若加关键字global
只能覆盖原内容。
面向对象编程
math.log(n, 2)
: l o g 2 n log^n_2 log2n键盘/文件 → \rightarrow → 读入/读出:
a = int(input()) # input()
b = [int(i) for i in input().split()] # input().split()
import sys
a = sys.stdin.readline() # 读一行
b = a.split() # 空格分割
# 一直输入
import sys
for line in sys.stdin:
b = line.split()
n = int(sys.stdin.readline().strip())
for i in range(n):
line = sys.stdin.readline().strip() # strip()去除首尾空格
values = list(map(int, line.split()))
常用:
None
没有Null
(python)MAX = float('inf')
无穷大from fractions import Fraction
Fraction(4,5)
集合:
set()
无序不重复元素集&
交、|
并、-
差、^
补[[i, 1<, set内部是dict实现的,O(1)
查找时间复杂度
字典:dict = {key: value}
defaultdict(default_factory)
在访问当前不存在字典的key时,自动构建key : value (value类型是default_factory)default_factory
:int
, list
创建:dict(zip('abc', [1, 2, 3]))
操作:
添加:dict[key]=value
,查找:dict[key]
or dict.get(key[, default])
。
取键值对dict.items()
,dict.values()
,dict.keys()
.
字典生成式:{key : value for key, value in my_dictionary.items() if value > 10}
注意:
列表:list:[]
list.append(num)
,表尾取出list.pop()
+
或者 list.extend(list2)
list.sort(reverse=True)
True: 大到小list(range(i,j))
生成 i 到 j 递增列表list(map(int,列表名))
将列表中数据都变成int类型数据类型转换:
ord(ch)
ASCIIlist()
bin()
chr()
''.join(stack)
,''
内是连接符