剑指offer刷题记录
LeetCode上的剑指offer题
刷题ing
#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
#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 ' '
#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
#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
#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
#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
#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+1k,则在右子树找第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
#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
#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表示不平衡
#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)就行
'''
#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
#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
#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]
#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) # 拼接并返回
#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)
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
'''
时间复杂度: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
#痛苦找规律
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
#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 则可构成顺子
#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
#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
#不让用乘除法、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就跳过后判断条件
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中的存储方式。
'''
#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
#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
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
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