劍指offer刷題記錄
LeetCode上的劍指offer題
刷題ing
49. 醜數
#1.dp_轉化成數組合並問題,用上個狀態的數
class Solution:
def nthUglyNumber(self, n: int) -> int:
#醜數的規律,從前向後看,每個數都是前邊的乘以2,3,5得到的
#三指針:三個有序數組(*2,*3,*5)無重複元素合併
if not n:
return 0
ugly=[1]*n
i,j,k=0,0,0
for idx in range(1,n):
tmp = min(ugly[i]*2,ugly[j]*3,ugly[k]*5)
if tmp==ugly[i]*2:
i+=1
if tmp==ugly[j]*3:
j+=1
if tmp==ugly[k]*5:
k+=1
ugly[idx]=tmp
return ugly[-1]
#2.堆&優先隊列_每加一個元素自動排序_用set去重
from queue import PriorityQueue
class Solution:
def nthUglyNumber(self, n: int) -> int:
# 集合+優先隊列
pq = PriorityQueue()#其實就是堆啦
rec = set()#set去重
pq.put(1)
rec.add(1)
i = 1
while True:
x = pq.get()
if i == n:
return x
i+=1
for k in 2*x, 3*x, 5*x:
if k not in rec:
rec.add(k)
pq.put(k)#自動排序
return -1
50.第一個只出現一次的字符
#1.一般計數器
#Python 3.6 後,默認字典就是有序的,因此無需使用 OrderedDict()
class Solution:
def firstUniqChar(self, s: str) -> str:
memo = {}
for l in s:
if l in memo:
memo[l]+=1
else:
memo[l]=1
for key in memo:
if memo[key]==1:
return key
else:
return ' '
51.數組中的逆序對
#1.merge
class Solution:
def mergeSort(self, nums, tmp, l, r):
#用merge利用數組的部分有序性
if l >= r:
return 0
mid = (l + r) // 2
#l,mid//mid+1,r,子數組的逆序對個數
inv_count = self.mergeSort(nums, tmp, l, mid) + self.mergeSort(nums, tmp, mid + 1, r)
i, j, pos = l, mid + 1, l
#i是左半個有序序列指針,j是右半有序序列的指針,tmp是新創建數組,pos是其指針
while i <= mid and j <= r:
if nums[i] <= nums[j]:
tmp[pos] = nums[i]#左邊的小,放進去
i += 1
inv_count += (j - (mid + 1))#其實是在r的元素被加進去下一步到加l元素的時候纔會記錄下來,本來是看j的元素小,放進去的時候i這邊還有幾個沒放,理解成放i的時候看已經放了幾個j進去了也是okk的
else:
#逆序對出現了
tmp[pos] = nums[j]
j += 1
pos += 1
#剩下的清掉
for k in range(i, mid + 1):
tmp[pos] = nums[k]
inv_count += (j - (mid + 1))
pos += 1
for k in range(j, r + 1):
tmp[pos] = nums[k]
pos += 1
nums[l:r+1] = tmp[l:r+1] #這樣nums就部分排好序了
return inv_count
def reversePairs(self, nums: List[int]) -> int:
n = len(nums)
tmp = [0] * n
return self.mergeSort(nums, tmp, 0, n - 1)
#2.樹狀數組
from typing import List
class Solution:
def reversePairs(self, nums: List[int]) -> int:
class FenwickTree:
#樹狀數組搭建
def __init__(self, n):
self.size = n
self.tree = [0 for _ in range(n + 1)]
def __lowbit(self, index):
return index & (-index)
# 單點更新:從下到上,最多到 len,可以取等
def update(self, index, delta):
while index <= self.size:
self.tree[index] += delta
index += self.__lowbit(index)
# 區間查詢:從上到下,最少到 1,可以取等
def query(self, index):
res = 0
while index > 0:
res += self.tree[index]
index -= self.__lowbit(index)
return res
# 特判
size = len(nums)
if size < 2:
return 0
# 原始數組去除重複以後從小到大排序,這一步叫做離散化
s = list(set(nums))
# 構建最小堆,因爲從小到大一個一個拿出來,用堆比較合適
import heapq
heapq.heapify(s)
# 由數字查排名
rank_map = dict()
rank = 1
# 不重複數字的個數
rank_map_size = len(s)
for _ in range(rank_map_size):
num = heapq.heappop(s)
rank_map[num] = rank
rank += 1
res = 0
# 樹狀數組只要不重複數字個數這麼多空間就夠了
ft = FenwickTree(rank_map_size)
# 從後向前看,拿出一個數字來,就更新一下,然後向前查詢比它小的個數
for i in range(size - 1, -1, -1):
rank = rank_map[nums[i]]
ft.update(rank, 1)
res += ft.query(rank - 1)
return res
52.兩個鏈表的第一個公共節點
#1.浪漫相遇
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
'''
1:有交集:相交鏈表連着到尾端,A與B交長度c,剩下a和b,有a+c+b = b+c+a,總能在交叉第一個相遇
2:無交集,a+b=b+a,兩個一起null,返回null
'''
p1 = headA
p2 = headB
while p1!=p2:
p1 = headB if not p1 else p1.next
p2 = headA if not p2 else p2.next
return p1
#2.hashmap看相同節點有無
#不滿足空間O(1)
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
memo = {}
p1 = headA
p2 = headB
while p1:
memo[p1]=1
p1 = p1.next
while p2:
if p2 in memo:
return p2
p2 = p2.next
return None
53-1.在排序數組中查找數字 I
#1.二分,注意and的前後判斷順序,邊界判斷在先
lass Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums or len(nums)==0:
return 0
#暴力不可,二分
i = 0
j = len(nums)-1
cnt = 0
while i<j:
mid = (j-i)//2+i
if nums[mid]<target:
i = mid+1
else:
j = mid
#找到了左起點
cnt = 0
while i<len(nums) and nums[i]==target:
cnt+=1
i+=1
return cnt
53-2.0~n-1中缺失的數字
#1.暴力遍歷
class Solution:
def missingNumber(self, nums: List[int]) -> int:
if nums == [] or nums[0]!=0:
return 0
for i in range(1,len(nums)):
if nums[i]!=i:
return i
return len(nums)
#2.二分(有序就想到二分,畢竟時間複雜度低)
class Solution:
def missingNumber(self, nums: List[int]) -> int:
i=0
j = len(nums)
while i<j:
mid = (i-j)//2+j
if nums[mid]==mid:
i=mid+1
else:
j=mid
return i
#3.異或(更加適用於無序的數組)
class Solution:
def missingNumber(self, nums: List[int]) -> int:
ans = len(nums)
for i in range(0,len(nums)):
#其實還是看i和numsi等不等
#如果中間缺了一個,那麼肯定其他的到最後的連續異或了都能抵消成0(ans初始是len(nums)剛好是最後一個元素))
#而剩下一個i,就是最後返回的缺失值
#如果是末尾缺失,那麼剛好就是len(nums)
ans^=nums[i]
ans^=i
return ans
54.二叉搜索樹的第k大節點
#1.一般迭代中序
class Solution:
def kthLargest(self, root: TreeNode, k: int) -> int:
#中序遍歷,返回-k
stack = [(0,root)]
inorder = []
while stack:
opt,node = stack.pop()
if not node:
continue
if opt==1:
inorder.append(node.val)
else:
stack.append((0,node.right))
stack.append((1,node))
stack.append((0,node.left))
return inorder[-k]
#2.遞歸下加個計數器也可以
class Solution:
def kthLargest(self, root: TreeNode, k: int) -> int:
#中序-計數k,右-中-左逆着記
self.ans = 0
self.cnt=0
def helper(root,k):
if root.right:
helper(root.right,k)
self.cnt+=1
if self.cnt==k:
self.ans = root.val
return
if root.left:
helper(root.left,k)
helper(root,k)
return self.ans
#3.老實人做法:借用BST結構特性
'''
先計算右子樹的節點數爲 r_num,那麼根節點是第 r_num+1 大的節點。如果r_num+1=k,則返回root的val值;如果r_num+1<k,則在左子樹中找第 k-r_num-1 大的節點;如果r_num+1>k,則在右子樹找第k大的節點。遞歸查找。
'''
class Solution:
def kthLargest(self, root: TreeNode, k: int) -> int:
if not root:
return 0
def helper(root):
if not root:
return 0
return helper(root.left)+helper(root.right)+1
r_num = helper(root.right)
if r_num+1==k:
return root.val
elif r_num+1<k:
return self.kthLargest(root.left,k-r_num-1)
else:
return self.kthLargest(root.right,k)
return 0
55-1.二叉樹的深度
#1.一般遞歸
class Solution:
def maxDepth(self, root: TreeNode) -> int:
def helper(node):
if not node:
return 0
max_left=helper(node.left)
max_right=helper(node.right)
return max(max_left,max_right)+1
return helper(root)
#2.dfs
class Solution:
def maxDepth(self, root: TreeNode) -> int:
self.depth=0
def dfs(node,level):
if not node:
return 0
if self.depth<level:
self.depth+=1
dfs(node.left,level+1)
dfs(node.right,level+1)
dfs(root,1)
return self.depth
#3.bfs_就是層序遍歷下
import collections
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if not root:
return 0
depth = 0
q = collections.deque()
q.append(root)
while q:
depth+=1
for i in range(len(q)):
node = q.popleft()
if not node:
continue
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
return depth
55-2.平衡二叉樹
#1.一般遞歸,需返回高度
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
#在記錄TF的時候還得記錄高度,之後對比用
def helper(root):
if not root:
return True,-1
left_balance,left_height = helper(root.left)
if not left_balance:
return False,0
right_balance,right_height = helper(root.right)
if not right_balance:
return False,0
if abs(left_height-right_height)>1:
return False,0
else:
return True,max(left_height,right_height)+1
return helper(root)[0]
#2.後序_直接用height判斷
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
return self.treeHeight(root) >= 0
def treeHeight(self, root):
if not root:
return 0
leftHeight = self.treeHeight(root.left)
rightHeight = self.treeHeight(root.right)
if leftHeight >= 0 and rightHeight >= 0 and abs(leftHeight - rightHeight) <= 1:
return max(leftHeight, rightHeight) + 1
else:
return -1 # -1表示不平衡
56-1.數組中數字出現的次數
#1.一般位運算
class Solution:
def singleNumbers(self, nums: List[int]) -> List[int]:
#hash計數兩次遍歷空間不滿足
#排序再遍歷估計行
#果然是異或了嗎
ans = 0
for i in range(len(nums)):
ans ^= nums[i]
#這時候得到的是那兩個數的異或
#1,6就是7;2,10就是8,爲1的某一位就可以區分這兩個數
idx=0
while ans&1==0:
#找到異或裏第一個爲1的位數
idx+=1
ans>>=1
left = 0
right = 0
for i in range(len(nums)):
if (nums[i]>>idx)&1==0:
#這一位上是0
left^=nums[i]
else:
right^=nums[i]
return [left,right]
'''
找到異或裏第一個爲1的位數那一步其實是lowbit
用ans&(-ans)就行
'''
56-2.數組中數字出現的次數 II
#1.hash計數
class Solution:
def singleNumber(self, nums: List[int]) -> int:
memo = {}
for i in range(len(nums)):
if nums[i] not in memo:
memo[nums[i]]=1
else:
memo[nums[i]]+=1
for num in memo:
if memo[num]==1:
return num
#2.Py_去重_數學
class Solution:
def singleNumber(self, nums: List[int]) -> int:
return (sum(set(nums))*3-sum(nums))//2
#3.數電法1,32位計位規律
'''
通過對數組中各個數的二進制表示形式逐位進行觀察,我們可以發現,當數組中只出現一次的那個數字(用k表示)
在二進制的對應位爲0時,該對應位爲1在數組各個數字中出現的總次數應當爲3^n
當k的對應位爲1時,該對應位爲1在數組各個數字中出現的總次數應當爲3^n+1,
爲此,我們可以統計數字中的各個位中1出現的次數,當爲3^n 次時,只出現一次的數字的對應位應當爲0,
當爲3^n + 1次時,只出現一次的數字的對應位應當爲1。
'''
class Solution:
def singleNumber(self, nums: List[int]) -> int:
stack = [0]*32
for num in nums:
for i in range(32):
stack[i] += 1 if (num&(1<<i))!=0 else 0
ans = 0
for i in range(32):
ans+=(1<<i)*(stack[i]%3)
return ans
#4.數電法2,位運算優化
'''
實際上,我們只需要記錄對應位出現的次數爲0、1、2次的情況,當對應位出現次數爲3的時候,
我們便可以將該位出現的次數置爲0,重新開始進行計數。由於int型中的各個二進制位出現的次數爲3進制的,
爲此我們需要兩個位來記錄各個位出現的次數,由此我們需要引入兩個變量a,b來統計對應位出現的次數。由ab兩個變量組合起來來記錄各個二進制位出現爲1的情況。變量a表示高位的情況,變量b表示低位的情況,而在遍歷數組運算完成之後,遍歷b的值便是答案。
a’=1,b‘=0,新位=1,此時a=0,b=0,的這樣一種三進製表示。
真值表推邏輯表達式:輸出1對應行看輸入,0是非,1是真
'''
class Solution:
def singleNumber(self, nums: List[int]) -> int:
a = 0
b = 0
for num in nums:
a = (a^num)&~b
b = (b^num)&~a
return a
57.和爲s的兩個數字
#1.雙指針 O(N) O(1)
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
#雙指針
i = 0
j = len(nums)-1
while i<j:
summ = nums[i]+nums[j]
if summ == target:
return [nums[i],nums[j]]
if summ<target:
i+=1
else:
j-=1
return None
#2.二分_超慢
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
#二分
for i in range(len(nums)):
out = target-nums[i]
l = 0
r = len(nums)-1
while l<r:
mid = (l-r)//2+r
if nums[mid] == out:
return [nums[i],nums[mid]]
if nums[mid]<out:
l = mid+1
else:
r = mid
57-2.和爲s的連續正數序列
#1.前綴和_遍歷
class Solution:
def findContinuousSequence(self, target: int) -> List[List[int]]:
#連續正整數序列,前綴和???
ans = []
end = target//2+1 #9--5;15--8
presum = [0]*(end+1)
nums = list(range(end+1))
for i in range(len(nums)):
presum[i] = presum[i-1]+nums[i]
#print(presum)
for i in range(len(nums)):
j = i+1
while j<len(nums) and presum[j]-presum[i]<target:
j+=1
if j>=len(nums) or presum[j]-presum[i]>target:
continue
if presum[j]-presum[i]==target:
#print(i,j)
ans.append(list(range(i+1,j+1)))
return ans
#2.數學優化前綴和-滑窗
class Solution:
def findContinuousSequence(self, target: int) -> List[List[int]]:
#數學優化前綴和+滑窗
ans = []
i = 1
j = 2
while i<j:
summ = (i+j)*(j-i+1)//2 #首項末項項數除2
if summ<target:
j+=1
elif summ>target:
i+=1
else:
ans.append(list(range(i,j+1)))
i+=1
return ans
#3.純數學法
'''
(x+y)∗(y−x+1)//2=target 解方程
y^2+y−x^2+x−2∗target=0 視y爲變量
a=1,b=1,c=-x^2+x-2*target 套公式
判斷是否整數解需要滿足兩個條件:
判別式 b^2-4ac 開根需要爲整數
最後的求根公式的分子需要爲偶數,因爲分母爲2
'''
class Solution:
def findContinuousSequence(self, target: int):
# 創建輸出列表
res = []
# y不能超過target的中值,即y<=target//2 + 1,range函數左開右閉,所以這裏是+2
for y in range(1,target//2 + 2):
# 應用我們的求根公式
x = (1/4 + y**2 + y - 2 * target) ** (1/2) + 0.5
# 我們要確保x不能是複數,且x必須是整數
if type(x) != complex and x - int(x) == 0:
res.append(list(range(int(x),y+1)))
return res
#4.間隔法
#複雜度O(√target)
'''
首項x末項x+i間隔i
2xi+2x+i^2+i=2t
t=x(i+1)+i(i+1)/2
x = (t-(i*(i+1)/2))/(i+1)
條件1: x必須是正整數,所以i(i+1)/2 要小於t,否則就會出現負數。
條件2: (t-(i*(i+1)/2))/(i+1)必須是整數
'''
class Solution:
def findContinuousSequence(self, target: int) -> List[List[int]]:
# 我們的間隔從1開始
i, res = 1, []
# 根據上面的條件1,限定i的大小,即間隔的範圍
while i*(i+1)/2 < target:
# 根據條件2,如果x不爲整數則擴大間隔
if not (target - i*(i+1)/2) % (i+1):
# 如果兩個條件都滿足,代入公式求出x即可,地板除//會把數改成float形式,用int()改回來
x = int((target - i*(i+1)/2) // (i+1))
# 反推出y,將列表填入輸出列表即可
res.append(list(range(x,x+i+1)))
# 當前間隔判斷完畢,檢查下一個間隔
i += 1
# 由於間隔是從小到大,意味着[x,y]列表是從大到小的順序放入輸出列表res的,所以反轉之
return res[::-1]
58-1.翻轉單詞順序
#1.Py_str法
class Solution:
def reverseWords(self, s: str) -> str:
s1 = s.strip().split(' ')
s2 = []
for i in range(len(s1)-1,-1,-1):
if s1[i]=="" or s1[i]==" ":
continue
s2.append(s1[i].strip())
return " ".join(s2)
#或者
class Solution:
def reverseWords(self, s: str) -> str:
return ' '.join(s.strip().split()[::-1])
#2.雙指針_後向前遍歷
class Solution:
def reverseWords(self, s: str) -> str:
s = s.strip() # 刪除首尾空格
i = j = len(s) - 1 #從結尾開始遍歷
res = []
while i >= 0:
while i >= 0 and s[i] != ' ': i -= 1 # 搜索首個空格
res.append(s[i + 1: j + 1]) # 添加單詞
while s[i] == ' ': i -= 1 # 跳過單詞間空格
j = i # j 指向下個單詞的尾字符
return ' '.join(res) # 拼接並返回
58-2.左旋轉字符串
#1.Py切片
class Solution:
def reverseLeftWords(self, s: str, n: int) -> str:
return s[n:]+s[:n]
#2.騷操作_取餘
class Solution:
def reverseLeftWords(self, s: str, n: int) -> str:
res = []
for i in range(n, len(s)):
res.append(s[i])
for i in range(n):
res.append(s[i])
return ''.join(res)
#3.三次翻轉
class Solution:
def reverseLeftWords(self, s: str, n: int) -> str:
if n > len(s) or not s:
return ''
s = list(s)
def reverse(start, end):
while start < end:
s[start], s[end] = s[end], s[start]
start += 1
end -= 1
length = len(s) - 1
reverse(0, n-1)
reverse(n,length)
reverse(0, length)
return ''.join(s)
'''
Py筆記
1.列表的一個內置方法,直接使用返回值爲None
2.reversed()的作用之後,返回的是一個把序列值經過反轉之後的迭代器,
所以,需要通過遍歷,或者List,或者next()等方法,獲取作用後的值;
3.reverse和reversed的區別是有沒有返回值
4.不管reverse哪個局部,最好的方法是a[a:b:c]=reversed(a[a:b:c])
'''
class Solution:
def reverseLeftWords(self, s: str, n: int) -> str:
if n > len(s) or not s:
return ''
s = list(s)
s[:n] = reversed(s[:n])
s[n:] = reversed(s[n:])
s = reversed(s)
return ''.join(s)
59-1.滑動窗口的最大值
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
#有麼有那樣的單調隊列--滑窗--單調
'''
棧模擬隊列
遞減隊,最左側元素一定爲當前滑窗內最大;
若遞減隊列大於等於滑窗大小,彈出最左側也就是最大元素;
若遇到非遞減元素,則把升序部分都pop,新入隊列
'''
q = []
ans = []
for i in range(len(nums)):
while q and nums[i]>nums[q[-1]]:
#單調隊列記錄索引
q.pop()
q.append(i)
while q[-1]-q[0]>=k:
q.pop(0)
ans.append(nums[q[0]])
return ans[k-1:] #從第一個滑窗開始記
#雙端隊列也一樣
from collections import deque
class Solution:
def maxSlidingWindow(self, nums: 'List[int]', k: 'int') -> 'List[int]':
queue, res = [], []
for i in range(len(nums)):
if len(queue) > 0 and i - queue[0] + 1 > k: del queue[0]
while len(queue) > 0 and nums[i] > nums[queue[-1]]: del queue[-1]
queue.append(i)
if i >= k - 1:
res.append(nums[queue[0]])
return res
59-2.隊列的最大值
'''
時間複雜度:O(1)(插入,刪除,求最大值)
刪除操作求最大值操作顯然只需要O(1) 的時間。
而插入操作雖然看起來有循環,做一個插入操作時最多可能會有n次出隊操作。
但要注意,由於每個數字只會出隊一次,因此對於所有的n個數字的插入過程,對應的所有出隊操作也不會大於
n次。因此將出隊的時間均攤到每個插入操作上,時間複雜度爲 O(1)。
空間複雜度:O(n),需要用隊列存儲所有插入的元素。
'''
#1.滑動窗口維護遞減隊列的方法,純用隊列
import queue
class MaxQueue:
def __init__(self):
self.deque = queue.deque()#雙端,主helper
self.queue = queue.Queue()#單向隊列
def max_value(self) -> int:
return self.deque[0] if self.deque else -1
def push_back(self, value: int) -> None:
while self.deque and self.deque[-1] < value:
self.deque.pop()
self.deque.append(value)
self.queue.put(value)
def pop_front(self) -> int:
if not self.deque:
return -1
ans = self.queue.get()
if ans == self.deque[0]:
self.deque.popleft()
return ans
#2.棧實現
import queue
class MaxQueue:
def __init__(self):
self.q = []
self.helper = []
def max_value(self) -> int:
return self.helper[0] if self.q else -1
def push_back(self, value: int) -> None:
while self.helper and self.helper[-1] < value:
self.helper.pop()
self.helper.append(value)
self.q.append(value)
def pop_front(self) -> int:
if not self.q:
return -1
ans = self.q.pop(0)
if ans == self.helper[0]:
self.helper.pop(0)
return ans
60.n個骰子的點數
#痛苦找規律
class Solution:
def twoSum(self, n: int) -> List[float]:
#n爲骰子個數
#找了一下規律,這題有點dpdp
start = (1/6)**n
'''
n=1: 0 1 1 1 1 1 1
n=2: 0 0 1 2 3 4 5 6 5 4 3 2 1
n=3: 0 0 0 1 3 6 10 ...
規律準確來講是:dp[n][s] = sum(dp[n-1][s-1] to dp[n-1][s-6])
'''
dp = [[0]*(n*6+1) for _ in range(n)]
dp[0][:7]=[0,1,1,1,1,1,1]
for j in range(1,n):
for s in range(j,n*6+1):
k = 1
while k<=6 and s>=k:
dp[j][s] += dp[j-1][s-k]
k+=1
ans = []
for num in dp[-1]:
if num>0:
ans.append(num*start)
return ans
#優化空間to單維
class Solution:
def twoSum(self, n: int) -> List[float]:
#n爲骰子個數
#找了一下規律,這題有點dpdp
start = (1/6)**n
dp = [0]*(n*6+1)
dp[:7]=[0,1,1,1,1,1,1]
for j in range(1,n):
for s in range(n*6,j-1,-1):
#單維優化得逆序
k = 1
dp[s] = 0#這一步注意,是從自己的0開始累加纔對
while k<=6 and s>=k:
dp[s] += dp[s-k]
k+=1
ans = []
for num in dp:
if num>0:
ans.append(num*start)
return ans
61.撲克牌中的順子
#1.排序遍歷_統計空位和0補位
class Solution:
def isStraight(self, nums: List[int]) -> bool:
#這個數組中0可以當任何數用,所以當牌不連續的時候,它就可以替補一下
nums.sort()
cnt0 = 1 if nums[0]==0 else 0
gap = 0
for i in range(1,len(nums)):
if nums[i]==0:
cnt0+=1
if nums[i]!=0 and nums[i]==nums[i-1]:
#順子中不能有0以外的重複牌
return False
if nums[i]==nums[i-1]+1 or nums[i-1]==0:
continue
else:
#非順子計算空位
gap += nums[i]-nums[i-1]-1
return cnt0>=gap
#2.排序遍歷
class Solution:
def isStraight(self, nums: List[int]) -> bool:
joker = 0
nums.sort() # 數組排序
for i in range(4):
if nums[i] == 0: joker += 1 # 統計大小王數量
elif nums[i] == nums[i + 1]: return False # 若有重複,提前返回 false
return nums[4] - nums[joker] < 5 # 最大牌 - 最小牌 < 5 則可構成順子
#3.set+遍歷
class Solution:
def isStraight(self, nums: List[int]) -> bool:
repeat = set()
ma, mi = 0, 14
for num in nums:
if num == 0: continue # 跳過大小王
ma = max(ma, num) # 最大牌
mi = min(mi, num) # 最小牌
if num in repeat: return False # 若有重複,提前返回 false
repeat.add(num) # 添加牌至 Set
return ma - mi < 5 # 最大牌 - 最小牌 < 5 則可構成順子
62.圓圈中最後剩下的數字
#1.照題意操作一遍就有了
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
nums = list(range(n))
flag = m-1
while len(nums)>1:
flag = flag%len(nums)
nums.pop(flag)
#print(su)
flag += m-1
return nums[0]
#2.數學法--約瑟夫環問題
'''
f(n,m)=[(m-1)%n+x+1]%n 其中x=f(n-1,m)
f(n,m)=[(m-1)%n+x+1]%n
=[(m-1)%n%n+(x+1)%n]%n
=[(m-1)%n+(x+1)%n]%n
=(m-1+x+1)%n
=(m+x)%n
'''
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
#約瑟夫環問題
ans = 0
for i in range(2,n+1):
ans = (ans+m)%i
return ans
63.股票的最大利潤
#1.一般do
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if len(prices)<=1:
return 0
dp = 0
for i in range(1,len(prices)):
dp = max(dp,prices[i]-min(prices[:i]))
return dp
#2.維護一個最小值有效提速
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if len(prices)<=1:
return 0
dp = 0
minmin = prices[0]
for i in range(1,len(prices)):
if prices[i]<minmin:
minmin = prices[i]
dp = max(dp,prices[i]-minmin)
return dp
64.求1+2+…+n
#不讓用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)
#1.遞歸,終點設爲0——遞歸短路
class Solution:
def sumNums(self, n: int) -> int:
return n and (n+self.sumNums(n-1))
## 這個特性實際叫做“驟死性評估”,是一種語言特性,即左側的表達式爲假時整個表達式後續將不再進行評估。
## 考察and特性,前者false就跳過後判斷條件
65. 不用加減乘除做加法
a, b 均可能是負數或 0;結果不會溢出 32 位整數;
'''
^ 亦或 ----相當於 無進位的求和, 想象10進制下的模擬情況:(
如:19+1=20;無進位求和就是10,而非20;因爲它不管進位情況)
& 與 ----相當於求每位的進位數, 先看定義:1&1=1;1&0=0;0&0=0;
即都爲1的時候才爲1,正好可以模擬進位數的情況,還是想象10進制下模擬情況:(
9+1=10,如果是用&的思路來處理,則9+1得到的進位數爲1,而不是10,所以要用<<1向左再移動一位,這樣就變爲10了);
這樣公式就是:(a^b) ^ ((a&b)<<1) 即:每次無進位求 + 每次得到的進位數
我們需要不斷重複這個過程,直到進位數爲0爲止;
'''
class Solution:
def add(self, a: int, b: int) -> int:
#位運算經典考察題目
return a if b==0 else add(a^b,(a&b)<<1)
#有關python存儲格式的考察
class Solution:
def add(self, a: int, b: int) -> int:
x = 0xffffffff
#Python中bin一個負數(十進制表示),輸出的是它的原碼的二進制表示加上個負號
a, b = a & x, b & x #獲取負數的補碼,捨去此數字32位以上的數字,從無限長度變爲一個32位整數。
while b != 0:
a, b = (a ^ b), (a & b) << 1 & x #補碼運算減也是加
return a if a <= 0x7fffffff else ~(a ^ x)
#如果 a 的補碼是負數(第32位是1),需要把這個補碼恢復到 python 存儲負數的形式
#若補碼a爲負數( 0x7fffffff 是最大的正數的補碼 ),需執行 ~(a ^ x) 操作,將補碼還原至 Python 的存儲格式。 # a ^ x運算將1至32位按位取反;~ 運算是將整個數字取反;因此, ~(a ^ x) 是將32位以上的位取反,由0變爲1,
#1至32位不變。
'''
因爲c java等是有位數限制的,所以可以直接做。但是python沒有位數限制,
可能一個數字用了大於32位去存儲。所以先把a,b都搞到32位範圍內,做運算。
最後結果a如果是負數,那麼可能超過了32位存儲,要給a恢復到超過32位的python中的存儲方式。
'''
66.構建乘積數組
#1.兩次遍歷
class Solution:
def constructArr(self, a: List[int]) -> List[int]:
ans = [1]*len(a)
mul = 1
for i in range(len(a)):
#左向右
ans[i]=mul
mul*=a[i]
mul = 1
for i in range(len(a)-1,-1,-1):
#右向左
ans[i]*=mul
mul*=a[i]
return ans
67.把字符串轉換成整數
#1.正則
class Solution:
def strToInt(self, str: str) -> int:
#正則一發
return max(min(int(*re.findall("^[\+\-]?\d+",str.lstrip())),2**31 - 1),-2**31)
#2.ifelse
class Solution:
def strToInt(self, str: str) -> int:
num_max = pow(2,31)-1
num_min = -pow(2,31)
if str=="":
return 0
s = list(str)
nums = ['1','2','3','4','5','6','7','8','9','0']
num_out = []
for i in range(len(str)):
if s[i] in nums:
num_out.append(s[i])
continue
if s[i]=="-" and num_out == []:
num_out.append(s[i])
continue
if s[i]=="+" and num_out ==[]:
num_out.append(s[i])
continue
if s[i]==" " and num_out ==[]:
continue
else:
break
if num_out == [] or num_out == ["-"] or num_out == ["+"]:
return 0
num_out = "".join(num_out)
print(num_out)
num_out = int(num_out)
if num_out<num_min:
return num_min
elif num_out>num_max:
return num_max
else:
return num_out
68-1.二叉搜索樹的最近公共祖先
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if root.val>p.val and q.val<root.val:
return self.lowestCommonAncestor(root.left,p,q)
if root.val<p.val and q.val>root.val:
return self.lowestCommonAncestor(root.right,p,q)
return root
68-2. 二叉樹的最近公共祖先
class Solution:
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
if not root:
return None
if root.val == p.val:
return p
if root.val == q.val:
return q
left = self.lowestCommonAncestor(root.left,p,q)
right = self.lowestCommonAncestor(root.right,p,q)
if left and right:
return root
if not left and right:
return right
if left and not right:
return left
else:
return None