把数组排序后,遍历数组,每次固定当前元素,然后用双指针再后续数组里面搜索与它相加为0的元素组合。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
n=len(nums)
res=[]
nums.sort()
for i in range(n):
if nums[i] > 0:
#因为排序了,遍历到大于0的数不可能有结果
return res
#重复元素只要第一个
if(i>0 and nums[i]==nums[i-1]):
continue
l = i + 1
r = n - 1
while l<r:
if nums[i] + nums[l] +nums[r] == 0:
res.append( [nums[i] , nums[l] , nums[r]])
#两个while过滤掉重复元素
while(l<r and nums[l]==nums[l+1]):
l += 1
while(l<r and nums[r]==nums[r-1]):
r -= 1
l -= 1
r -= 1
elif(nums[i]+nums[l]+nums[r]>0):
r-=1
else:
l+=1
return res
要求原地变换,所以用行列的第一个元素作为标记,来表示该行该列是否要变为0
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
#记录第一列
is_col = False
R = len(matrix)
C = len(matrix[0])
for i in range(R):
#只要第一列出现0,标记为True
if matrix[i][0] == 0:
is_col = True
for j in range(1, C):
if matrix[i][j] == 0:
matrix[0][j] = 0
matrix[i][0] = 0
#根据行列开头标志改变元素
for i in range(1, R):
for j in range(1, C):
if not matrix[i][0] or not matrix[0][j]:
matrix[i][j] = 0
#单独处理第一列和第一行
if matrix[0][0] == 0:
for j in range(C):
matrix[0][j] = 0
if is_col:
for i in range(R):
matrix[i][0] = 0
本质上是要我们维护一个字典,字母异位词要对应着同一个key,我们可以把字符串排序后当做key,可以把字母计数结果当作key。
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
ans = collections.defaultdict(list)
#默认字典,索引不在key中,就新建一个空list对应
for s in strs:
count = [0] * 26
for c in s:
count[ord(c) - ord('a')] += 1
ans[tuple(count)].append(s)
return list(ans.values())
很明显要使用滑动窗口的思想,一边滑动一边判断就可。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
l = r = out = 0
#窗口内字符
pattern = set()
while r < len(s):
if s[r] not in pattern:
pattern.add(s[r])
out = max(out,r-l+1)
r += 1
else:
pattern.remove(s[l])
l += 1
return out
回文子串可以分解为对应的子问题,所以采用动态规划思想。要注意的是两个循环的写法,要保证子问题的求解顺序,不能先出现依赖还没求解子问题的高阶问题。
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
dp = [[False] * n for _ in range(n)]
ans = ""
# 枚举子串的长度 l+1
for l in range(n):
#子串起始位置
for i in range(n):
j = i + l
if j >= len(s):
break
if l == 0:
dp[i][j] = True
elif l == 1:
dp[i][j] = (s[i] == s[j])
else:
dp[i][j] = (dp[i + 1][j - 1] and s[i] == s[j])
if dp[i][j] and l + 1 > len(ans):
ans = s[i:j+1]
return ans
问题只要求判断存在与否就可,我们可以用两个变量存储当前的最小值和次小值,如果新遍历到的值大于次小值,那么已经构成了一个三元组,可以返回True;如果不大于,则根据具体值更新最小次小值即可。
class Solution:
def increasingTriplet(self, nums: List[int]) -> bool:
n1 = float('inf')
n2 = float('inf')
for num in nums:
if num <= n1:
n1 = num
elif num <= n2:
n2 = num
else:
return True
return False
模拟字符串数字加法即可
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
res = p = ListNode()
carry = 0
while l1 or l2 or carry:
#优化边界判断
x = l1.val if l1 else 0
y = l2.val if l2 else 0
p.next = ListNode((x+y+carry)%10)
p = p.next
carry = (x+y+carry)//10
if l1:
l1 = l1.next
if l2:
l2 = l2.next
return res.next
用两个指针分别记录奇链表尾和偶链表头,再用一个指针遍历原链表(顺便充当偶链表尾)即可
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def oddEvenList(self, head: ListNode) -> ListNode:
if not head :
return head
p1 = head
p2 = p = head.next
#P1,P2分别为奇偶链表尾
while p2 and p2.next:
p1.next = p2.next
p1 = p1.next
p2.next = p2.next.next
p2 = p2.next
p1.next = p
return head
把两个链表分别前后顺序拼接起来,如果是相交的,遍历的时候一定会到相同的节点(走过相同的路)。
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
p1 = headA
p2 = headB
while p1 != p2:
p1 = p1.next if p1 else headB
p2 = p2.next if p2 else headA
#如果两个链表不相交的话,p1和p2都是空值
return p1
递归的解法很容易写出
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
res = []
def help(root,res):
if not root:
return
help(root.left,res)
res.append(root.val)
help(root.right,res)
help(root,res)
return res
迭代需要考虑访问顺序
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
res = []
s = []
x = root
while s or x:
while x:
s.append(x)
x = x.left
#左子树全部进栈了
#接下来弹出的就是根节点
x = s.pop()
res.append(x.val)
x = x.right
return res
同时迭代的代码改成先序遍历很简单
def preorderTraversal(self, root: TreeNode) -> List[int]:
res = []
s = []
x = root
while s or x:
while x:
res.append(x.val)
#第一次遍历到节点就输出,而不是上面那样出栈时再输出
s.append(x)
x = x.left
x = s.pop()
x = x.right
return res
后序遍历的迭代就稍微麻烦一点了
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
res = []
s = []
x = root
while s or x:
while x:
s.append(x)
x = x.left if x.left is not None else x.right
#这里不光是上面那样先访问左子树,没有左子树但是有右子树的也要先访问。
x = s.pop()
res.append(x.val)
#退栈时只是左子树完了的话还需要访问右子树,因为后序时唯一一个右在根节点之前的遍历方式。
if s and s[-1].left == x: #若栈不为空且当前节点是栈顶元素的左节点
x = s[-1].right ## 则转向遍历右节点
else:
x = None # 没有左子树或右子树,强迫退栈
return res
可以简单的按照一般层次遍历结果再按奇偶反转,也可以用双端队列花哨一点,但是本质上复杂度是一样的。。。
class Solution:
def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
if not root:
return []
q = queue.Queue()
q.put(root)
res = []
is_left = False
while q.qsize():
tmp = collections.deque()
for _ in range(q.qsize()):
node = q.get()
if is_left:
tmp.appendleft(node.val)
else:
tmp.append(node.val)
if node.left:
q.put(node.left)
if node.right:
q.put(node.right)
is_left = not is_left
res.append(list(tmp))
return res
先在前序序列找到根节点,再根据根节点在中序序列中划分开左右子树的节点,递归构造子树。
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
if len(inorder) == 0:
return None
# 前序遍历第一个值为根节点
root = TreeNode(preorder[0])
# 利用根节点划开左右子树
mid = inorder.index(preorder[0])
root.left = self.buildTree(preorder[1:mid+1], inorder[:mid])
root.right = self.buildTree(preorder[mid+1:], inorder[mid+1:])
return root
同时有一个迭代的方法,不过比较复杂,大家可以看 官方题解.
相当于一种增加了操作的层次遍历。
class Solution:
def connect(self, root: 'Node') -> 'Node':
if not root:
return
q = []
q.append(root)
while q:
tmp = []
for i in range(len(q)):
node = q[i]
node.next = q[i+1] if i <len(q)-1 else None
if node.left:
tmp.append(node.left)
if node.right:
tmp.append(node.right)
q = tmp[:]
#记得返回根节点,开始看原地还以为不用返回
return root
利用二叉搜索树自身的良好性质,先遍历中序遍历,每遍历一个节点就计数器k-1,直到输出k=0的节点。
class Solution:
def kthSmallest(self, root: TreeNode, k: int) -> int:
self.k = k
def dfs(root):
if not root:
return
dfs(root.left)
self.k -= 1
if self.k == 0:
self.res = root.val
dfs(root.right)
dfs(root)
return self.res
用dfs一次把一个岛屿的1清空即可,但是注意题目输入时字符串,不是数字0和1
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
direcs = [[0,1],[-1,0],[0,-1],[1,0]]
res = 0
def dfs(x,y):
grid[x][y] = '0'
for direc in direcs:
new_x = x +direc[0]
new_y = y +direc[1]
#判断是否更深度搜索
if 0 <= new_x < len(grid) and 0 <= new_y < len(grid[0]) and grid[new_x][new_y] == '1':
dfs(new_x,new_y)
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == '1':
dfs(i,j)
res += 1
return res
按照字典映射,保存当前已有的字母组合后缀添加数字即可。
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
phone = {'2': ['a', 'b', 'c'],
'3': ['d', 'e', 'f'],
'4': ['g', 'h', 'i'],
'5': ['j', 'k', 'l'],
'6': ['m', 'n', 'o'],
'7': ['p', 'q', 'r', 's'],
'8': ['t', 'u', 'v'],
'9': ['w', 'x', 'y', 'z']}
if not digits:
return []
res = ['']
#已有组合
for c in digits:
temp = []
#保存新组合
for pre in res:
for alpha in phone[c]:
temp.append(pre+alpha)
res = temp
return res
如果我们把第一个的’(‘对应的’)‘看作要改变的量,那么’)'左边会有i对括号,右边有n-i-1对括号,调用递归即可。
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
if n == 0:
return ['']
out = []
for i in range(n):
for l in self.generateParenthesis(i):
for r in self.generateParenthesis(n -1 - i):
out.append('(' + l + ')' + r)
return out
也可以符合官方思想的迭代
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
if n == 0:
return ['']
out = []
def backtrack(s,l,r):
if len(s) == 2*n:
out.append(''.join(s))
return
if l < n:
s.append('(')
backtrack(s,l+1,r)
#回溯,记得出栈
s.pop()
if l > r:
s.append(')')
backtrack(s,l,r+1)
#回溯,记得出栈
s.pop()
backtrack([],0,0)
return out
生成排列时把数字分为已使用和未使用
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
#first就是未使用数字的开头
def backtrack(first = 0):
# 所有数都填完了
if first == n:
res.append(nums[:])
for i in range(first,n):
nums[i],nums[first] = nums[first],nums[i]
backtrack(first + 1)
#回溯的撤销操作
nums[i],nums[first] = nums[first],nums[i]
n = len(nums)
res = []
backtrack()
return res
一个子集可以用依次对于每个元素考虑包含和不包含的方法来生成。
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
out = [[]]
for x in nums:
n = len(out)
for i in range(n):
temp = out[i][:]
#保留原来那个不包含x的子集
temp.append(x)
out.append(temp)
return out
也可以就用回溯思想
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
def backtrack(first = 0, curr = []):
#满足长度k
if len(curr) == k:
output.append(curr[:])
for i in range(first, n):
#dfs
curr.append(nums[i])
backtrack(i + 1, curr)
#回溯撤销
curr.pop()
output = []
n = len(nums)
for k in range(n + 1):
#因为子集长度不一,每个for求出一种长度的
backtrack()
return output
如果没有说不能重复使用字符,那么简单的dfs就可以,但是说了就用用到回溯法,用一个同样大小的矩阵标记元素有没有被访问过。
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
direcs = [[0,1],[1,0],[-1,0],[0,-1]]
m = len(board)
n = len(board[0])
def dfs(x,y,k):
if k == len(word)-1:
return True
flag = False
#访问过的标志
visid[x][y] = False
for direc in direcs:
new_x = x + direc[0]
new_y = y + direc[1]
if 0 <= new_x < m and 0 <= new_y < n and board[new_x][new_y] == word[k+1] and visid[new_x][new_y]:
flag = flag or dfs(new_x,new_y,k+1)
#回溯撤销
visid[x][y] = True
return flag
#标记访问过没有
visid = [[True]*n for _ in range(m)]
for i in range(m):
for j in range(n):
if board[i][j] == word[0]:
if dfs(i,j,0):
return True
return False
除了遍历用的指针外,再用两个指针定位当前插入0和插入2的位置。
class Solution:
def sortColors(self, nums: List[int]) -> None:
#插入0的位置
p0 = 0
#遍历指针
p1 = 0
#插入2的位置
p2 = len(nums)-1
while p1 <= p2:
if nums[p1] == 0:
nums[p1],nums[p0] = nums[p0],nums[p1]
p0 += 1
p1 += 1
elif nums[p1] == 2:
nums[p1],nums[p2] = nums[p2],nums[p1]
p2 -= 1
else:
p1 += 1
统计一下各个元素出现次数,再根据这个排序输出结果即可。
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
#元素-出现次数字典
n_f = {}
#出现次数-元素list字典
f_n = {}
for i in nums:
n_f[i] = n_f.get(i,0) + 1
for n,f in n_f.items():
temp = f_n.get(f,[])
temp.append(n)
f_n[f] = temp
arr = []
for x in range(len(nums),0,-1):
#把出现次数从大到小检索,而不是排序频率的字典了
if x in f_n:
for i in f_n[x]:
arr.append(i)
return arr[:k]
借助快排里面的划分函数,O(n)时间完成查询操作。
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def partition(l,r):
#常规的划分算法
if l >= r:
return
mid = l + (r-l)//2
index = nums[mid]
nums[mid],nums[r] = nums[r],nums[mid]
parti = l
for i in range(l,r):
if nums[i] < index:
nums[i],nums[parti] = nums[parti],nums[i]
parti += 1
nums[parti],nums[r] = nums[r],nums[parti]
return parti
def select(l,r,k):
#这里k是指第k小,所以输入的k要反过来算
if l == r:
return nums[l]
pivot = partition(l, r)
if k == pivot:
return nums[k]
#另外的区间寻找
elif k < pivot:
return select(l,pivot-1,k)
else:
return select(pivot + 1,r,k)
return select(0,len(nums)-1,len(nums)-k)
看到要求的 O ( l o g 2 n ) O(log_2n) O(log2n)就只要要用二分法。
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
l = 0
r = len(nums)-1
while l < r:
mid = l + (r-l)//2
if nums[mid] > nums[mid+1]:
#该情况波峰在左边,并且有可能是mid,所以不能r=mid-1
r = mid
else:
#该情况波峰在右边,并且nums[mid+1]的值大于nums[mid]的,所以l=mid+1,mid没有机会成为峰值
l = mid + 1
return l
利用两趟二分查找即可。
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
l = 0
r = len(nums)-1
#寻找左边界
while l <= r:
mid = l + (r-l)//2
if nums[mid] < target:
l = mid + 1
else:
r = mid - 1
lout = r
l = 0
r = len(nums)-1
#寻找右边界
while l <= r:
mid = l + (r-l)//2
if nums[mid] <= target:
l = mid + 1
else:
r = mid - 1
rout = l
return [lout+1,rout-1] if rout - lout > 1 else [-1,-1]
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
out = []
if not intervals:
return out
intervals.sort(key = lambda x: x[0])
s = -1
e = -1
for tmp in intervals:
if tmp[0] > e:
#应该新建的区间
out.append([s,e])
s = tmp[0]
e = tmp[1]
else:
#应该合并,结束时间取最大
e = max(e,tmp[1])
else:
#尾处理最后一个区间
out.append([s,e])
#消除第一个[-1,-1]
return out[1:]
发现这道题不要想的太复杂把自己绕进去了,二分搜索下再细分两种情况就可以,当前位置左边有序还有右边有序。
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums:
return -1
l = 0
r = len(nums) - 1
while l <= r:
mid = l + (r-l)//2
if nums[mid] == target:
return mid
#左半有序,记得加等号,因为mid可能等于l
if nums[l] <= nums[mid]:
#在左半有序区间,好处理
if nums[l] <= target < nums[mid]:
r = mid -1
#在右边未知顺序的数组里面,就给下一个循环判断
else:
l = mid + 1
#右半有序
else:
#在右半有序区间,好处理
if nums[mid] < target <= nums[r] :
l = mid + 1
else:
#在左边未知顺序的数组里,交给下一个循环判断
r = mid -1
#上面查找很完备,找不到则是不存在
return -1
利用矩阵自身性质,从左下角或者右上角开始搜索。
class Solution:
def searchMatrix(self, matrix, target):
if not matrix:
return False
m = len(matrix)
n = len(matrix[0])
i = m-1
j = 0
while i >=0 and j < n:
if matrix[i][j] == target:
return True
#往右,变大
elif matrix[i][j] < target:
j += 1
#往上,变小
else:
i -= 1
return False
又到了我最喜欢的dp环节了
借用一句话‘贪心是每个状态只使用一次的动态规划。’,这道题用贪心的思路,维护一个最大跳跃距离即可。
class Solution:
def canJump(self, nums: List[int]) -> bool:
#最大距离
maxl = 0
#当前位置
i = 0
while i <= maxl and i < len(nums):
maxl = max(maxl,i+nums[i])
i += 1
return i == len(nums)
一个方块只能由上或者右到达。
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
#压缩空间,只要一行记录就行,遍历是一行行来的。
dp = [1] * n
for i in range(1,m):
for j in range(1,n):
dp[j] += dp[j-1]
return dp[-1]
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
#记录硬币个数
dp = [0] + [float('inf')] * (amount)
for i in range(amount+1):
for coin in coins:
if i >= coin:
#新的组合是否更好
dp[i] = min(dp[i],dp[i-coin] + 1)
return dp[-1] if dp[-1] != float('inf') else -1
用dp思想很容易写出代码,因为子序列明显可以划分子问题。
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if not nums:
return 0
dp = [1] * len(nums)
for i in range(len(nums)):
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i],dp[j]+1)
return max(dp)
但是这样时间复杂度是 O ( n 2 ) O(n^2) O(n2)可以进一步优化一下,可以换一种贪心策略,则我们需要让序列上升得尽可能慢,因此我们希望每次在上升子序列最后加上的那个数尽可能的小。
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
#维护一个上升序列
res = []
for num in nums:
if not res or num > res[-1]:
res.append(num)
else:
#找到应该替换的位置
l, r = 0, len(res) - 1
loc = r
#除非替换到了最后一个,不然不影响长度,同时最
while l <= r:
mid = (l + r) // 2
if res[mid] >= num:
loc = mid
r = mid - 1
else:
l = mid + 1
res[loc] = num
return len(res)
熟练运用递归思路构建。
class Codec:
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
def help(root,s):
if not root:
s += 'None,'
else:
s += str(root.val) + ','
s = help(root.left,s)
s = help(root.right,s)
return s
return help(root,'')
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
def help(data_list):
x = data_list.pop(0)
if x == 'None':
return None
root = TreeNode(x)
root.left = help(data_list)
root.right = help(data_list)
return root
data_list = data.split(',')
root = help(data_list)
return root
除了正常的数组存储数据之外,还要一个字典保存索引和数值的对应关系。整个功能最关键的地方在于删除操作,为了进行最小程度的修改,用数组的最后一个元素填充到删除的位置,这样只要更改一个索引即可。
class RandomizedSet:
def __init__(self):
"""
Initialize your data structure here.
"""
self.lst = []
self.idx = {}
def insert(self, val: int) -> bool:
"""
Inserts a value to the set. Returns true if the set did not already contain the specified element.
"""
if val not in self.idx:
self.idx[val] = len(self.lst)
self.lst.append(val)
return True
return False
def remove(self, val: int) -> bool:
"""
Removes a value from the set. Returns true if the set contained the specified element.
"""
if val not in self.idx.keys():
return False
index = self.idx[val]
fill = self.lst[-1]
#最后一个数值填到删除的位置
self.lst[index] = fill
self.idx[fill] = index
self.lst.pop()
del self.idx[val]
return True
def getRandom(self) -> int:
"""
Get a random element from the set.
"""
return random.choice(self.lst)
本质上相当于判断链表是否有环。
class Solution:
def isHappy(self, n: int) -> bool:
#定义一个next方法
def next(number):
sum = 0
while number > 0:
number, digit = divmod(number, 10)
sum += digit ** 2
return sum
#快慢指针
slow_runner = n
fast_runner = next(n)
#双指针判断链表是否有环
while fast_runner != 1 and slow_runner != fast_runner:
slow_runner = next(slow_runner)
fast_runner = next(next(fast_runner))
return fast_runner == 1
需要知道,阶乘中出现0的情况只能是2*5,所以0的个数是为阶乘元素因子分解中2和5中最小出现次数那么多。实际上5比2少得到,所以5的个数就是乘完后0的个数。
class Solution:
def trailingZeroes(self, n: int) -> int:
res = 0
while n:
n //= 5
#因为数字可能是25,125之类含有多个5的因子
res += n
return res
当作26进制的数,逐位转化
class Solution:
def titleToNumber(self, s: str) -> int:
res = 0
#位数
power = 0
for c in reversed(s):
res += (ord(c) - ord('A') + 1) * pow(26,power)
power += 1
return res
二分思想拆分,可以减少一般递归次数。
class Solution:
def myPow(self, x: float, n: int) -> float:
def quickMul(N):
if N == 0:
return 1.0
y = quickMul(N // 2)
#奇数多余一个x
return y * y if N % 2 == 0 else y * y * x
#考虑负指数
return quickMul(n) if n >= 0 else 1.0 / quickMul(-n)
把n拆成2进制考虑也是一个道理
class Solution:
def myPow(self, x: float, n: int) -> float:
def quickMul(x,N):
res = 1.0
while N:
#对应二进制位值为1,考虑这个倍数
if N%2:
res *= x
x *= x
N = N >> 1
return res
return quickMul(x,n) if n >= 0 else 1.0 / quickMul(x,-n)
利用牛顿法求解
class Solution:
def mySqrt(self, x: int) -> int:
if x == 0:
return 0
C, x0 = float(x), float(x)
while True:
#牛顿法,泰勒一阶展开令其等于0推出迭代公式,注意和优化器里的牛顿法区分一下,那是利用牛顿法求解极值问题,是二阶展开并对x求导。
xi = 0.5 * (x0 + C / x0)
if abs(x0 - xi) < 1e-7:
break
x0 = xi
return int(x0)
在二进制数据上模拟以前的短除法达到要求。
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
#边界处理
sign = (dividend > 0) ^ (divisor > 0)
dividend = abs(dividend)
divisor = abs(divisor)
count = 0
while dividend >= divisor:
count += 1
divisor <<= 1
result = 0
while count:
count -= 1
divisor >>= 1
if divisor <= dividend:
#这里的移位运算是把二进制(第count+1位上的1)转换为十进制
result += 1 << count
dividend -= divisor
if sign:
result = -result
#输出边界限制
return result if -(1<<31) <= result <= (1<<31)-1 else (1<<31)-1
模拟实际运算的操作。
class Solution:
def fractionToDecimal(self, numerator: int, denominator: int) -> str:
#处理边界条件
sign = (numerator > 0) ^ (denominator > 0)
numerator = abs(numerator)
denominator = abs(denominator)
if numerator == 0:
return '0'
#结果变量,先存正数部分
res = str(numerator // denominator) + '.'
numerator %= denominator
numerator *= 10
numerators = []
#一边算小数部分,一边判断是否有重复出现
while numerator != 0 and numerator not in numerators:
numerators.append(numerator)
cur = numerator // denominator
res += str(cur)
numerator %= denominator
numerator *= 10
if numerator in numerators:
#寻找循环出现的位置
idx = len(numerators) - numerators.index(numerator)
res = res[:-idx] + '(' + res[-idx:] + ')'
if res[-1] == '.':
res = res[:-1]
return '-' + res if sign else res
两数异或可以模拟无进位的加法,且再进位可以模拟进位结果,重复即可,考虑python整型问题还要一点特殊处理。
class Solution:
def getSum(self, a: int, b: int) -> int:
MASK = 0x100000000
# 整型最大值
MAX_INT = 0x7FFFFFFF
MIN_INT = MAX_INT + 1
while b:
carry = (a & b) << 1
a = (a ^ b) % MASK
b = carry % MASK
#模拟32位整型,负数先把后32位取反,再整个数统一取反
return a if a <= MAX_INT else ~((a % MIN_INT) ^ MAX_INT)
利用栈模拟真实操作
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
PopList = []
for i in tokens:
if i in "+-*/":
tmp = PopList.pop()
tmp2 = PopList.pop()
#eval把括号里面的式子当输入运行
PopList.append(str(int(eval(tmp2+i+tmp))))
else:
PopList.append(i)
return int(PopList[0])
多数元素因为出现次数大于一半,保存当前出现次数大于其他数字之和的结果即可。
class Solution:
def majorityElement(self, nums: List[int]) -> int:
res = 0
count = 0
for num in nums:
if count == 0:
res = num
count = 1
continue
if num != res:
count -= 1
else:
count += 1
return res
我们一轮轮的安排任务,假设一轮是n+1的运行时间,一共有max_num - 1
轮,然后把其他任务插入到这些空闲时间,看是否能够排列。
class Solution:
def leastInterval(self, tasks: List[str], n: int) -> int:
times = [0]*26;
for c in tasks:
times[ord(c) - ord("A")] +=1
times.sort(reverse=True)
#最大任务排列轮数
max_val = times[0] - 1
#紧密的一轮,能产生的空闲
idle = max_val * n
for i in range(1,26):
if times[i] == 0:
break
#注意最大是max_val,最大出现次数减一
#列优先运行的话,不管怎么样两个任务的间隔都大于n了
idle -= min(times[i],max_val)
#如果还有剩余,证明需要专门腾出时间等待;如果没有说明紧密的安排可以完成
return len(tasks) + idle if idle >0 else len(tasks)