劍指offer刷題記錄
LeetCode上的劍指offer題
刷題ing
26.樹的子結構
#1.雙遞歸
class Solution:
def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
'''
先序遍歷樹A中的每個節點An,對應函數isSubStructure(A, B)
判斷樹A中以An爲根節點的子樹是否包含樹B,對應函數helper(A, B)
'''
#有點雙遞歸的意思
#是在說子結構,沒說子樹,B遍歷空之後還可以有A的子節點
if not A or not B:
#空樹不是任意一個樹的子結構
return False
def helper(A,B):
if not B:
return True
if not A:
return False
if A.val==B.val:
return helper(A.left,B.left) and helper(A.right,B.right)
else:
return False
return helper(A,B) or self.isSubStructure(A.left,B) or self.isSubStructure(A.right,B)#找起始點
27.二叉樹的鏡像
#1.遞歸
class Solution:
def mirrorTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
root1 = root.left #存一下左節點之後調用
root.left = self.mirrorTree(root.right)
root.right = self.mirrorTree(root1)
return root
#2.隊列
import collections
class Solution:
def mirrorTree(self, root: TreeNode) -> TreeNode:
q = collections.deque()
q.append(root)
while q:
node = q.popleft()
if not node:
continue
#子樹直接交換
tmp = node.left
node.left=node.right
node.right = tmp
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
return root
#3.棧模擬隊列
class Solution:
def mirrorTree(self, root: TreeNode) -> TreeNode:
s = []
s.append(root)
while s:
node = s[0]
s.pop(0)
if not node:
continue
#子樹直接交換
tmp = node.left
node.left=node.right
node.right = tmp
if node.left:
s.append(node.left)
if node.right:
s.append(node.right)
return root
28.nn對稱的二叉樹
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
#要看子樹想不相同
def helper(left,right):
if not left and not right:
return True
if not left and right or left and not right:
return False
if left.val!=right.val:
#比當前節點的值
return False
return helper(left.left,right.right) and helper(left.right,right.left)
return helper(root,root)
29.順時針打印矩陣
#1.老實人寫法,左-右-下-左-上,保持每一行列移動的邊界更新
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix:
return []
ans = []
m = len(matrix)
n = len(matrix[0])
l = 0
r = n-1
up = 0
down = m-1
while True:
#左→右
ans+=[matrix[up][i] for i in range(l,r+1)]
up += 1
if up>down:
break
#上↓下
ans+=[matrix[i][r] for i in range(up,down+1)]
r -= 1
if r<l:
break
#右←左
ans+=[matrix[down][i] for i in range(r,l-1,-1)]
down -= 1
if up>down:
break
#下↑上
ans+=[matrix[i][l] for i in range(down,up-1,-1)]
l += 1
if l>r:
break
return ans
#2.Py
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
ans = []
while matrix:
ans += list(matrix.pop(0))#取出第一行
matrix = list(zip(*matrix))[::-1]#逆時針rot90度
# A[::-1]這個操作對於行向量可以左右翻轉;對於二維矩陣可以實現上下翻轉
return ans
30.包含min函數的棧
class MinStack:
def __init__(self):
"""
initialize your data structure here.
"""
#這就是那個吧,設置最小棧的那個
self.minstack = [float('inf')]
self.stack = []
def push(self, x: int) -> None:
self.stack.append(x)
self.minstack.append(min(x,self.minstack[-1]))#只加入當前最小值
def pop(self) -> None:
self.stack.pop()
self.minstack.pop()
def top(self) -> int:
return self.stack[-1]
def min(self) -> int:
return self.minstack[-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()
31.棧的壓入、彈出序列
class Solution:
def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
stack = []
#壓棧試試
cnt=0
for i in range(len(pushed)):
stack.append(pushed[i])
while stack and stack[-1]==popped[cnt]:
stack.pop()
cnt+=1
return cnt==len(popped)
32-1.從上到下打印二叉樹
#1.隊列
import collections
class Solution:
def levelOrder(self, root: TreeNode) -> List[int]:
#層序遍歷
q = collections.deque()
q.append(root)
ans = []
while q:
for i in range(len(q)):
node = q.popleft()
if not node:
continue
ans.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
return ans
#2.遞歸
from typing import List
from functools import reduce
from operator import iconcat
class Solution:
def levelOrder(self, root: TreeNode) -> List:
#遞歸,記錄深度,並將同層節點放到同一個數組中
trees = []
self.levelOrderWithDepth(root, 0, trees)
return reduce(iconcat, trees, [])#reduce() 函數會對參數序列中元素進行累積。
def levelOrderWithDepth(self, root: TreeNode, depth: int, trees: [[int]]):
if not root or depth < 0:
return
while len(trees) <= depth:
trees.append([])
trees[depth].append(root.val)
self.levelOrderWithDepth(root.left, depth + 1, trees)
self.levelOrderWithDepth(root.right, depth + 1, trees)
#3.普通遞歸也
from operator import iconcat
class Solution:
def levelOrder(self, root: TreeNode) -> List[int]:
ans = []
def helper(node,depth,ans):
if not node:
return
if len(ans)<=depth:
ans.append([])
ans[depth].append(node.val)
helper(node.left,depth+1,ans)
helper(node.right,depth+1,ans)
helper(root,0,ans)
return reduce(iconcat, ans, [])
32-2.從上到下打印二叉樹 II
#1.dfs
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
ans = []
def helper(node,depth,ans):
if not node:
return
if len(ans)<=depth:
ans.append([])
ans[depth].append(node.val)
helper(node.left,depth+1,ans)
helper(node.right,depth+1,ans)
helper(root,0,ans)
return ans
#2.bfs
import collections
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
ans = []
if not root:
return ans
q = collections.deque()
q.append(root)
while q:
tmp = []
for i in range(len(q)):
node = q.popleft()
if not node:
continue
tmp.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
ans.append(tmp)
return ans
32-3.從上到下打印二叉樹 III
#1.dfs_換湯不換藥
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
ans = []
def helper(node,depth,ans):
if not node:
return
if len(ans)<=depth:
ans.append([])
ans[depth].append(node.val)
helper(node.left,depth+1,ans)
helper(node.right,depth+1,ans)
helper(root,0,ans)
for depth in range(len(ans)):
if depth%2==1:
#奇數行反過來輸出就okk
ans[depth] = ans[depth][::-1]
return ans
#2.bfs_同理
import collections
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
ans = []
if not root:
return ans
q = collections.deque()
q.append(root)
now_depth=0
while q:
tmp = []
for i in range(len(q)):
node = q.popleft()
if not node:
continue
tmp.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
if now_depth%2==1:
ans.append(tmp[::-1])
else:
ans.append(tmp)
now_depth+=1
return ans
33.二叉搜索樹的後序遍歷序列
#1.遞歸
class Solution:
def verifyPostorder(self, postorder: List[int]) -> bool:
#後序的話,最後一個是root,沒法建立唯一的樹,不知道左右子樹的長度
#往回找,比root大就在右子樹,比root小就在左子樹,前邊開始遍歷即可
def helper(pos):
if len(pos)<=1:
#到了子節點了
return True
root = pos[-1]
for i in range(len(pos)):
if pos[i]>root:
break
for j in range(i,len(pos)-1):
if pos[j]<root:
return False
return helper(pos[:i]) and helper(pos[i:-1])
if not postorder:
return True
return helper(postorder)
#2.單調棧輔助迭代
#越往右越大,這樣,就可以構造一個單調遞增的棧,來記錄遍歷的元素。
#往右子樹遍歷的過程,value是越來越大的,一旦出現了value小於棧頂元素value的時候,
#就表示要開始進入左子樹了(如果不是,就應該繼續進入右子樹,否則不滿足二叉搜索樹的定義
class Solution:
def verifyPostorder(self, postorder: [int]) -> bool:
stack, root = [], float("+inf")
for i in range(len(postorder) - 1, -1, -1):
if postorder[i] > root: return False
while(stack and postorder[i] < stack[-1]):
#找到左子樹了,右子樹的節點和root都丟出來
root = stack.pop()#查看左子樹,當前爲root
stack.append(postorder[i])
return True
34.二叉樹中和爲某一值的路徑
#1.標準dfs
'''
用dfs的時候注意記錄path的數組變化,應該在判斷前添加val,到底時判斷,到底且不屬於的話記得pop不然下一個if的時候
數組值會變,path添加到ans裏的時候記得copy,只是變量名的話是貼標籤,之後會變化的!!!
'''
class Solution:
def pathSum(self, root: TreeNode, target: int) -> List[List[int]]:
#不是BST,dfs
if not root:
return []
ans = []
def helper(root,nums):
nums.append(root.val)
if not root.left and not root.right:
#到底
summ=sum(nums)
#print(nums)
if summ==target:
ans.append(nums[:])#nums要拷貝,不能是貼着變量名標籤不然會隨整體變化最後清空
if root.left:
helper(root.left,nums)
if root.right:
helper(root.right,nums)
nums.pop()#走到底且不滿足的話要清理nums防止進入下一個if時nums變化了
helper(root,[])
return ans
35.複雜鏈表的複製
淺拷貝只複製指向某個對象的指針,而不復制對象本身,新舊對象還是共享同一塊內存。但深拷貝會另外創造一個一模一樣的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象。
#1.Hash
#On,On
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
#hash
if not head:
return head
memo = {None:None}#不能用dict()這樣初始化,不然需要判斷q.random!=None,畢竟dict的key一般不能爲None
cur = head
#hashmap裏存儲(原節點,copy節點)的映射
while cur:
memo[cur] = Node(cur.val)
cur = cur.next
cur = head
#映射的值與值連接,節點組成新的鏈表
while cur:
memo[cur].next = memo[cur.next]
memo[cur].random = memo[cur.random]
cur = cur.next
return memo[head]
#2.原地修改(間隔節點法)
#On,O1
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
#原地修改
if not head:
return head
cur = head
copy = None
#將拷貝節點放到原節點後面,例如1->2->3這樣的鏈表就變成了這樣1->1'->2->2'->3->3'
while cur:
copy = Node(cur.val)
copy.next = cur.next
cur.next = copy
cur = cur.next.next
cur = head
#把拷貝節點的random指針安排上
#複製的random就是上一個節點的random的next
while cur:
if cur.random:
cur.next.random = cur.random.next#此時cur.next就都是copy節點了
cur = cur.next.next
newhead = head.next#copy第一個節點位置
#分離拷貝節點和原節點,變成1->2->3和1'->2'->3'兩個鏈表,後者就是答案
#想拉拉鍊一樣,咔咔咔咔咔咔
cur = head
tmp = None
while cur and cur.next:
tmp = cur.next
cur.next = tmp.next
cur = tmp
return newhead
#3.dfs
#本質與hashmap很類似
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
def dfs(head):
if not head: return None
if head in visited:
return visited[head]#return copy的節點
# 創建新結點
copy = Node(head.val, None, None)
visited[head] = copy
copy.next = dfs(head.next)#連到dfs的copy節點上
copy.random = dfs(head.random)
return copy
visited = {}
return dfs(head)
36.二叉搜索樹與雙向鏈表
#1.遞歸中序+頭結點設定+最後建立循環
class Solution:
def treeToDoublyList(self, root: 'Node') -> 'Node':
def dfs(cur):
if not cur: return
dfs(cur.left) # 遞歸左子樹
if self.pre: # 修改節點引用
self.pre.right, cur.left = cur, self.pre
else: # 記錄頭節點
self.head = cur
self.pre = cur # 保存 cur
dfs(cur.right) # 遞歸右子樹
if not root: return
self.pre = None
dfs(root)
self.head.left, self.pre.right = self.pre, self.head#首尾連接
return self.head
#2.迭代中序+建立啞結點標記頭部,首尾連接跳過dummy
class Solution:
def treeToDoublyList(self, root: 'Node') -> 'Node':
#雙向鏈表也和tree一樣,是left和right標向
#升序的話就是前=中序遍歷
if not root:
return None
dummy = Node(0)
last = dummy
stack = [(0,root)]
while stack:
opt,node = stack.pop()
if not node:
continue
if opt==1:
#print(last.right,dummy.right)#神奇的是第一次last賦值後dummy被掛在了頭結點左邊,標籤行爲轉移到本體上
#淺拷貝只是增加了一個指針指向已經存在的內存,而深拷貝就是增加一個指針並且申請一個新的內存
#按道理來講用none來init加個判斷連到head上會比較好
last.right = node
node.left = last
last = node
else:#更早的關係會被提前壓入棧
stack.append((0,node.right))
stack.append((1,node))
stack.append((0,node.left))
dummy.right.left = last
last.right= dummy.right
return dummy.right
37.序列化二叉樹
#1.井號層序遍歷隔開+恢復時注意邊界,隊列的元素對應一左一右倆
class Codec:
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
q = collections.deque()
ans = []
q.append(root)
while q:
for i in range(len(q)):
node = q.popleft()
if not node:
ans.append('null')
continue
ans.append(str(node.val))
q.append(node.left)
q.append(node.right)
return "#".join(ans)
def deserialize(self, data):
data = data.split("#")
#print(data)
if data[0]=='null':
return None
q = collections.deque()
root = TreeNode(int(data[0]))
q.append(root)
i = 1
while q:
node = q.popleft()
if not node:
continue
node.left = TreeNode(int(data[i])) if data[i]!='null' else None
node.right = TreeNode(int(data[i+1])) if data[i+1]!='null' else None
i += 2
q.append(node.left)
q.append(node.right)
return root
38.字符串的排列
#1.dfs+字符串先排序,重複字符不在同一位置進行dfs
class Solution:
def permutation(self, s: str) -> List[str]:
ans = []
def dfs(comb,s):
if len(s) == 0:
#一個單詞滿了
ans.append("".join(comb))
return
else:
#一個單詞沒填滿
for i in range(len(s)):
if i==0:
dfs(comb+[s[i]],s[:i]+s[i+1:])
if i>0 and s[i]!=s[i-1]:
dfs(comb+[s[i]],s[:i]+s[i+1:])
s = "".join(sorted(s))
dfs([],s)
return ans
#2.dfs用dict、set一類剪枝
class Solution:
def permutation(self, s: str) -> List[str]:
c, res = list(s), []
def dfs(x):
if x == len(c) - 1:
res.append(''.join(c)) # 添加排列方案
return
dic = set()
for i in range(x, len(c)):
if c[i] in dic: continue # 重複,因此剪枝
dic.add(c[i])
c[i], c[x] = c[x], c[i] # 交換,將 c[i] 固定在第 x 位
dfs(x + 1) # 開啓固定第 x + 1 位字符
c[i], c[x] = c[x], c[i] # 恢復交換
dfs(0)
return res
39.數組中出現次數超過一半的數字
#1.hashmap
class Solution:
def majorityElement(self, nums: List[int]) -> int:
n = len(nums)//2
memo = {}
for i in range(len(nums)):
memo[nums[i]] = memo.get(nums[i],0)+1
if memo[nums[i]]>n:
return nums[i]
#2.排序後一定在中間
class Solution:
def majorityElement(self, nums: List[int]) -> int:
nums.sort()
return nums[len(nums)//2]
#3.摩爾投票法
class Solution:
def majorityElement(self, nums: List[int]) -> int:
#摩爾投票法:正負抵消,剩餘即爲衆數
votes = 0
for num in nums:
if votes == 0: x = num
votes += 1 if num == x else -1
return x
40. 最小的k個數
#1.Pysort
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
arr.sort()
return arr[:k]
#2.寫個快排
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
#使用 partition 過程找到下標爲 k - 1 的那個數即可
def quicksort(nums,l,r,target):
if l<r:
i = l
j = r
key = nums[l]
while i<j:
while i<j and nums[j]>=key:
j-=1
if i<j:
nums[i]=nums[j]
i+=1
while i<j and nums[i]<=key:
i+=1
if i<j:
nums[j]=nums[i]
j-=1
nums[i]=key
if i<target:
quicksort(nums,i+1,r,target)
else:
quicksort(nums,l,i-1,target)
quicksort(arr,0,len(arr)-1,k-1)
return arr[:k]
#3.寫個堆
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
if k == 0:
return list()
hp = [-x for x in arr[:k]]#因爲py是最小堆所以取反數,除掉k以上大的值,之後的堆取反數輸出,堆大小爲k
heapq.heapify(hp)
for i in range(k, len(arr)):
if -hp[0] > arr[i]:
heapq.heappop(hp)
heapq.heappush(hp, -arr[i])
ans = [-x for x in hp]
return ans
41.數據流中的中位數
#1.Pysort
class MedianFinder:
def __init__(self):
"""
initialize your data structure here.
"""
self.stack = []
def addNum(self, num: int) -> None:
self.stack.append(num)
self.stack.sort()
def findMedian(self) -> float:
if len(self.stack)%2==0:
mid = len(self.stack)//2
return (self.stack[mid-1]+self.stack[mid])/2
else:
mid = len(self.stack)//2
return self.stack[mid]
# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()
42.連續子數組的最大和
#1.dpdp
'''
如果和是負數,那就從裏邊選個小點的負數:重新計數的環節
如果和是正數,那就先記錄下來在擴大範圍看看有沒有更大的正數
'''
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
#dpdp
mm = nums[0]
dp = [0]*(len(nums))
dp[0]=nums[0]
for i in range(1,len(nums)):
dp[i] = max(nums[i],dp[i-1]+nums[i])#dp[i-1]有可能小於0
#dp[i-1]<0的場合:這裏dp存的並不是當前最大和,而是看是否從i重新開始計數
#dp[i-1]>0的場合:先記下帶nums[i]的,無論nums[i]爲正負,mm負責記錄max值
if dp[i]>mm:
mm = dp[i]
return mm
#2.dpdp_Py空間優化
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
for i in range(1, len(nums)):
nums[i] += max(nums[i - 1], 0)#在nums數組上改,O1,到i時,使用的i還沒變化,i-1記錄dp的
return max(nums)
#2.大根堆+小根堆
import heapq
class MedianFinder:
'''
把數據分爲兩部分,讓左半部分永遠元素個數永遠大於等於右半部分,
這樣左端大頂堆的堆頂就是左半部分最大值,右端小頂堆的堆頂就是右半部分最小值。
'''
def __init__(self):
self.right = [] # 小頂堆,保存較大的一半
self.left = [] # 大頂堆,保存較小的一半,負的小頂堆
def addNum(self, num: int) -> None:
'''
Push item on the heap, then pop and return the smallest item from the heap.
The combined action runs more efficiently than heappush() followed by a separate call to heappop().
當 m = n(即N爲偶數):需向A添加一個元素。實現方法:將新元素num插入至B,再將B堆頂元素插入至A ;
當 N爲奇數:需向B添加一個元素。實現方法:將新元素num插入至A,再將 A堆頂元素插入至B ;
if len(self.A) != len(self.B):
heappush(self.A, num)
heappush(self.B, -heappop(self.A))
else:
heappush(self.B, -num)
heappush(self.A, -heappop(self.B))
'''
if len(self.left) != len(self.right):
#奇數?
heappush(self.left, -heappushpop(self.right, num))
else:
#偶數?
heappush(self.right, -heappushpop(self.left, -num))
def findMedian(self) -> float:
#永遠是right多存一點,left是負數注意
return self.right[0] if len(self.right) != len(self.left) else (self.right[0] - self.left[0]) / 2.0
43.1~n整數中1出現的次數
#1.找規律,位數從低到高
'''
當 cur = 0時: 此位 1 的出現次數只由高位 high和位數決定:high*i
當 cur = 1時: 此位 1 的出現次數由高位 high ,位數和低位 low 決定,計算公式爲:high×digit+low+1相當於整數0以下的部分以外多了一個1以及對應低位帶來的部分
當 cur=2,3,⋯,9 時: 此位 1 的出現次數只由高位 high 決定,計算公式爲:(high+1)×digit,0以上1的位數個肯定是有了
'''
class Solution:
def countDigitOne(self, n: int) -> int:
cnt = 0
i = 1
while n//i !=0:
high = n//(10*i)#位數,高位
cur = (n//i)%10#餘數,當前位i
low = n-(n//i)*i#除法取整殘差,低位
if cur==0:
cnt+=high*i
elif cur==1:
cnt+=high*i+low+1
else:
cnt+=(high+1)*i
i = i*10
return cnt
#2.遞歸,位數從高到低
class Solution:
def countDigitOne(self, n: int) -> int:
return self.dfs(n)
def dfs(self,n):
if n<=0: return 0
num_s = str(n)
high = int(num_s[0])
Pow = 10**(len(num_s)-1)
last = n - high*Pow
if high == 1:
return self.dfs(Pow-1)+self.dfs(last)+last+1
else:
return Pow+high*self.dfs(Pow-1)+self.dfs(last)
44.數字序列中某一位的數字
class Solution:
def findNthDigit(self, n: int) -> int:
i=0
last = 0
while n>0:
last = n
n-=(10**(i))*9*(i+1)
i+=1
#i就是當前所求數的位數,恢復n到循環的位置
n = last
start = 10**(i-1)
end = str(start+(n-1)//i)
num = end[(n-1)%i]#n-1的話就是從新的數開始計位了,從start開始個數和計位都是
return int(num)
45.把數組排成最小的數
#本來想用全排列選一下最小值的,但是測試用例比intmaxsize還大,這也就是說需要字符串比較大小
#1.字符串比較大小
#sorted key 不僅可以傳lamda , 普通函數 , 還可以傳有實現比較的類
class smallnumkey(str):
def __lt__(x,y):
return x+y<y+x
class Solution:
def minNumber(self, nums: List[int]) -> str:
ans = "".join(sorted(map(str,nums),key=smallnumkey))
return ans
#2.還是比較大小
#沒用class,用了快排
class Solution:
def minNumber(self, nums: List[int]) -> str:
def fast_sort(l , r):
if l >= r: return
i, j = l, r
while i < j:
while strs[j] + strs[l] >= strs[l] + strs[j] and i < j: j -= 1
while strs[i] + strs[l] <= strs[l] + strs[i] and i < j: i += 1
strs[i], strs[j] = strs[j], strs[i]
strs[i], strs[l] = strs[l], strs[i]
fast_sort(l, i - 1)
fast_sort(i + 1, r)
strs = [str(num) for num in nums]
fast_sort(0, len(strs) - 1)
return ''.join(strs)
46.把數字翻譯成字符串
#1.dpdp
#邊界條件注目
#小青蛙爬樓梯類問題
#還可以進一步空間優化就是了,-2:prepre,-1:pre,0:now這樣三個值存儲節省空間
class Solution:
def translateNum(self, num: int) -> int:
#分組,檢查是否valid,+1
ans = 1#單數字字母 #雙數字字母 #感覺很dpdp
num = str(num)
dp = [1]*(len(num))
if len(num)>=2 and int(num[0]+num[1])<=25:
dp[1]=2
for i in range(2,len(num)):
tmp = int(num[i-1]+num[i])
if tmp<=25 and tmp>=10:
dp[i] = dp[i-1]+dp[i-2]#1:和前一個數組成兩位的字符,2:單獨作爲新的一個字符
else:
dp[i] = dp[i-1]#沒法和前一個數組成兩位的字符,拼法沒有增多
return dp[-1]
#2.遞歸
class Solution:
def translateNum(self, num: int) -> int:
self.ans = 0
def helper(nums):
if len(nums)==0:
self.ans+=1
return
helper(nums[1:])#1的場合
if len(nums)>=2:
if nums[0]=='0' or nums[:2]>'25':
return
helper(nums[2:])#2的場合
helper(str(num))
return self.ans
47.禮物的最大價值
#1.dfs超時了,避免重複搜索de記憶存儲法基本上就是dp,那還不如dp
class Solution:
def maxValue(self, grid: List[List[int]]) -> int:
#dpdp,或者dfs兩方向遍歷也行
#直接用dfs結果超時了,看看剪枝
self.ans = 0
def dfs(grid,i,j,tmp):
tmp+=grid[i][j]
if i==len(grid)-1 and j==len(grid[0])-1:
#到角落了
self.ans = max(self.ans,tmp)
return
if i+1<len(grid):
dfs(grid,i+1,j,tmp)
if j+1<len(grid[0]):
dfs(grid,i,j+1,tmp)
dfs(grid,0,0,0)
return self.ans
#2.dpdp
class Solution:
def maxValue(self, grid: List[List[int]]) -> int:
m =len(grid)
n =len(grid[0])
dp = [[0]*n for _ in range(m)]
dp[0][0]=grid[0][0]
for j in range(1,n):
#右
dp[0][j]=dp[0][j-1]+grid[0][j]
for i in range(1,m):
#下
dp[i][0]=dp[i-1][0]+grid[i][0]
for j in range(1,n):
dp[i][j] = max(dp[i-1][j],dp[i][j-1])+grid[i][j]
return dp[-1][-1]
48.最長不含重複字符的子字符串
#1.雙指針哈希
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
last_idx=-1
ans = 0
memo = dict()
for i in range(len(s)):
if s[i] in memo and memo[s[i]]>last_idx:
#出現重複字符,且字符位置大於當前記錄子串的起始idx,得是在現在的子串裏重複的字符
#一換一,ans不用更新
last_idx = memo[s[i]]
memo[s[i]]=i
else:
memo[s[i]]=i
ans = max(ans,i-last_idx)#上一個重複字開始的新位置到i長度,abca的話就是b2-a[-1]=3
return ans
#2.空間優化dp哈希
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
dic = {}
res = tmp = 0
for j in range(len(s)):
i = dic.get(s[j], -1) # 獲取索引 i,沒有就返回-1
dic[s[j]] = j # 更新哈希表
'''
dp記錄當前子串的長度
1;dp[j-1]<j-i,子串可以延長,tmp+1
2.dp[j-1]>=j-i,子串長度變不動了,遇到相同字符了,當前子串j-i長度
'''
tmp = tmp + 1 if tmp < j - i else j - i # dp[j - 1] -> dp[j]
res = max(res, tmp) # max(dp[j - 1], dp[j])
return res