方法一:设置pre和cur两个指针来改变指向
class Solution:
def ReverseList(self , head: ListNode) -> ListNode:
pre = None
cur = head
while cur:
tmp = cur.next
cur.next = pre
pre = cur
cur = tmp
return pre
头插法:将cur的后一个不断放到前面
class Solution:
def ReverseList(self , head: ListNode) -> ListNode:
dummy = ListNode(-1)
dummy.next = head
cur = head #cur始终指向原始的第一个节点
while cur and cur.next: #下一个还有就将其放到前面
tmp = cur.next
cur.next = tmp.next #当tmp是最后一个节点时,cur.next指向tmp.next即空
tmp.next = dummy.next
dummy.next = tmp
return dummy.next
头插法,写法二:
class Solution:
def ReverseList(self , head: ListNode) -> ListNode:
if not head:
return
dummy = ListNode(-1)
dummy.next = head
cur = head #cur始终指向原始的第一个节点
while cur.next:
tmp = cur.next
cur.next = tmp.next
tmp.next = dummy.next
dummy.next = tmp
return dummy.next
头插法,写法三:比较麻烦,不使用
cur指向的是dummy的下一个
class Solution:
def ReverseList(self , head: ListNode) -> ListNode:
if not head:
return
dummy = ListNode(0)
dummy.next = head
cur = head
nt = cur.next
cur.next = None
while nt:
tmp = nt.next
nt.next = cur
dummy.next = nt
cur = nt
nt = tmp
return dummy.next
方法一:
class Solution:
def reverseBetween(self , head: ListNode, m: int, n: int) -> ListNode:
dummy = ListNode(-1)
dummy.next = head
pre = dummy
for _ in range(m-1):
pre = pre.next
cur = pre.next
for _ in range(n-m):
#不断地改变cur的下一个,将cur的下一个放到pre的前面
#cur和pre都是不变的
tmp = cur.next #要改变cur的指向,先保存一下cur的下一个
cur.next = tmp.next
tmp.next = pre.next #要改变pre的指向,先保存一下pre的下一个(这里顺用tmp来保存正好正确连接)
pre.next = tmp
return dummy.next
方法一:暴力,利用栈
class Solution:
def reverseKGroup(self , head: ListNode, k: int) -> ListNode:
# write code here
dummy = ListNode(-1)
p = dummy
while True:
stack = []
t = 0
tmp = head
while t < k and tmp:
t += 1
stack.append(tmp)
tmp = tmp.next
if len(stack) < k:
p.next = head
break
while stack:
p.next = stack.pop()
p = p.next
p.next = tmp #tmp已经到了下一组的第一个
head = tmp
return dummy.next
方法二:尾插法,区别于前面头插
class Solution:
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
dummy = ListNode(0)
dummy.next = head
pre = tail = dummy
while True:
count = k
while count and tail: #将tail指向当前组的尾部
count -= 1
tail = tail.next
if not tail:
break
head = pre.next # head是当前组的第一个,反转后就是最后一个
while pre.next != tail:
tmp = pre.next
pre.next = tmp.next
tmp.next = tail.next
tail.next = cur
pre = head
tail = head
return dummy.next
class Solution:
def Merge(self , pHead1: ListNode, pHead2: ListNode) -> ListNode:
# write code here
dummy = ListNode(-1)
cur = dummy
p1 = pHead1
p2 = pHead2
while p1 and p2:
if p1.val < p2.val:
cur.next = p1
p1 = p1.next
cur = cur.next
else:
cur.next = p2
p2 = p2.next
cur = cur.next
cur.next = p1 if p1 else p2
return dummy.next
方法一:维持一个res,与各个链表两两归并
class Solution:
def mergeKLists(self , lists: List[ListNode]) -> ListNode:
# write code here
if not lists: return
def mergeTwoLists(head1, head2):
dummy = ListNode(0)
cur = dummy
p1 = head1
p2 = head2
while p1 and p2:
if p1.val <= p2.val:
cur.next = p1
p1 = p1.next
else:
cur.next = p2
p2 = p2.next
cur = cur.next
cur.next = p1 if p1 else p2
return dummy.next
res = lists[0]
for head in lists[1:]:
res = mergeTwoLists(res, head)
return res
方法二:分治递归
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
def mergeTwoLists(head1, head2):
dummy = ListNode(0)
cur = dummy
p1 = head1
p2 = head2
while p1 and p2:
if p1.val <= p2.val:
cur.next = p1
p1 = p1.next
else:
cur.next = p2
p2 = p2.next
cur = cur.next
cur.next = p1 if p1 else p2
return dummy.next
def process(lists, left, right):
if (left == right):
return lists[left]
mid = left + ((right-left) >> 1)
# head1 = process(lists, left, mid)
# head2 = process(lists, mid+1, right)
# return mergeTwoLists(head1, head2)
return mergeTwoLists(process(lists, left, mid), process(lists, mid+1, right))
if not lists: return
return process(lists, 0, len(lists)-1)
方法三: 优先队列
方法一:哈希
class Solution:
def hasCycle(self , head: ListNode) -> bool:
dic = set()
while head:
if head in dic:
return True
dic.add(head)
head = head.next
return False
方法二:快慢指针,在环上,每次移动一次就,快指针和慢指针的距离会减一,最终总会追及
class Solution:
def hasCycle(self , head: ListNode) -> bool:
fast = slow = head
while fast and fast.next: #因为每次移动两步,所以要考虑fast.next才不会漏掉条件
fast = fast.next.next
slow = slow.next
if fast == slow:
return True
return False
方法一:双指针
设入口节点前有a个节点,环中有b个节点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m0nfu8qf-1652544251605)(image-20220409105036009.png)]
class Solution:
def EntryNodeOfLoop(self, pHead):
fast = slow = pHead
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow: #第一次相交的地方fast走的步数是slow的两倍 f = 2s,f = s + nb 有s = nb ,再走a步就到入口节点了
break
if not fast or not fast.next:
return
fast = pHead
while fast != slow:
fast = fast.next
slow = slow.next
return fast
方法一:双指针
class Solution:
def FindKthToTail(self , pHead: ListNode, k: int) -> ListNode:
# write code here
fast = pHead
while k > 0 and fast:
fast = fast.next
k -= 1
if k > 0: #如果链表长度小于k
return
while fast:
fast = fast.next
pHead = pHead.next
return pHead
方法一:双指针
class Solution:
def removeNthFromEnd(self , head: ListNode, n: int) -> ListNode:
# write code here
dummy = ListNode(0)
dummy.next = head
slow = dummy
fast = head
for _ in range(n):
fast = fast.next
while fast:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummy.next
class Solution:
def FindFirstCommonNode(self , pHead1 , pHead2 ):
p1 = pHead1
p2 = pHead2
while p1 != p2: #找到第一个相等的节点则返回
p1 = p1.next if p1 else pHead2
p2 = p2.next if p2 else pHead1
return p1
结果返回一个链表
class Solution:
def addInList(self , head1: ListNode, head2: ListNode) -> ListNode:
# write code here
nums1 = []
nums2 = []
while head1:
nums1.append(head1.val)
head1 = head1.next
while head2:
nums2.append(head2.val)
head2 = head2.next
c = 0
tmp = None
while nums1 or nums2:
if nums1 and nums2:
s = nums1.pop() + nums2.pop() + c #用栈就相当于倒序了
s1 = s % 10
c = s // 10
node = ListNode(s1)
node.next = tmp
tmp = node
elif nums1:
s = nums1.pop() + c
s1 = s % 10
c = s // 10
node = ListNode(s1)
node.next = tmp
tmp = node
else:
s = nums2.pop() + c
s1 = s % 10
c = s // 10
node = ListNode(s1)
node.next = tmp
tmp = node
if c:
node = ListNode(c)
node.next = tmp
tmp = node
return tmp
归并排序
先用快慢指针找到中点
class Solution:
def sortInList(self , head: ListNode) -> ListNode:
if not head or not head.next: return head #空节点或者节点为一,什么都不用做,返回
slow, fast = head, head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
mid = slow.next
slow.next = None
left = self.sortInList(head)
right = self.sortInList(mid)
dummy = cur = ListNode(0)
while left and right:
if left.val <= right.val:
cur.next = left
left = left.next
else:
cur.next = right
right = right.next
cur = cur.next
cur.next = left if left else right
return dummy.next
class Solution:
def isPail(self , head: ListNode) -> bool:
# write code here
cur = head
stack = []
while cur:
stack.append(cur.val)
cur = cur.next
return stack == stack[::-1]
奇数节点和偶数节点分别放在一起,重排后输出
方法一:
用两个头节点粉分别记录奇偶
class Solution:
def oddEvenList(self , head: ListNode) -> ListNode:
# write code here
dummy1 = cur1 = ListNode(0)
dummy2 = cur2 = ListNode(0)
count = 1
while head:
if count % 2:
cur1.next = head
cur1 = cur1.next
else:
cur2.next = head
cur2 = cur2.next
count += 1
head = head.next
cur1.next = dummy2.next
cur2.next = None
return dummy1.next
方法二:
同样用两个节点记录奇偶,但交替进行,更简洁
class Solution:
def oddEvenList(self , head: ListNode) -> ListNode:
if not head: return
evenhead = head.next
odd, even = head, evenhead
while even and even.next:
odd.next = even.next
odd = odd.next
even.next = odd.next
even = even.next
odd.next = evenhead
return head
方法一:不等则链接
class Solution:
def deleteDuplicates(self , head: ListNode) -> ListNode:
# write code here
if not head: return
slow = head
fast = head.next
while fast:
if slow.val != fast.val:
slow.next = fast
slow = slow.next
elif not fast.next:
slow.next = None
fast = fast.next
return head
方法二:如果下一个与当前相等,则下一个变为下一个的下一个
class Solution:
def deleteDuplicates(self , head: ListNode) -> ListNode:
if not head: return
cur = head
while cur.next:
if cur.val == cur.next.val: #相等则连到下一个,直到不等
cur.next = cur.next.next
else:
cur = cur.next
return head
删除所有出现的元素,第一个不保留
方法一:用到dummy技巧
并且是标记最后一个重复的元素,最后再链接
class Solution:
def deleteDuplicates(self , head: ListNode) -> ListNode:
if not head: return
dummy = pre = ListNode(0)
dummy.next = head
cur = head
while cur:
while cur.next and cur.val == cur.next.val:
cur = cur.next #结束循环后cur是最后一个重复的元素
if pre.next == cur: #没有重复,cur无移动
pre = cur
else:
pre.next = cur.next #跳过重复的元素,相当于删除,pre位置不变
cur = cur.next
return dummy.next
实现无重复数字的升序数组的二分查找
class Solution:
def search(self , nums: List[int], target: int) -> int:
# write code here
if not nums: return -1
i, j = 0, len(nums) - 1
while i <= j:
mid = i + ((j - i) >> 1)
if nums[mid] == target:
return mid
elif nums[mid] < target:
i = mid + 1
else:
j = mid - 1
return -1
class Solution:
def minNumberInRotateArray(self , rotateArray: List[int]) -> int:
# write code here
i, j = 0, len(rotateArray) - 1
while i <= j:
m = i + ((j - i) >> 1)
if rotateArray[m] > rotateArray[j]: i = m + 1
elif rotateArray[m] < rotateArray[i]: j = m #注意不是m -1
else: j -= 1
return rotateArray[i]
class Solution:
def hasPathSum(self , root: TreeNode, sum: int) -> bool:
# write code here
def dfs(root, sum):
if not root:
return False
if not root.left and not root.right:
if root.val == sum:
return True
return dfs(root.left, sum - root.val) or dfs(root.right, sum - root.val)
return dfs(root, sum)
class Solution:
def Convert(self , pRootOfTree ):
# write code here
def dfs(cur):
if not cur:
return
dfs(cur.left)
if self.pre:
self.pre.right = cur
cur.left = self.pre
self.pre = cur
else:
self.head = cur #记录头结点
self.pre = cur #初始化pre
dfs(cur.right)
if not pRootOfTree:
return
self.pre = None
dfs(pRootOfTree)
return self.head
class Solution:
def isSymmetrical(self , pRoot: TreeNode) -> bool:
# write code here
def dfs(A, B):
if not A and not B:
return True
elif not A or not B:
return False
elif A.val != B.val:
return False
else:
return dfs(A.left, B.right) and dfs(A.right, B.left)
if not pRoot:
return True
return dfs(pRoot.left, pRoot.right)
class Solution:
def isSymmetrical(self , pRoot: TreeNode) -> bool:
# write code here
def dfs(A, B):
if not A and not B:
return True
elif not A or not B:
return False
elif A.val != B.val:
return False
else:
return dfs(A.left, B.right) and dfs(A.right, B.left)
if not pRoot:
return True
return dfs(pRoot.left, pRoot.right)
class Solution:
def Mirror(self , pRoot: TreeNode) -> TreeNode:
# write code here
if not pRoot:
return
tmp = pRoot.left
pRoot.left = self.Mirror(pRoot.right)
pRoot.right = self.Mirror(tmp)
return pRoot
方法一:递归
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
def recur(root, lower, upper):
if not root:
return True #都到空了还没有返回False的就返回Ture
if root.val <= lower or root.val >= upper:
return False #关键是找到什么时候不等
return recur(root.left, lower, root.val) and recur(root.right, root.val, upper)
if not root: return
return recur(root, float('-inf'), float('+inf'))
方法二:中序遍历,递归
中序遍历的的结果应该是递增的,即当前节点的值应该大于前一个节点,否则返回False
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
self.pre = float('-inf')
def recur(root):
if not root:
return True
l = recur(root.left) #中序遍历,会一直进入到左子树的尽头,才会进行下面的访问操作
if root.val <= self.pre:
return False
self.pre = root.val
r = recur(root.right)
return l and r
return recur(root)
方法三:中序遍历,迭代
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
stack = []
pre = float('-inf')
while stack or root:
if root:
stack.append(root)
root = root.left
else:
tmp = stack.pop()
if tmp.val <= pre:
return False
pre = tmp.val
root = tmp.right
return True
中序遍历迭代法的另一种写法
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
stack, inorder = [], float('-inf')
while stack or root:
while root:
stack.append(root)
root = root.left
root = stack.pop()
# 如果中序遍历得到的节点的值小于等于前一个 inorder,说明不是二叉搜索树
if root.val <= inorder:
return False
inorder = root.val
root = root.right
return True
方法一: 层序遍历,空节点也加入队列,如果出现了空节点,右边还有节点就不是完全二叉树
class Solution:
def isCompleteTree(self , root: TreeNode) -> bool:
# write code here
if not root:
return True
queue = [root]
flag = False
while queue:
for _ in range(len(queue)):
node = queue.pop(0)
if not node:
flag = True
else: #当前节点不为空
if flag: #如果出现了一个空节点,右边还有节点,说明不是完全二叉树
return False
queue.append(node.left) #空子节点也加入队列
queue.append(node.right)
return True #遍历完返回True
写法一:类似LC100 相同的树、BM31对称的树
class Solution:
def IsBalanced_Solution(self , pRoot: TreeNode) -> bool:
if not pRoot:
return True
left = self.depth(pRoot.left)
right = self.depth(pRoot.right)
if abs(left - right) > 1:
return False
return self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)
def depth(self, root):
if not root:
return 0
left = self.depth(root.left)
right = self.depth(root.right)
return max(left, right) + 1
写法二:K神
class Solution:
def IsBalanced_Solution(self , pRoot: TreeNode) -> bool:
# write code here
if not pRoot:
return True
return abs(self.height(pRoot.left) - self.height(pRoot.right)) <= 1 and self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)
def height(self, root):
if not root:
return 0
return max(self.height(root.left), self.height(root.right)) + 1
class Solution:
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
if not p and not q: #到空了还没有False的就返回True
return True
elif not p or not q: #在不是两个都为空的条件下,那么至少有一个不为空。如果有一个为空则就是一个为空另一个不为空,那么一定不相同
return False
elif p.val != q.val:
return False
else:
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
方法一:遍历得到p、q的路径,则路径中最后一个相同的节点就是所求
class Solution:
def lowestCommonAncestor(self , root: TreeNode, p: int, q: int) -> int:
# write code here
path1 = self.getPath(root, p)
path2 = self.getPath(root, q)
i = 0
while i < len(path1) and i < len(path2):
if path1[i] == path2[i]:
res = path1[i]
i += 1
else:
break
return res
def getPath(self, root, target):
path = []
if not root:
return
node = root
while node.val != target:
path.append(node.val)
if target < node.val:
node = node.left
else:
node = node.right
path.append(node.val)
return path
方法二:递归
如果某节点是p、q的最近公共祖先,那么p、q肯定在该节点的两边
如果p、q同在某节点左侧或右侧,则该节点还不是最近的公共祖先
#方法二:递归
class Solution:
def lowestCommonAncestor(self , root: TreeNode, p: int, q: int) -> int:
if (p <= root.val and q >= root.val) or (p >= root.val and q <= root.val):
return root.val
elif p <= root.val and q <= root.val:
return self.lowestCommonAncestor(root.left, p, q)
else:
return self.lowestCommonAncestor(root.right, p, q)
方法一:递归
class Solution:
def lowestCommonAncestor(self , root: TreeNode, o1: int, o2: int) -> int:
# write code here
if not root:
return #或标记为 -1
if root.val == o1 or root.val == o2:
return root.val
left = self.lowestCommonAncestor(root.left, o1, o2) #在左子树中寻找公共祖先
right = self.lowestCommonAncestor(root.right, o1, o2) #在右子树中寻找公共祖先
if not left:
return right #左子树中没找到,则在右子树中
if not right:
return left #右子树中没找到吗,则在左子树中
return root.val #否则是当前节点(两边都找到)
方法二:找路径,然后比较
class Solution:
flag = False
def dfs(self, root, path, target):
if self.flag or not root:
return
path.append(root.val)
if root.val == target:
self.flag = True
return
self.dfs(root.left, path, target)
self.dfs(root.right, path, target)
if self.flag: #找到则一直返回
return
path.pop() #该子树没有,回溯
def lowestCommonAncestor(self , root: TreeNode, o1: int, o2: int) -> int:
path1 = []
path2 = []
self.dfs(root, path1, o1)
self.flag = False
self.dfs(root, path2, o2)
i = 0
while i < len(path1) and i < len(path2):
if path1[i] == path2[i]:
res = path1[i]
i += 1
else:
break
return res
class Solution:
index = 0
s = ''
def SerializeFunc(self, root):
# write code here
if not root:
self.s += '#'
return
self.s += str(root.val) + '!'
self.SerializeFunc(root.left)
self.SerializeFunc(root.right)
def Serialize(self, root):
if not root:
return '#'
self.SerializeFunc(root)
return self.s
def Deserialize(self, s):
# write code here
if s == '#':
return
if self.index >= len(s) or s[self.index] == '#':
self.index += 1
return None
val = 0
while s[self.index] != '!' and self.index != len(s):
val = val * 10 + int(s[self.index])
self.index += 1
root = TreeNode(val)
if self.index == len(s):
return root
else:
self.index += 1
root.left = self.Deserialize(s)
root.right = self.Deserialize(s)
return root
根据前序遍历和中序遍历结果重建二叉树
class Solution:
def reConstructBinaryTree(self , pre: List[int], vin: List[int]) -> TreeNode:
# write code here
#left, right是子树的边界
def recur(root, left, right): ##注意这里的root是当前子树的根在pre中的索引
if left > right:
return
node = TreeNode(pre[root])
i = dic[pre[root]] #根在vin中的索引
node.left = recur(root+1, left, i-1)
node.right = recur(root+i-left+1, i+1, right)
return node
dic = {}
for i, v in enumerate(vin):
dic[v] = i
return recur(0, 0, len(vin)-1)
方法一:建树+BFS
class Solution:
def solve(self , xianxu: List[int], zhongxu: List[int]) -> List[int]:
# write code here
def recur(root, left, right):
if left > right:
return
node = TreeNode(xianxu[root])
i = dic[xianxu[root]] #在中序遍历中的索引
node.left = recur(root+1, left, i-1)
node.right = recur(root+i-left+1, i+1, right)
return node
dic = {}
for i in range(len(zhongxu)):
dic[zhongxu[i]] = i
root = recur(0, 0, len(zhongxu)-1)
#层序遍历
res = []
queue = [root]
while queue:
n = len(queue)
for i in range(n):
node = queue.pop(0)
if i == n-1:
res.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
return res
方法二:建树+DFS
\DFS,按照 [根 | 右 | 左] 的顺序访问
class Solution:
def solve(self , xianxu: List[int], zhongxu: List[int]) -> List[int]:
# write code here
def recur(root, left, right):
if left > right:
return
node = TreeNode(xianxu[root])
i = dic[xianxu[root]] #在中序遍历中的索引
node.left = recur(root+1, left, i-1)
node.right = recur(root+i-left+1, i+1, right)
return node
dic = {}
for i in range(len(zhongxu)):
dic[zhongxu[i]] = i
root = recur(0, 0, len(zhongxu)-1)
def dfs(root, depth):
if not root:
return
if depth == len(res):
res.append(root.val)
depth += 1
dfs(root.right, depth)
dfs(root.left, depth)
res = []
dfs(root, 0)
return res
class Solution:
def __init__(self):
self.stack1 = []
self.stack2 = []
def push(self, node):
# write code here
self.stack1.append(node)
def pop(self):
# return xx
if not self.stack2:
while self.stack1:
self.stack2.append(self.stack1.pop())
if self.stack2:
return self.stack2.pop()
class Solution:
A = []
B = []
def push(self, node):
# write code here
self.A.append(node)
if not self.B or node <= self.B[-1]:
self.B.append(node)
def pop(self):
# write code here
val = self.A.pop()
if self.B[-1] == val:
self.B.pop()
return val
def top(self):
# write code here
return self.A[-1]
def min(self):
# write code here
return self.B[-1]
给出一个仅包含字符’(‘,’)‘,’{‘,’}‘,’[‘和’]',的字符串,判断给出的字符串是否是合法的括号序列
class Solution:
def isValid(self , s: str) -> bool:
# write code here
#([]{})
stack = []
for c in s:
if c == '(' or c == '[' or c == '{':
stack.append(c)
elif not stack: #必须有左括号才能遇到右括号
return False
elif c == ')':
if stack.pop() != '(':
return False
elif c == ']':
if stack.pop() != '[':
return False
else:
if stack.pop() != '{':
return False
return not stack
方法一:维持一个单调递减的双向队列
class Solution:
def maxInWindows(self , num: List[int], size: int) -> List[int]:
from collections import deque
res = []
dq = deque()
for i in range(size):
while dq and num[dq[-1]] < num[i]:
dq.pop() #比当前小的都不会是后面的最大值,pop掉
dq.append(i)
for i in range(size, len(num)):
res.append(num[dq[0]])
if dq and dq[0] < i - size + 1:
#弹出窗口移走后的值
dq.popleft()
while dq and num[dq[-1]] < num[i]:
dq.pop()
dq.append(i)
res.append(num[dq[0]])
return res
快排写法一:
def quick_sort(arr, l, r):
if l >= r:
return
i, j = l, r
while i < j:
while i < j and arr[j] <= arr[l]: j -= 1
while i < j and arr[i] >= arr[l]: i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i], arr[l] = arr[l], arr[j]
quick_sort(arr, l, i-1)
quick_sort(arr, i+1, r)
quick_sort(a, 0, n-1)
快排写法二:
def partition(arr, l, r):
tmp = arr[l]
while l < r:
while l < r and arr[r] <= tmp:
r -= 1
if l == r:
break
else:
arr[l] = arr[r]
while l < r and arr[l] >= tmp:
l += 1
if l == r:
break
else:
arr[r] = arr[l]
arr[l] = tmp
return l
def quick_sort(arr, l, r):
if l >= r:
return
p = partition(arr, l, r)
quick_sort(arr, l, p-1)
quick_sort(arr, p+1, r)
quick_sort(a, 0, n-1)
方法一:快排+二分
class Solution:
def findKth(self , a: List[int], n: int, K: int) -> int:
# write code here
def partition(arr, l, r):
tmp = arr[l]
while l < r:
while l < r and arr[r] <= tmp:
r -= 1
if l == r:
break
else:
arr[l] = arr[r]
while l < r and arr[l] >= tmp:
l += 1
if l == r:
break
else:
arr[r] = arr[l]
arr[l] = tmp
return l
def quick_sort(arr, l, r, k):
p = partition(arr, l, r)
if p - l + 1 == k:
return arr[p]
elif p - l + 1 > k:
return quick_sort(arr, l, p-1, k)
else:
return quick_sort(arr, p+1, r, k - (p - l + 1))
return quick_sort(a, 0, n - 1, K)
方法一:最大最小堆
from heapq import *
class Solution:
A = [] #最小堆,保存大的一半
B = [] #最大堆,保存小的一半
def Insert(self, num):
# write code here
import heapq
if len(self.A) == len(self.B): #两者长度一样时,添加到A
heappush(self.B, -num)
heappush(self.A, -heappop(self.B))
else: #添加到B
heappush(self.A, num)
heappush(self.B, -heappop(self.A) )
def GetMedian(self):
# write code here\
return self.A[0] if len(self.A) != len(self.B) else (self.A[0] - self.B[0]) / 2
方法二:
插入排序
class Solution:
A = []
def Insert(self, num):
self.A.append(num)
for i in range(len(self.A)-1, 0, -1):
if self.A[i] < self.A[i-1]:
self.A[i], self.A[i-1] = self.A[i-1], self.A[i]
else:
break
def GetMedian(self):
n = len(self.A)
if n % 2:
return self.A[n//2]
else:
return (self.A[(n-1)//2] + self.A[n//2]) / 2
方法一:栈+递归
class Solution:
def solve(self , s: str) -> int:
# write code here
s.strip()
stack = []
res = 0
num = 0
sign = '+'
index = 0
while index < len(s):
if s[index] == '(':
lens = 1 #当前还有几个左括号
end = index + 1
while lens > 0:
if s[end] == '(':
lens += 1
if s[end] == ')':
lens -= 1
end += 1
num = self.solve(s[index + 1: end - 1])
index = end - 1
continue
if '0' <= s[index] <='9':
num = num * 10 + int(s[index])
if not '0' <= s[index] <= '9' or index == len(s) - 1:
if sign == '+':
stack.append(num)
elif sign == '-':
stack.append(-1 * num)
elif sign == '*':
stack.append(stack.pop() * num)
num = 0
sign = s[index]
index += 1
while stack:
res += stack.pop()
return res
注意这里数组下标从1开始算
class Solution:
def twoSum(self , numbers: List[int], target: int) -> List[int]:
# write code here
dic = {}
for i, num in enumerate(numbers):
if target - num in dic:
return [dic[target-num]+1, i+1]
dic[num] = i #关键是遍历一次就将其记录下来,避免重复遍历
return
方法一:摩尔投票
不同的两两抵消
class Solution:
def MoreThanHalfNum_Solution(self , numbers: List[int]) -> int:
# write code here
count = 0
for num in numbers:
if count == 0:
x = num
count += 1
else:
if num == x:
count += 1
else:
count -= 1
return x
最优的应该是异或位运算发方法
方法一:哈希统计
class Solution:
def FindNumsAppearOnce(self , array: List[int]) -> List[int]:
# write code here
dic = {}
res = []
for num in array:
if num in dic:
dic[num] += 1
else:
dic[num] = 1
for k, v in dic.items():
if v == 1:
res.append(k)
res.sort()
return res
方法一:哈希
class Solution:
def minNumberDisappeared(self , nums: List[int]) -> int:
# write code here
A = set()
for num in nums:
A.add(num)
i = 1
while 1:
if i not in A:
return i
break
i += 1
方法一:先排序,然后遍历做双指针
class Solution:
def threeSum(self , num: List[int]) -> List[List[int]]:
# write code here
num.sort()
res = []
for k in range(len(num) - 2):
if num[k] > 0:
break
if k > 0 and num[k] == num[k-1]:
continue
i, j = k + 1, len(num) - 1
while i < j:
s = num[k] + num[i] + num[j]
if s < 0:
i += 1
while i < j and num[i] == num[i-1]: i += 1
elif s > 0:
j -= 1
while i < j and num[j] == num[j+1]: j -= 1
else:
res.append([num[k], num[i], num[j]])
i += 1
j -= 1
while i < j and num[i] == num[i-1]: i += 1
while i < j and num[j] == num[j+1]: j -= 1 #注意这里是num[j+1]
return res
LC46 题解
方法一:回溯
选一个,后接剩下没选过的的数字的全排列
达到深度则返回(当前填充到最后一个)
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
def dfs(nums, size, depth, path, used, res):
if depth == size:
res.append(path[:]) #如果写成res.append(path)最后的结果都是[], 因为这样传进去的是path的地址,path最后返回根变成空,结果也跟着变
return
for i in range(size): #遍历选择当前的数字
if not used[i]: #选择还没选过的
used[i] = True
path.append(nums[i]) #选择i之后,接的是i+1及后面数字的排列
dfs(nums, size, depth+1, path, used, res)
used[i] = False #回溯
path.pop()
if not nums:
return
res, path = [], []
used = [False for _ in range(len(nums))]
dfs(nums, len(nums), 0, path, used, res)
return res
写法二:选过的则去掉,写法一是选过的则标记
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
def dfs(nums, path, res):
if not nums:
res.append(path)
return
for i in range(len(nums)):
dfs(nums[:i] + nums[i+1:], path+[nums[i]], res)
path, res =[], []
dfs(nums, path, res)
return res
class Solution:
def permuteUnique(self , num: List[int]) -> List[List[int]]:
# write code here
def dfs(num, size, index, path, used, res): #index表示当前要选择的位置
if index == size:
res.append(path[:])
return
for i in range(size):
#i和i-1相等的情况下, 只有i-1是用过在才递归,这样保证只出现一次1,1,2
if not used[i] and (i == 0 or num[i] != num[i-1] or used[i-1]):
# if used[i] or (i > 0 and num[i] == num[i-1] and not used[i-1]): #i和i-1相等,且i没用过,再用i-1作为起点就重复了,要跳过
# continue
used[i] = True
path.append(num[i])
dfs(num, size, index + 1, path, used, res)
used[i] = False
path.pop()
num.sort()
used = [False for _ in range(len(num))]
res, path = [], []
dfs(num, len(num), 0, path, used, res)
return res
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
def dfs(nums, start, path, res):
res.append(path[:])
for i in range(start, len(nums)):
path.append(nums[i])
dfs (nums, i+1, path, res)
path.pop()
res, path = [], []
dfs(nums, 0, path, res)
return res
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
def dfs(nums, start, size, path, res, target):
if target < 0:
return
if target == 0:
res.append(path[:])
return
for i in range(start, size):
path.append(nums[i])
target -= nums[i]
dfs(nums, i, size, path, res, target) #注意这里仍然是从i开始,可以重复选取
# dfs(nums, i, size, path, res, target - nums[i]) #如果在这里计算target,传进去的是计算出来的一个新值,有新的地址,返回当前target不变
target += nums[i]
path.pop()
path, res = [], []
dfs(candidates, 0, len(candidates), path, res, target)
return res
方法一:回溯
遍历行和列进行,如果当前为岛屿1,则进入dfs
在dfs中将其置为0,并且dfs其上下上左右
dfs终止的条件是当前位置为0或者超出边界
class Solution:
def solve(self , grid: List[List[str]]) -> int:
# write code here
def dfs(grid, i, j):
nr, nc = len(grid), len(grid[0])
if not 0 <= i < nr or not 0 <= j < nc or grid[i][j] == '0': #终止条件
return
grid [i][j] = '0' #标记为0避免重复访问
dfs(grid, i - 1, j)
dfs(grid, i + 1, j)
dfs(grid, i, j - 1)
dfs(grid, i, j + 1)
nr, nc = len(grid), len(grid[0])
count = 0
for i in range(nr):
for j in range(nc):
if grid[i][j] == '1':
count += 1
dfs(grid, i, j)
return count
有重复的需要先排序
class Solution:
def Permutation(self , str: str) -> List[str]:
# write code here
def dfs(s, size, index, path, used, res):
if index == size:
res.append(path)
return
for i in range(size):
if not used[i] and (i == 0 or s[i] != s[i-1] or used[i-1]):
used[i] = True
path += s[i]
dfs(s, size, index+1, path, used, res)
path = path[:-1]
used[i] = False
str = sorted(str) #注意str没有str.sort
used = [False for _ in range(len(str))]
res, path = [], ''
dfs(str, len(str), 0, path, used, res)
return res
方法一:回溯
用dfs遍历行
对于每一行,遍历每一列,并看当前列是不是可以放Q的位置
放满n行结果+1
class Solution:
def Nqueen(self , n: int) -> int:
# write code here
def dfs(r, res):
if r == n:
res += 1
return res
for i in range(n):
if i in columns or r - i in diagonals1 or r + i in diagonals2:
continue
columns.add(i)
diagonals1.add(r - i)
diagonals2.add(r + i)
res = dfs(r + 1, res)
columns.remove(i)
diagonals1.remove(r - i)
diagonals2.remove(r + i)
return res
columns = set()
diagonals1 = set()
diagonals2 = set()
res = 0
return dfs(0, res)
注意:
Python是不允许程序员选择采用传值还是传址的。Python参数传递采用的肯定是“传对象引用”的方式。实际上,这种方式相当于传值和传址的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值——相当于传址。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象——相当于传值。
所以以下代码返回的结果是0
class Solution:
def Nqueen(self , n: int) -> int:
# write code here
def dfs(r, res):
if r == n:
res += 1
for i in range(n):
if i in columns or r - i in diagonals1 or r + i in diagonals2:
continue
columns.add(i)
diagonals1.add(r - i)
diagonals2.add(r + i)
dfs(r + 1, res)
columns.remove(i)
diagonals1.remove(r - i)
diagonals2.remove(r + i)
columns = set()
diagonals1 = set()
diagonals2 = set()
res = 0
dfs(0, res)
return res
返回可行棋盘版本:
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
def generateBoard():
board = list()
for i in range(n):
row[queens[i]] = "Q" #row是一维向量,表示当前行的情况,i表示当前是第几行,queens[i]表示第几列;即当前第i行的第几列是Q
board.append("".join(row))
row[queens[i]] = "."
return board
def backtrack(row: int):
if row == n:
board = generateBoard()
solutions.append(board)
else:
for i in range(n): #遍历所有的列
if i in columns or row - i in diagonal1 or row + i in diagonal2:
continue
queens[row] = i #第row行的第i列; 使用一个数组记录每行放置的皇后的列下标,依次在每一行放置一个皇后
columns.add(i)
diagonal1.add(row - i) #同是左上-右下对角线的话,行坐标与列坐标之差相等
diagonal2.add(row + i) #同是做下-右上对角线,行坐标与列坐标之和相等
backtrack(row + 1) #遍历行
columns.remove(i)
diagonal1.remove(row - i)
diagonal2.remove(row + i)
solutions = list()
queens = [-1] * n
columns = set()
diagonal1 = set()
diagonal2 = set()
row = ["."] * n
backtrack(0)
return solutions
方法一:回溯
相比于全排列,只是没有了从所给nums中选择的过程,而是直接添加 ‘(’ 或 ‘)’
全排列 indexlen(nums) 时添加结果,这里 s2*n 时添加结果
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
def dfs(s, left, right):
if len(s) == 2 * n:
res.append(''.join(s))
return
if left < n:
s.append('(')
dfs(s, left + 1, right)
s.pop()
if right < left: #如果写right < n, 就包括了left <= right的情况, 这是不该加右括号的
s.append(')')
dfs(s, left, right + 1)
s.pop()
res = []
s = []
dfs(s, 0, 0)
return res
写法二:
from typing import List
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
res = []
cur_str = ''
def dfs(cur_str, left, right, n):
"""
:param cur_str: 从根结点到叶子结点的路径字符串
:param left: 左括号已经使用的个数
:param right: 右括号已经使用的个数
:return:
"""
if left == n and right == n:
res.append(cur_str)
return
if left < right:
return
if left < n:
dfs(cur_str + '(', left + 1, right, n)
if right < n:
dfs(cur_str + ')', left, right + 1, n)
dfs(cur_str, 0, 0, n)
return res
方法一:深度优先搜索+保存中间结果
记忆化搜索
class Solution:
def solve(self , matrix: List[List[int]]) -> int:
if not matrix: return
m, n = len(matrix), len(matrix[0])
memo = [[0] * n for _ in range(m)]
def dfs(x, y):
if memo[x][y] != 0: return memo[x][y] #避免重复计算;相当与动态规划用dp保存子问题的解
memo[x][y] += 1
for new_x, new_y in [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1 )]:
if 0 <= new_x < m and 0 <= new_y < n and matrix[x][y] < matrix[new_x][new_y]:
memo[x][y] = max(memo[x][y], dfs(new_x, new_y) + 1) #每能遍历一个,长度就加一
return memo[x][y] #一定是遍历到最后超出边界或者没有增长路径才返回,每个格子的结果是唯一的
res = 0
for i in range(m):
for j in range(n):
res = max(res, dfs(i, j))
return res
与子集、组合总和问题一样,需要定义一个起点,然后遍历;这里需要通过长度和数值大小进行约束剪枝
class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
seg_count = 4
res = []
segments = [0] * seg_count
def dfs(segId, segStart):
if segId == 4:
if segStart == len(s):
ip = '.'.join([str(seg) for seg in segments])
res.append(ip)
return
if segStart == len(s):
return
if s[segStart] == '0':
segments[segId] = 0
dfs(segId + 1, segStart + 1)
addr = 0
for segEnd in range(segStart, len(s)):
addr = addr * 10 + (ord(s[segEnd]) - ord('0'))
if 0 < addr <= 255:
segments[segId] = addr #这里不涉及pop,因为不是用栈来保存结果,这里新结果会覆盖旧结果
dfs(segId + 1, segEnd + 1)
else:
return
dfs(0, 0)
return res
写法二,segments作为参数
class Solution:
def restoreIpAddresses(self , s: str) -> List[str]:
seg_count = 4
res = []
segments = [0] * seg_count
def dfs(segId, segStart, segments):
if segId == 4:
if segStart == len(s):
ip = '.'.join([str(seg) for seg in segments])
res.append(ip)
return
if segStart == len(s):
return
if s[segStart] == '0':
segments[segId] = 0
dfs(segId + 1, segStart + 1, segments)
addr = 0
for segEnd in range(segStart, len(s)):
addr = addr * 10 + (ord(s[segEnd]) - ord('0'))
if 0 < addr <= 255:
segments[segId] = addr #这里不涉及pop,因为不是用栈来保存结果,这里新结果会覆盖旧结果
dfs(segId + 1, segEnd + 1, segments)
else:
return
dfs(0, 0, segments)
return res
写法一:
class Solution:
def Fibonacci(self , n: int) -> int:
a, b = 1, 1
if n == 1 or n ==2:
return 1
for i in range(n-2):
tmp = a
a = b
b += tmp
return b
写法二:
class Solution:
def Fibonacci(self , n: int) -> int:
a, b = 0, 1
for _ in range(n):
tmp = a
a = b
b += tmp
return a
写法三:
class Solution:
def Fibonacci(self , n: int) -> int:
dp = [0, 1]
for i in range(2, n+1):
dp.append(dp[i-1] + dp[i-2])
return dp[n]
class Solution:
def jumpFloor(self , number: int) -> int:
#1, 2,
#dp[i] = dp[i-1] + dp[i-2]]
a, b = 1, 1
for _ in range(number):
tmp = a
a = b
b += tmp
return a
方法一:
用dp保存跳到第i个阶梯的费用,i可以从i-2开始跳,跳两个台阶,也可以从i-1开始跳,跳一个台阶
转移方程:dp[i] = min(cost[i-2] + dp[i-2], cost[i-1] + dp[i-1])
关键是递推过程,而不是模拟具体怎么跳
class Solution:
def minCostClimbingStairs(self , cost: List[int]) -> int:
n = len(cost)
dp = [0] * (n + 1)
for i in range(2, n + 1): #题目的跳到顶部是越过n-1到n
dp[i] = min(cost[i-2] + dp[i-2], cost[i-1] + dp[i-1])
return dp[n]
涉及两个字符串,一般是动态规划一般是二维的
class Solution:
def LCS(self , s1: str, s2: str) -> str:
# write code here
m, n = len(s1), len(s2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
if s1[i-1] == s2[j-1]:
dp[i][j] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) #但此时s1[i-1]不一定等于s2[j], s1[i-1]不一定等于s2[j]
i, j = m, n
s = []
while dp[i][j] != 0:
if dp[i][j] == dp[i - 1][j]:
i -= 1
elif dp[i][j] == dp[i][j - 1]:
j -= 1
elif dp[i][j] > dp[i-1][j - 1]: #说明s1[i - 1] == s2[j - 1]
i -= 1
j -= 1
s.append(s1[i])
if not s:
return '-1'
else:
return ''.join(s[::-1])
方法一:枚举
暴力法是遍历str1中的每个起点,并遍历每个长度,看是在str2中。复杂度太大
改进方法是维持一个最大长度,看i往前max_len+1个字符在不在str2中,在则更新
class Solution:
def LCS(self , str1: str, str2: str) -> str:
if len(str1) > len(str2):
str1, str2 = str2, str1
max_len = 0
for i in range(len(str1)):
if str1[i - max_len : i + 1] in str2: #第i个字符(含)往前max_len+1个字符在str2中才更新最长字符
res = str1[i - max_len : i + 1]
max_len += 1
return res
方法二:动态规划
相比与BM65 最长公共子序列,在更新最大值的时候记录位置即可,然后往前找max_len个
步骤:
dp [ i ] [ j ]维持 str1[0, i - 1] 和 str2[0, j - 1]的最大公共子串
转移方程为:如果遍历到的该位两个字符相等,则此时长度等于两个前一位长度+1,dp [ i ] [ j ] = dp[i - 1] [ j - 1 ] + 1,如果遍历到该位时两个字符不相等,则置为0,因为这是子串,必须连续相等,断开要重新开始。
每次更新dp[i] [j]后维护最大值,并更新字串结束的位置
最后根据最大值结束的位置可截取出字串
class Solution:
def LCS(self , str1: str, str2: str) -> str:
# write code here
m, n = len(str1), len(str2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
max_len = 0
pos = 0
for i in range(1, m + 1):
for j in range(1, n + 1):
if str1[i-1] == str2[j-1]:
dp[i][j] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = 0 #不等则置0重新开始
if dp[i][j] > max_len:
max_len = dp[i][j]
pos = i - 1 #str1的第i-1个字符和str2的第j-1个字符是相等的
return str1[pos - max_len + 1 : pos + 1]
方法一:动态规划
class Solution:
def uniquePaths(self , m: int, n: int) -> int:
# write code here
dp = [[1] * n] + [[1] + [0] * (n - 1) for _ in range(m - 1)]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i][j - 1] + dp[i - 1][j]
return dp[m - 1][n - 1]
写法二:
class Solution:
def uniquePaths(self , m: int, n: int) -> int:
dp = [[0] * n for _ in range(m)]
for i in range(m):
for j in range(n):
if i == 0:
dp[i][j] = 1
continue
if j == 0:
dp[i][j] = 1
continue
dp[i][j] = dp[i - 1][j] + dp[i][j -1]
return dp[m - 1][n -1]
方法二:递归,超时
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
if m == 1 or n == 1:
return 1
else:
return self.uniquePaths(m - 1, n) + self.uniquePaths(m, n - 1)
方法一:动态规划
class Solution:
def minPathSum(self , matrix: List[List[int]]) -> int:
m, n = len(matrix), len(matrix[0])
for i in range(m):
for j in range(n):
if i == 0 and j == 0:
matrix[i][j] = matrix[i][j]
continue
if i == 0:
matrix[i][j] += matrix[i][j - 1]
elif j == 0:
matrix[i][j] += matrix[i -1][j]
else:
matrix[i][j] += min(matrix[i-1][j], matrix[i][j -1])
return matrix[m -1][n -1]
有一种将字母编码成数字的方式:‘a’->1, ‘b->2’, … , ‘z->26’。
我们把一个字符串编码成一串数字,再考虑逆向编译成字符串。
由于没有分隔符,数字编码成字母可能有多种编译结果,例如 11 既可以看做是两个 ‘a’ 也可以看做是一个 ‘k’ 。但 10 只可能是 ‘j’ ,因为 0 不能编译成任何结果。
方法一:动态规划
class Solution:
def solve(self , nums: str) -> int:
# 当前位单独翻译dp[i] = dp[i-1], 与前一位一起翻译 dp[i] = dp[i - 2]
n = len(nums)
dp = [0] * (n + 1)
dp[0] = 1
dp[1] = 1
for i in range(2, n +1):
if nums[i - 1] == '0' and nums[i - 2] != '1' and nums[i - 2] != '2':
return 0
elif '11' <= nums[i - 2 : i] <= '19' or '21' <= nums[i - 2 : i] <= '26':
dp[i] = dp[i - 2] + dp[i - 1]
elif nums[i - 2 : i] == '10' or nums[i - 2 : i] == '20':
dp[i] = dp[i - 2]
else:
dp[i] = dp[i - 1]
return dp[n]
LC 剑指offer 46. 把数字翻译成字符串
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
写法一:初始化一个第一个状态,i表示当前字符的下标加一
class Solution:
def translateNum(self, num: int) -> int:
s = str(num)
n = len(s)
dp = [0] * (n + 1)
dp[0] = 1
dp[1] = 1
for i in range(2, n + 1):
if '10' <= s[i - 2 : i] <= '25':
dp[i] = dp[i - 2] + dp[i - 1]
else:
dp[i] = dp[i - 1]
return dp[n]
写法二:初始化两个状态,i就是当前字符的下标
class Solution:
def translateNum(self, num: int) -> int:
s = str(num)
n = len(s)
if n == 1:
return 1
dp = [0] * n
dp[0] = 1
if '10' <= s[:2] <= '25':
dp[1] = 2
else:
dp[1] = 1
for i in range(2, n):
if '10' <= s[i - 1 : i + 1] <= '25':
dp[i] = dp[i - 2] + dp[i - 1]
else:
dp[i] = dp[i - 1]
return dp[n - 1]
方法一:动态规划
dp[i]根据dp[0]~dp[i-1]得到
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GMZDJdV1-1652544251608)(image-20220505214127913.png)]
class Solution:
def minMoney(self , arr: List[int], aim: int) -> int:
dp = [float('+inf')] * (aim + 1)
dp[0] = 0
for i in range(1, aim + 1): #遍历1~aim元
for j in range(len(arr)):
if arr[j] <= i:
#在遍历硬币的时候,i还是i,并没有减少,只是不断查看dp[i - arr[j]]看哪个dp[0]~dp[i-1]中的dp最小,并维护最小值
dp[i] = min(dp[i], dp[i - arr[j]] + 1)
return dp[aim] if dp[aim] != float('+inf') else -1
错解:dp[i] = dp[i - 1] if arr[i] 不大于子序列的最后一个,else dp[i - 1] + 1
不能只考虑i - 1,i 可以和0~i-1范围的数构成子序列
对于0~i-1范围内的某个位置k,只要arr[i] > arr[k],则可以构成递增子序列,更新dp = dp[k]
#错解
class Solution:
def LIS(self , arr: List[int]) -> int:
n = len(arr)
dp = [0] * n
dp[0] = 1
last = arr[0]
for i in range(1, n):
if arr[i] > last:
dp[i] = dp[i - 1] + 1
last = arr[i]
else:
dp[i] = dp[i -1]
return dp[n - 1]
方法一:动态规划
要找到最长的递增子序列长度,每当我们找到一个位置,它是继续递增的子序列还是不是,它选择前面哪一处接着才能达到最长的递增子序列
dp[i] 表示以arr[i]结尾的最长子序列长度
class Solution:
def LIS(self , arr: List[int]) -> int:
if not arr:
return 0
n = len(arr)
dp = [1] * n
for i in range(n):
for j in range(i):
if arr[j] < arr[i]:
dp[i] = max(dp[i], dp[j] + 1) #已知dp[0] ~ dp[i - 1]
return max(dp) #这里不是dp[n - 1], dp[i]的值代表以arr[i]结尾的最长子序列长度,不是前i个数字的最长子序列,最长子序列不一定以arr[i]结尾
方法一:动态规划
dp[i] 代表以元素 array[i] 为结尾的连续子数组最大和。
dp[i] = max(dp[i - 1] + array(i) , array[i] )
class Solution:
def FindGreatestSumOfSubArray(self , array: List[int]) -> int:
# write code here
if len(array) == 1:
return array[0]
s = array[0]
res = float('-inf')
for num in array[1:]:
# if s + num > num:
# s += num
# else:
# s = num
s = max(s + num, num)
res = max(res, s)
return res
class Solution:
def getLongestPalindrome(self , A: str) -> int:
n = len(A)
dp = [[False] * n for _ in range(n)] #dp[i][j]表示i到j的子串是否为回文串
for i in range(n):
dp[i][i] = True
max_len = 1 #需要记录最大长度
for L in range(2, n + 1): #状态转移的时候,是从较短的字符向较长的字符转移,所以循环的时候先枚举长度
for i in range(n):
j = i + L -1
if j > n -1:
break
if A[i] != A[j]:
dp[i][j] = False
else:
if L <= 3:
dp[i][j] = True
else:
dp[i][j] = dp[i + 1][j - 1]
if dp[i][j] and L > max_len:
max_len = L
return max_len
class Solution:
def editDistance(self , str1: str, str2: str) -> int:
#dp[i][j]表示str1前i个字符与str2前j个字符的编辑距离
m = len(str1)
n = len(str2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m + 1):
dp[i][0] = i
for j in range(n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
a = dp[i - 1][j] + 1 #在str1中插入一个字符到达dp[i][j]
b = dp[i][j - 1] + 1 #在str2中插入一个字符到达dp[i][j]
c = dp[i - 1][j - 1] #str[i] == str[j]的情况下,不用操作,dp[i][j] =dp[i -1][j - 1]
if str1[i - 1] != str2[j - 1]:
c += 1
dp[i][j] = min(a, b, c)
return dp[m][n]
class Solution:
def match(self , str: str, pattern: str) -> bool:
m = len(str)
n = len(pattern)
dp = [[False] * (n + 1) for _ in range(m + 1)]
dp[0][0] = True
def match(i, j):
if i == 0:
return False
if pattern[j - 1] == '.':
return True
return str[i - 1] == pattern[j - 1]
for i in range(m + 1):
for j in range(1, n + 1):
if pattern[j - 1] == '*':
dp[i][j] |= dp[i][j - 2] #pattern *前面的字符取0次
if match(i, j - 1): #str[i] 与pattern[j - 1]匹配,*前面的字符可以取多次,相当于把str的最后一个字符一次次丢弃,并与pattern匹配
dp[i][j] |= dp[i - 1][j] #在s[i] == p[i - 1]时,将s[i]扔掉, 检查s的1~i-1是否与p的1~j匹配
#虽然这里只看dp[i - 1][j] 但dp[i - 1][j] 也是由dp[i - 2][j]得来的
else:
if match(i, j):
dp[i][j] |= dp[i - 1][j - 1]
return dp[m][n]
class Solution:
def longestValidParentheses(self, s: str) -> int:
#dp[i] 表示以s[i]结尾的最长有效子串长度,注意不是前i个字符的最长有效子串长度
n = len(s)
if n == 0 or n == 1:
return 0
dp = [0] * n
if s[0] == '(' and s[1] == ')':
dp[1] = 2
res = max(0, dp[1])
for i in range(2, n):
if s[i] == '(': #以'('结尾,必不是有效的子串
dp[i] = 0
elif s[i] == ')' and s[i - 1] == '(':
dp[i] = dp[i - 2] + 2
elif s[i] == ')' and s[i - 1] == ')':
if i - dp[i - 1] - 1 >= 0 and s[i - dp[i - 1] - 1] == '(':
dp[i] = dp[i - 1] + 2 + dp[i - dp[i - 1] - 2] #最后一项是因为s[i - dp[i -1] -1]前可能是(...)这样的有效括号,通过s[i - dp[i - 1] - 1]连起来了
res = max(res, dp[i])
return res
方法一:动态规划
dp[i] 表示到第i间房屋可以偷得的最大金额
i 可以选择偷或者不偷, 偷的话dp[i] = dp[i -2] + nums[i] , 不偷的话dp[i] = dp[i -1]
偷窃第 k间房屋,那么就不能偷窃第 k-1 间房屋,偷窃总金额为前 k-2间房屋的最高总金额与第 kk 间房屋的金额之和。
不偷窃第 k 间房屋,偷窃总金额为前 k-1 间房屋的最高总金额。
dp[i] = max()
写法一:
class Solution:
def rob(self , nums: List[int]) -> int:
n = len(nums)
if n == 1:
return nums[0]
dp = [0] * n
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i in range(2, n):
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])
return dp[n - 1]
空间优化:用两个变量滚动保存dp[i - 2] 和dp[i - 1]。因为这里dp[i] [j]只与dp[i - 1] 和dp[i - 2]有关系
class Solution:
def rob(self , nums: List[int]) -> int:
n = len(nums)
if n == 1:
return nums[0]
a = nums[0]
b = max(nums[0], nums[1])
for i in range(2, n):
# tmp = a
# a = b
# b = max(tmp + nums[i], b)
a, b = b, max(a + nums[i], b)
return b
写法二:dp[i]表示第i个,比对应的下标要多1
class Solution:
def rob(self , nums: List[int]) -> int:
n = len(nums)
dp = [0] * (n + 1)
dp[1] = nums[0]
for i in range(2, n + 1):
dp[i] = max(dp[i - 2] + nums[i-1], dp[i - 1]) #分别对应选和不选当前nums.如果选,则只能和dp[i-2]相加
#如果上一次选了当前nums,下一次选在num的时候只能和dp[i-2]相加,也不相邻
return dp[n]
class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
if n == 1:
return nums[0]
pre = nums[0]
cur = max(pre, nums[1])
if n == 2:
return cur
for i in range(2, n - 1):
pre, cur = cur, max(pre + nums[i], cur)
tmp = cur
pre = nums[1]
cur = max(pre, nums[2])
for i in range(3, n):
pre, cur = cur, max(pre + nums[i], cur)
return max(tmp, cur)
dp[i]表示第i天的利润,
dp[i] = max(dp[i - 1], prices[i] - cost)
维护一个cost表示第i天之前的最低价格
class Solution:
def maxProfit(self , prices: List[int]) -> int:
n = len(prices)
res = 0
cost = prices[0]
for i in range(1, n):
res = max(res, prices[i] - cost)
cost = min(cost, prices[i])
return res
方法一:动态规划
dp[i] [0]和dp[i] [1]两个状态分别表示当前不持有和持有股票的最大利益
class Solution:
def maxProfit(self , prices: List[int]) -> int:
n = len(prices)
dp = [[0] * 2 for _ in range(n)]
dp[0][0] = 0 #第一天结束不持有
dp[0][1] = - prices[0] #第一天持有
for i in range(1, n):
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])
dp[i][1] = max(dp[i - 1][0] - prices[i], dp[i - 1][1])
return dp[n - 1][0]
写法二:由于dp[i]只跟前一天的状态有关,故可以用两个变量分别表示前一天的dp[i - 1] [0]和dp[i - 1] [1]
class Solution:
def maxProfit(self , prices: List[int]) -> int:
n = len(prices)
dp0 = 0
dp1 = -prices[0]
for i in range(1, n):
dp0 = max(dp0, dp1 + prices[i]) #当天结束不持有
dp1 = max(dp0 - prices[i], dp1)
return dp0
方法二:贪心
class Solution:
def maxProfit(self , prices: List[int]) -> int:
res = 0
for i in range(1, len(prices)):
if prices[i] > prices[i - 1]:
res += prices[i] - prices[i - 1]
return res
dp[i] [0] 表示到第i天为止没有买卖过的最大收益
dp[i] [1] 表示到第i天为止买了一次的最大收益
dp[i] [2] 表示到第i天为止买一次,卖一次的最大收益
dp[i] [3] 表示到第i天为止买两次,卖一次的最大收益
dp[i] [4] 表示到第i天为止买两次,卖两次的最大收益
class Solution:
def maxProfit(self , prices: List[int]) -> int:
n = len(prices)
dp = [[float('-inf')] * 5 for _ in range(n)]
dp[0][0] = 0 #第一天不持有
dp[0][1] = -prices[0] #第一天持有
for i in range(1, n):
dp[i][0] = dp[i - 1][0] #实际上都是0
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]) #分别对应之前买的和当天买的
dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + prices[i])
dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i])
dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i])
return max(dp[n - 1][2], max(0, dp[n - 1][4]))
对于一个长度为 n 字符串,我们需要对它做一些变形。
首先这个字符串中包含着一些空格,就像"Hello World"一样,然后我们要做的是把这个字符串中由空格隔开的单词反序,同时反转每个字符的大小写。
比如"Hello World"变形后就变成了"wORLD hELLO"。
方法一:str.split()函数
class Solution:
def trans(self , s: str, n: int) -> str:
lst = s.split(' ') #就算遇到多空格,用一个空格分割是没问题的,下面再用一个空格拼接;但如果用s.split(''),空格最后都变成单空格了
lst.reverse() #在原来的地址上修改,不能写lst = lst.reverse()
s = ' '.join(lst)
res = []
# for c in s:
# if c.isupper():
# tmp = c.lower() #返回另一个地址,原来的c没有变。所以c.lower(),再res.append(c)是修改不了的
# else:
# tmp = c.upper()
# res.append(tmp)
# return ''.join(res)
return s.swapcase() #直接大小写交换
方法二:
每处理一个就标记是字符还是空格,下一次根据标记来处理
class Solution:
def trans(self , s: str, n: int) -> str:
flag = True
lst = []
tmp = ''
for c in s:
if c != ' ':
if flag == False:
lst.append(tmp)
tmp = c
flag = True
else:
tmp += c
else:
if flag == False:
tmp += c
else:
lst.append(tmp)
tmp = c
flag = False
lst.append(tmp)
lst.reverse()
res = ''.join(lst)
return res.swapcase()
class Solution:
def longestCommonPrefix(self , strs: List[str]) -> str:
if not strs:
return ''
for i in range(len(strs[0])):
for s in strs[1:]:
if i => len(s) or s[i] != strs[0][i]:
return strs[0][:i]
return strs[0]
class Solution:
def solve(self , IP: str) -> str:
numsv4 = IP.split('.')
numsv6 = IP.split(':')
res = ''
if len(numsv4) == 4:
for num in numsv4:
for c in num:
if not '0' <= c <= '9':
res = 'Neither'
break
if res == 'Neither':
break
if not num or (num[0] == '0' and len(num) > 1) or int(num) > 255:
res = 'Neither'
break
if res != 'Neither':
res = 'IPv4'
if res == 'IPv4':
return res
res1 = ''
if len(numsv6) == 8:
for num in numsv6:
if len(num) <= 0 or len(num) >= 5:
res1 = 'Neither'
break
for c in num:
if '0' <= c <= '8' or 'a' <= c <= 'f' or 'A' <= c <= 'F':
continue
else:
res1 = 'Neither'
break
if res1 != 'Neither':
res1 = 'IPv6'
return res1 if res1 == 'IPv6' else 'Neither'
写法一:res = ’ ',不断修改字符串res
class Solution:
def solve(self , s: str, t: str) -> str:
s = [int(c) for c in s]
t = [int(c) for c in t]
res = ''
digi = 1
carry = 0
while s or t:
if s and t:
SUM = s.pop() + t.pop() + carry
carry = SUM // 10
s1 = SUM % 10
res = str(s1) + res
elif s:
SUM = s.pop() + carry
carry = SUM // 10
s1 = SUM % 10
res = str(s1) + res
else:
SUM = t.pop() + carry
carry = SUM // 10
s1 = SUM % 10
res = str(s1) + res
if carry != 0:
res = str(carry) + res
return res
写法二:res = [ ],后面再join(res)
该写法并没有提高空间效率
写法一:时间70%,空间12%
写法二:时间1%,空间6%
class Solution:
def solve(self , s: str, t: str) -> str:
s = [int(c) for c in s]
t = [int(c) for c in t]
res = []
digi = 1
carry = 0
while s or t:
if s and t:
SUM = s.pop() + t.pop() + carry
carry = SUM // 10
s1 = SUM % 10
res.insert(0, str(s1))
elif s:
SUM = s.pop() + carry
carry = SUM // 10
s1 = SUM % 10
res.insert(0, str(s1))
else:
SUM = t.pop() + carry
carry = SUM // 10
s1 = SUM % 10
res.insert(0, str(s1))
if carry != 0:
res.insert(0, str(carry))
return ''.join(res)
class Solution:
def merge(self , A, m, B, n):
i = m - 1
j = n - 1
p = m + n - 1
while i >= 0 and j >= 0:
if A[i] > B[j]:
A[p] = A[i]
i -= 1
else:
A[p] = B[j]
j -= 1
p -= 1
while j >= 0:
A[p] = B[j]
j -= 1
p -= 1
class Solution:
def judge(self , str: str) -> bool:
# write code here
i, j = 0, len(str) - 1
while i < j:
if str[i] != str[j]:
return False
i += 1
j -= 1
return True
当时怎么可能做出来的?
果然刷自然就容易写出来
# class Interval:
# def __init__(self, a=0, b=0):
# self.start = a
# self.end = b
class Solution:
def merge(self , intervals: List[Interval]) -> List[Interval]:
if not intervals:
return []
intervals.sort(key = lambda x: x.start)
res = []
pre = intervals[0]
for cur in intervals[1:]:
if pre.end < cur.start:
res.append(pre)
pre = cur
elif cur.start <= pre.end < cur.end:
pre.end = cur.end
res.append(pre)
return res
写法二:
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals.sort(key = lambda x: x[0])
res = []
for cur in intervals:
if not res or res[-1][1] < cur[0]: #res为空,或者区间不重合,直接添加到结果
res.append(cur) #当前结果的最大值(右)都小于当前区间的最小值(左),不重合
else: #当结果中最后一个元素的右端点大于当前元素的左端点,则说明当前结果包含当前区间
res[-1][1] = max(res[-1][1], cur[1])
return res
方法一:哈希 + 滑动窗口
哈希记录T中的每字符串还缺几个
i, j 分别指向滑动窗口的左右端点。
j 向右扩张,如果窗口满足包含T的条件,则i向右收缩,同时记录最短字串和对应的端点
class Solution:
def minWindow(self , S: str, T: str) -> str:
dic = {}
for c in T:
if c in dic:
dic[c] -= 1 #表示还缺几个字符
else:
dic[c] = -1
def check(dic):
for k, val in dic.items():
if val < 0: #当还缺字符,则不满足
return False
return True
L = len(S) + 1
i = 0
j = 0
left = -1 #用来记录最短区间的左右端点,因为最后要返回字符串而不是最小长度
right = -1
while j < len(S):
if S[j] in dic: #找到一个则加1
dic[S[j]] += 1
while check(dic): #窗口的字符串满足要求
if j - i + 1 < L:
L = j - i + 1
left = i
right = j
if S[i] in dic: #当前收缩的子符是否在T中
dic[S[i]] -= 1 #如果在T中,则减1
i += 1 #左边界收缩
j += 1
if left == -1: #找不到,
return ''
return S[left : right + 1]
class Solution:
def solve(self , str: str) -> str:
i = len(str) - 1
res = []
while i >= 0:
res.append(str[i])
i -= 1
return ''.join(res)
方法一:哈希,记录每个数字的最右边的位置
class Solution:
def maxLength(self , arr: List[int]) -> int:
dic = {}
L = 0
start = 0
res = 0
for i, num in enumerate(arr):
if num in dic and dic[num] >= start:
L = i - dic[num]
start = dic[num] + 1
else:
L += 1
dic[num] = i
res = max(res, L)
return res
写法二:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
dic = {}
res = tmp = 0
for j in range(len(s)):
i = dic.get(s[j],-1) #dic.get获取键s[j]的值,若不存在则返回第二参数-1
dic[s[j]] = j
tmp = tmp + 1 if j - i > tmp else j - i
res = max(tmp, res)
return res
方法二:哈希 + 滑动窗口
设置left、right双指针
class Solution:
def maxLength(self , arr: List[int]) -> int:
dic = {}
left = 0
res = 0
for right in range(len(arr)):
if arr[right] in dic:
dic[arr[right]] += 1
else:
dic[arr[right]] = 1
while dic[arr[right]] > 1: #大于1左边界收缩,不大于1右边界不断增长
dic[arr[left]] -= 1
left += 1
res = max(res, right - left + 1)
return res
关键是容积由最小的高度决定
class Solution:
def maxArea(self , height: List[int]) -> int:
n = len(height)
if n < 2:
return 0
i, j = 0, n - 1
res = min(height[i], height[j]) * (j - i)
while i < j:
if height[i] < height[j]:
i += 1
else:
j -= 1
res = max(res, min(height[i], height[j]) * (j - i))
return res
方法一:双指针
指针指向两边向中间靠,
并且维护左右的最大边界的高度,当指针指向的高度比边界高度低时,对应的雨水单位就是边界减当前高度
class Solution:
def maxWater(self , arr: List[int]) -> int:
i, j = 0, len(arr) - 1
maxL = 0
maxR = 0
res = 0
while i < j:
maxL = max(maxL, arr[i])
maxR = max(maxR, arr[j])
if arr[i] < arr[j]:
res += maxL - arr[i]
i += 1
else:
res += maxR - arr[j]
j -= 1
return res
方法一:两次遍历
从左到右,当前比左边大时,当前的糖果为左边加1
从右到左,当前比右边大,但糖果数比右边小时,当前的糖果为右边加1
class Solution:
def candy(self , arr: List[int]) -> int:
n = len(arr)
nums = [1] * n
for i in range(1, n):
if arr[i] > arr[i - 1]:
nums[i] = nums[i - 1] + 1
res = nums[n - 1]
i = n - 2
while i >= 0:
if arr[i] > arr[i + 1] and nums[i] <= nums[i + 1]: #当左边比右边大,但分到的糖果小于或等于右边
nums[i] = nums[i + 1] + 1
res += nums[i]
i -= 1
return res
方法一:排序+遍历比较
class Solution:
def minmumNumberOfHost(self , n: int, startEnd: List[List[int]]) -> int:
start = []
end = []
for i in range(n):
start.append(startEnd[i][0])
end.append(startEnd[i][1])
start.sort()
end.sort()
res = 0
j = 0
for i in range(n):
if start[i] >= end[j]: #新开始的节目的开始时间大于上一轮(最快要结束)的结束时间,主持人不变
j += 1 #最快要结束的时间变为下一个
else:
res += 1 #增加主持,最快要结束的时间还是原来的j,不变
return res
方法一:切片
class Solution:
def solve(self , n: int, m: int, a: List[int]) -> List[int]:
m = m % n
res = a[n - m :] + a[:n - m]
return res
方法二:三次反转
class Solution:
def solve(self , n: int, m: int, a: List[int]) -> List[int]:
m = m % n
a.reverse()
b = a[:m]
c = a[m:]
b.reverse()
c.reverse()
a[:m] = b
a[m:] = c
return a
class Solution:
def spiralOrder(self , matrix: List[List[int]]) -> List[int]:
if not matrix:
return []
l, r, u, d = 0, len(matrix[0]) - 1, 0, len(matrix) - 1
res = []
while l <= r and u <= d: #等于的时候是要进入循环的,比如[[2, 3]],u == d == 0. 一旦l > r 或者u > d就跳出
for j in range(l, r + 1):
res.append(matrix[u][j])
u += 1
if u > d: #但不满足时就要跳出,否者下面会重复打印。比如matrix= [[2, 3]]时,若不跳出,
break
for i in range(u, d + 1):
res.append(matrix[i][r])
r -= 1
if l > r:
break
for j in range(r, l - 1, -1): #这里会重复打印
res.append(matrix[d][j])
d -= 1
if u > d:
break
for i in range(d, u - 1, -1):
res.append(matrix[i][l])
l += 1
if l > r:
break
return res
方法一:利用辅助矩阵
设原矩阵的元素坐标为i, j,那么在结果中该元素的坐标为 j, n - i - 1
即原来在第几列,则在新数组中第几行。原来在第几行,则在新数组中的倒数第几列
class Solution:
def rotateMatrix(self , mat: List[List[int]], n: int) -> List[List[int]]:
mat_tmp = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(n):
mat_tmp[j][n - i - 1] = mat[i][j]
return mat_tmp
方法二:不用辅助矩阵, 空间O(1)
观察第一列和第一行,
将第一列转置再翻转,则得到结果
#就是转置再按行翻转的结果
class Solution:
def rotateMatrix(self , mat: List[List[int]], n: int) -> List[List[int]]:
# [[1, 2, 3],
# [4, 5, 6],
# [7, 8, 9]]
# [[7, 4, 1], #旋转后的第一行,如果翻转,就是原来的转置
# [8, 5, 2], #所以,将原来的数组转置,再按行翻转就能得到旋转的结果
# [9, 6, 3]]
for i in range(n):
for j in range(i): #注意这里不是到n,如果全部遍历一遍,则每个数都会交换两次,即无变化。
mat[i][j], mat[j][i] = mat[j][i], mat[i][j]
for row in mat:
row.reverse()
return mat
双向链表+哈希
class DlinkedNode:
def __init__(self, key = 0, value = 0):
self.key = key
self.value = value
self.next = None
self.prev = None
class LRUCache:
def __init__(self, capacity: int):
self.cache = {}
self.head = DlinkedNode()
self.tail = DlinkedNode()
self.head.next = self.tail
self.tail.prev = self.head
self.capacity = capacity
self.size = 0
def get(self, key: int) -> int:
if not key in self.cache:
return -1
node = self.cache[key]
self.removeNode(node)
self.addToHead(node)
return node.value
def put(self, key: int, value: int) -> None:
if key in self.cache:
node = self.cache[key]
self.removeNode(node)
self.addToHead(node)
node.value = value
else:
node = DlinkedNode(key, value)
self.cache[key] = node
self.addToHead(node)
self.size += 1
if self.size > self.capacity:
removed = self.removeTail()
self.cache.pop(removed.key)
self.size -= 1
def addToHead(self, node):
node.next = self.head.next
node.prev = self.head
self.head.next = node
node.next.prev = node
def removeNode(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def removeTail(self,):
node = self.tail.prev
# node.prev.next = self.tail
# self.tail.prev = node.prev
self.removeNode(node)
return node
用两个哈希:
# import collections
class Node:
def __init__(self, key, value):
self.freq = 0
self.key = key
self.value = value
self.pre = None
self.next = None
class LFUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.size = 0
self.minFreq = 0
self.freqMap = collections.defaultdict(self.create_linked_list) #如果使用某个键时,该键不存在,则生成该键,值为self.create_linked_list()
self.keyMap = {} #self.freqMap保存的值是双向链表的头和尾
#注意喂进去的函数参数没有括号
def create_linked_list(self):
head = Node(0, 0)
tail = Node(0, 0)
head.next = tail
tail.pre = head
return (head, tail) #元组类似于列表,区别是元组不能修改
def insert(self, node1, node2): #将node2插入到node1后面
node2.pre = node1
node2.next = node1.next
node1.next.pre = node2
node1.next = node2
def delete(self, node):
if node.pre:
node.pre.next = node.next
node.next.pre = node.pre
if node.pre is self.freqMap[node.freq][0] and node.next is self.freqMap[node.freq][1]: #如果node是最后一个元素
self.freqMap.pop(node.freq)
return node.key
def increase(self, node): #插入到下一个频率对应的双链表
node.freq += 1
self.delete(node)
self.insert(self.freqMap[node.freq][-1].pre, node) #插入到尾部,尾部是最新的
if node.freq == 1:
self.minFreq = 1
elif self.minFreq == node.freq - 1: #如果当前处理的node是之前频率最小的,则查看node.freq - 1还有没有节点,如果没有则node.freq就是最小
head, tail = self.freqMap[node.freq - 1]
if head.next is tail:
self.minFreq = node.freq
def get(self, key: int) -> int:
if key in self.keyMap:
self.increase(self.keyMap[key])
return self.keyMap[key].value
return -1
def put(self, key: int, value: int) -> None:
if self.capacity == 0:
return
if key in self.keyMap:
node = self.keyMap[key]
node.value = value
else:
node = Node(key, value)
self.keyMap[key] = node
self.size += 1
if self.size > self.capacity:
self.size -= 1
deleted = self.delete(self.freqMap[self.minFreq][0].next) #删除频率最小的头部缓存
self.keyMap.pop(deleted)
self.increase(node)