class Solution:
def generateParenthesis(self, n: int) -> List[str]:
res = []
def backtracking(S, left, right):
if len(S) == 2*n:
res.append(''.join(S))
return
if left < n:
S.append('(')
backtracking(S, left+1, right)
S.pop()
# 这里是右括号的数目要小于左括号的数目
if right < left:
S.append(')')
backtracking(S, left, right+1)
S.pop()
backtracking([], 0, 0)
return res
class Solution:
def minWindow(self, s: str, t: str) -> str:
m, n = len(s), len(t)
if m < n: return ''
dic_t = collections.Counter(t)
def is_in(dic, dic_t):
for k, v in dic_t.items():
if dic[k] <= 0: return False
if dic[k] < dic_t[k]: return False
return True
dic = collections.defaultdict(int)
l, r = 0, 0
min_length = float('inf')
res = ''
while r < m:
while r < m and not is_in(dic, dic_t):
dic[s[r]] += 1
r += 1
while l < r and is_in(dic, dic_t):
if r-l < min_length:
min_length = r - l
res = s[l:r]
dic[s[l]] -=1
l += 1
return res
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
# 使用双指针的解法,可以使得复杂度降低到O(n^2)
res, k = [], 0
nums.sort()
for k in range(len(nums) - 2):
if nums[k] > 0: break
if k > 0 and nums[k] == nums[k-1]: continue
l, r = k+1, len(nums) - 1
while l < r:
s = nums[k] + nums[l] + nums[r]
if s < 0:
# 太小了,右移左指针
l += 1
while l < r and nums[l] == nums[l-1]: l += 1
elif s > 0:
# 太大了,减小右指针
r -= 1
while l < r and nums[r] == nums[r+1]: r -= 1
else:
# 刚刚好
res.append([nums[k], nums[l], nums[r]])
# 可以趁机继续剪枝
l += 1
r -= 1
while l < r and nums[l] == nums[l-1]: l += 1
while l < r and nums[r] == nums[r+1]: r -= 1
return res
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
# 先序遍历的第一个元素肯定是根节点, [根节点, 左子树, 右子树]
# 中序遍历的结果是,[左子树,根节点,右子树]
# 因此,第一步先通过先序遍历的第一个元素找到根节点,再在中序遍历中找到根节点所对应的位置,
if not preorder: return None
root_val = preorder[0]
root = TreeNode(root_val)
seperate_idx = inorder.index(root_val)
left_inorder = inorder[:seperate_idx]
right_inorder = inorder[seperate_idx+1:]
left_preorder = preorder[1:1+len(left_inorder)]
right_preorder = preorder[1+len(left_inorder):]
root.left = self.buildTree(left_preorder, left_inorder)
root.right = self.buildTree(right_preorder, right_inorder)
return root
此外,从三种遍历中任意取两个出来,都可以恢复这个二叉树。
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
n = len(intervals)
if n < 2: return intervals
intervals = sorted(intervals, key=lambda x: x[0]) # 按照左边界进行排序
res = []
l, r = 0, 1
while r < n:
left = intervals[l][0]
right = intervals[l][1]
while r < n and intervals[r][0] <= right:
right = max(intervals[r][1], right)
r += 1
res.append([left, right])
l = r
r += 1
if l == n - 1:
res.append(intervals[l])
return res
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
def dfs(i, j):
# 终止条件
if not (0 <= i < len(grid)) or not (0 <= j < len(grid[0])): return
if grid[i][j] == '0': return
# 内部逻辑
grid[i][j] = '0' # 淹没该陆地
up = dfs(i-1, j)
down = dfs(i+1, j)
left = dfs(i, j-1)
right = dfs(i, j+1)
return
m, n = len(grid), len(grid[0])
res = 0
for i in range(m):
for j in range(n):
if grid[i][j] == '1':
dfs(i, j)
res += 1
return res
class Solution:
def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
def dfs(i, j):
if not (0 <= i < len(grid)) or not (0 <= j < len(grid[0])): return 0
if grid[i][j] == 0: return 0
# 内部逻辑
grid[i][j] = 0 # 淹没陆地
return 1 + dfs(i-1, j) + dfs(i+1, j) + dfs(i, j-1) + dfs(i, j+1)
m, n = len(grid), len(grid[0])
res = 0
for i in range(m):
for j in range(n):
if grid[i][j] == 1:
area = dfs(i, j)
res = max(res, area)
return res
class Solution:
def islandPerimeter(self, grid: List[List[int]]) -> int:
res = 0
m, n = len(grid), len(grid[0])
for i in range(m):
for j in range(n):
if grid[i][j] == 1:
res += 4
# 如果上下左右有相邻的陆地,有一块就要减去1
for dx, dy in [[-1, 0], [1, 0], [0, -1], [0, 1]]:
x, y = i + dx, j + dy
if 0 <= x < m and 0 <= y < n and grid[x][y] == 1:
res -= 1
return res
如果有多块陆地,下面的递归方法稍微改一下,价格计数器,每次dfs完把当前岛的周长加到计数器上,最后一起返回就行了。
class Solution:
def islandPerimeter(self, grid: List[List[int]]) -> int:
def dfs(i, j):
if not (0 <= i < len(grid)) or not (0 <= j < len(grid[0])): return 1 # 边界
if grid[i][j] == 0: return 1 # 水
if grid[i][j] != 1: return 0 # 走过了
grid[i][j] = 2 # 走过的路标记一下
return dfs(i-1, j) + dfs(i+1, j) + dfs(i, j-1) + dfs(i, j+1)
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == 1:
return dfs(i, j)
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
q = [(-nums[i], i) for i in range(k)]
heapq.heapify(q)
res = [-q[0][0]]
n = len(nums)
for i in range(k, n):
heapq.heappush(q, (-nums[i], i))
while q[0][1] <= i - k:
heapq.heappop(q)
res.append(-q[0][0])
return res
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
q = []
for i in range(k):
while q and nums[i] >= nums[q[-1]]:
q.pop()
q.append(i)
res = [nums[q[0]]]
for i in range(k, len(nums)):
while q and nums[i] >= nums[q[-1]]:
q.pop()
q.append(i)
while q and q[0] <= i - k:
q.pop(0)
res.append(nums[q[0]])
return res
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
if not digits: return []
dic = {'2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl', '6': 'mno',
'7': 'pqrs', '8': 'tuv', '9': 'wxyz'}
paths, path = [], []
def backtracking(i, path):
if len(path) >= len(digits):
paths.append(''.join(path))
return
for digit in dic[digits[i]]:
path.append(digit)
backtracking(i+1, path)
path.pop()
backtracking(0, path)
return paths
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
def quickSort(i, j):
if i >= j: return
pivot = nums[i]
low = i
high = j
while i < j:
while i < j and nums[j] >= pivot: j -= 1
nums[i] = nums[j]
while i < j and nums[i] < pivot: i += 1
nums[j] = nums[i]
nums[j] = pivot
quickSort(low, j-1)
quickSort(j+1, high)
quickSort(0, len(nums)-1)
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
# dfs
def dfs(i, j, k):
if k >= len(word): return True
if not (0 <= i < len(board)) or not (0 <= j < len(board[0])): return False
if board[i][j] != word[k]: return False
tmp = board[i][j]
board[i][j] = ''
res = dfs(i-1, j, k+1) or dfs(i+1, j, k+1) or dfs(i, j-1, k+1) or dfs(i, j+1, k+1)
board[i][j] = tmp
return res
m, n = len(board), len(board[0])
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
class Solution:
def longestValidParentheses(self, s: str) -> int:
# dp
# dp[i]表示以i结尾的最长有效括号的长度
if not s: return 0
n = len(s)
dp = [0] * n
for i in range(1, n):
# 遇到右括号,上前匹配左括号
if s[i] == ')':
pre = i - dp[i-1] - 1
if pre >= 0 and s[pre] == '(':
dp[i] = dp[i-1] + 2
# 防止出现独立括号,比如()(),()(())的情况
if pre > 0:
dp[i] += dp[pre-1]
return max(dp)
class Solution:
def search(self, nums: List[int], target: int) -> int:
l, r = 0, len(nums) - 1
while l <= r:
mid = l + (r - l) // 2
if nums[mid] == target:
return mid
# 前半截有序
if nums[0] <= nums[mid]:
if nums[0] <= target < nums[mid]:
r = mid - 1
else:
l = mid + 1
else:
if nums[mid] < target <= nums[-1]:
l = mid + 1
else:
r = mid - 1
return -1
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
paths = []
def backtracking(i, cur_sum, path):
if cur_sum == target:
paths.append(path[:])
return
if cur_sum > target:
return
for i in range(i, len(candidates)):
path.append(candidates[i])
cur_sum += candidates[i]
backtracking(i, cur_sum, path)
path.pop()
cur_sum -= candidates[i]
backtracking(0, 0, [])
return paths
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
n = len(matrix)
# 水平翻转
for i in range(n // 2):
for j in range(n):
matrix[i][j], matrix[n-i-1][j] = matrix[n-i-1][j], matrix[i][j]
# 按照对角线反转
for i in range(n):
for j in range(i):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
dp = [[0] * n for _ in range(m)]
dp[0][0] = grid[0][0]
# 初始化dp
for i in range(1, m):
dp[i][0] = dp[i-1][0] + grid[i][0]
for i in range(1, n):
dp[0][i] = dp[0][i-1] + grid[0][i]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
return dp[-1][-1]
class Solution:
def climbStairs(self, n: int) -> int:
if n < 3: return n
l, r = 1, 2
for i in range(2, n):
next = l + r
l = r
r = next
return r
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
dp = [[0] * (n+1) for _ in range(m+1)]
for i in range(1, m+1):
dp[i][0] = dp[i-1][0] + 1
for i in range(1, n+1):
dp[0][i] = dp[0][i-1] + 1
# 开始,假如插入,删除,替换都有不同的成本,那么直接把下面对应的1换成对应的成本就行了
# 整体上不会有太大的变化
for i in range(1, m+1):
for j in range(1, n+1):
left = dp[i-1][j] + 1 # xx变成xx#,新增一个字符
down = dp[i][j-1] + 1 # xxx变成xx,删除一个字符
left_down = dp[i-1][j-1]
if word1[i-1] != word2[j-1]:
left_down += 1 # xx#变成xx*, 替换一个字符
dp[i][j] = min(left_down, left, down)
return dp[-1][-1]
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
paths = []
path = []
def backtrackeing(i, path):
paths.append(path[:])
if len(path) >= len(nums):
return
for j in range(i, len(nums)):
path.append(nums[j])
backtrackeing(j+1, path)
path.pop()
backtrackeing(0, path)
return paths
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
dic = collections.defaultdict(list)
for s in strs:
dic[''.join(sorted(s))].append(s)
return list(dic.values())
class Solution:
def isValidBST(self, root: Optional[TreeNode]) -> bool:
def recur(root, lower=-float('inf'), upper=float('inf')):
if not root: return True
if root.val <= lower or root.val >= upper:
return False
left = recur(root.left, lower, root.val)
right = recur(root.right, root.val, upper)
return left and right
return recur(root)
class Solution:
def isValidBST(self, root: Optional[TreeNode]) -> bool:
nums = []
def inorder(root):
if not root: return
inorder(root.left)
nums.append(root.val)
inorder(root.right)
inorder(root)
# 得到nums之后只需要判断它是不是有序的
for i in range(len(nums) - 1):
if nums[i+1] <= nums[i]:
return False
return True
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def flatten(self, root: Optional[TreeNode]) -> None:
"""
Do not return anything, modify root in-place instead.
"""
nums = []
def dfs(root):
if not root: return
nums.append(root.val)
dfs(root.left)
dfs(root.right)
dfs(root)
cur = root
for num in nums[1:]:
cur.left = None
if cur.right:
cur.right.val = num
else:
cur.right = TreeNode(num)
cur = cur.right
假设第i天要卖出股票,那么能赚的最大的收益就是前面最低的价格与当前价格的差。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n < 2: return 0
res = 0
min_price = float('inf')
for num in prices:
res = max(res, num - min_price)
min_price = min(min_price, num)
return res
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
n = len(nums)
if n < 2: return n
nums_set = set(nums)
longest = 0
for num in nums_set:
if num - 1 not in nums_set:
cur = 1
while num + 1 in nums_set:
cur += 1
num += 1
longest = max(longest, cur)
return longest
class Solution:
def singleNumber(self, nums: List[int]) -> int:
# x ^ 0 = x
# x ^ x = 0
res = 0
for num in nums:
res = res ^ num
return res
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
# 动态规划
wordDict = set(wordDict)
n = len(s)
dp = [False] * (n + 1)
dp[0] = True
for i in range(n):
for j in range(i+1, n+1):
if dp[i] and s[i:j] in wordDict:
dp[j] = True
return dp[-1]
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
visited = set()
cur = head
while cur:
if cur not in visited:
visited.add(cur)
cur = cur.next
else:
return True
return False
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
if not head: return False
slow, fast = head, head.next
while fast != slow:
if not fast or not fast.next: return False
slow = slow.next
fast = fast.next.next
return True
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head: return
dic = {}
cur = head
idx = 0
while cur:
if cur not in dic:
dic[cur] = idx
cur = cur.next
else:
return cur
return
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head: return
slow, fast = head, head
while True:
if not fast or not fast.next: return
slow = slow.next
fast = fast.next.next
if fast == slow: break
fast = head
while fast != slow:
fast, slow = fast.next, slow.next
return slow
class Solution:
def maxProduct(self, nums: List[int]) -> int:
n = len(nums)
if n < 2: return nums[0]
max_num, min_num = nums[0], nums[0]
p = nums[0]
for num in nums[1:]:
mx, mn = max_num, min_num
max_num = max(mx * num, num, mn * num)
min_num = min(mn * num, num, mx * num)
p = max(max_num, p)
return p
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
l1, l2 = headA, headB
while l1 != l2:
l1 = l1.next if l1 else headB
l2 = l2.next if l2 else headA
return l1
class Solution:
def majorityElement(self, nums: List[int]) -> int:
dic = collections.Counter(nums)
n = len(nums)
for k, v in dic.items():
if v > n // 2:
return k
class Solution:
def majorityElement(self, nums: List[int]) -> int:
# 使用哈希表,排序都不是最优解
# 最优的是投票
count = 0
candiadate = -1
for num in nums:
if count == 0:
candiadate = num
count += 1 if num == candiadate else -1
return candiadate
class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
if n < 3: return max(nums)
dp = [0] * n
dp[0] = nums[0]
dp[1] = max(dp[0], nums[1])
for i in range(2, n):
dp[i] = max(dp[i-2]+nums[i], dp[i-1])
return dp[-1]
class Solution:
def rob(self, nums: List[int]) -> int:
pre, cur = 0, 0
for num in nums:
pre, cur = cur, max(pre+num, cur)
return cur
class Solution:
def maximalSquare(self, matrix: List[List[str]]) -> int:
m, n = len(matrix), len(matrix[0])
dp = [[0] * n for _ in range(m)]
max_len = 0
for i in range(m):
for j in range(n):
if matrix[i][j] == '1':
if i == 0 or j == 0:
dp[i][j] = 1
else:
dp[i][j] = min(dp[i-1][j-1], dp[i][j-1], dp[i-1][j]) + 1
max_len = max(max_len, dp[i][j])
return max_len ** 2
最大矩形的解法和最大正方形的解法完全不一样。是一道很hard的题。他是直方图中最大矩形的扩展,直方图中最大矩形本身就是一个hard题了。
class Solution:
def isPalindrome(self, head: Optional[ListNode]) -> bool:
nums = []
cur = head
while cur:
nums.append(cur.val)
cur = cur.next
return nums[::-1] == nums
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
res = []
n = len(nums)
left, right = [1] * (n + 1), [1] * (n+1)
for i in range(n):
left[i+1] = left[i] * nums[i]
right[n-i-1] = right[n-i] * nums[n-i-1]
for i in range(n):
res.append(left[i] * right[i+1])
return res
最优解:
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
n = len(nums)
left, right = 1, 1
res = [1] * n
for i, num in enumerate(nums):
res[i] *= left
left *= num
res[n-i-1] *= right
right *= nums[n-i-1]
return res
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if not root: return
if root.val == p.val or root.val == q.val: return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if left and right: return root # 1
if not left: return right # 2
if not right: return left # 3
return root # 4
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
m, n = len(matrix), len(matrix[0])
i, j = 0, n - 1
while 0 <= i < m and 0 <= j < n:
if matrix[i][j] == target:
return True
elif matrix[i][j] > target:
j -= 1
elif matrix[i][j] < target:
i += 1
return False
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
if len(nums) < 2:
return
l, r = 0, 1
while r < len(nums):
if nums[l] != 0:
l += 1
elif nums[r] != 0:
nums[l], nums[r] = nums[r], nums[l]
l += 1
r += 1
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
if len(nums) < 2:
return
l, r = 0, 0
while r < len(nums):
if nums[r] != 0:
nums[l], nums[r] = nums[r], nums[l]
l += 1
r += 1
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
# O(n)时间复杂度。O(n)空间复杂度
visited = set()
for num in nums:
if num in visited: return num
visited.add(num)
return -1
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
# O(1)的空间复杂度,O(nlogn)时间复杂度
n = len(nums)
if n < 2: return nums[0]
nums.sort()
for l in range(n-1):
if nums[l] == nums[l+1]:
return nums[l]
return -1
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
n = len(nums)
if n < 2: return n
# 每一个dp[i]表示以nums[i]为结尾的最长递增子序列的长度,以及子序列的最后一个值
dp = [[1, nums[i]] for i in range(n)]
res = 1
for i in range(1, n):
for j in range(i):
if dp[j][1] < nums[i] and dp[j][0] + 1 > dp[i][0]:
dp[i][0] = dp[j][0] + 1
dp[i][1] = nums[i]
res = max(res, dp[i][0])
return res
上面代码一个冗余的地方是,dp[i][1]
就是nums[i]
。那么根本没必要做两个元素的dp了,简化之后就是这样。但是时间复杂度依然是O(n^2)
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
n = len(nums)
if n < 2: return n
dp = [1] * n
res = 1
for i in range(1, n):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[j] + 1, dp[i])
res = max(res, dp[i])
return res
还有一个O(nlogn)
的解法,就是在dp的同时,加上二分。
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
if amount == 0: return 0
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for i in range(1, amount+1):
dp[i] = min(dp[i-c] if i - c >= 0 else float('inf') for c in coins) + 1
return dp[-1] if dp[-1] != float('inf') else -1
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
if amount == 0: return 0
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for i in range(1, amount+1):
for c in coins:
if i - c >= 0:
dp[i] = min(dp[i], dp[i-c] + 1)
return dp[-1] if dp[-1] != float('inf') else -1
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
import functools
@functools.lru_cache(None)
def dfs(amount):
if amount == 0: return 0
return min(dfs(amount - c) if amount - c >= 0 else float('inf') for c in coins) + 1
res = dfs(amount)
return res if res != float('inf') else -1
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
dic = collections.Counter(nums)
tmp = sorted(dic.items(), key=lambda x: x[1], reverse=True)
res = [t[0] for t in tmp[:k]]
return res
class Solution:
def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
if root1 and root2:
root1.val += root2.val
root1.left = self.mergeTrees(root1.left, root2.left)
root1.right = self.mergeTrees(root1.right, root2.right)
return root1
else:
return root1 or root2
class Solution:
def hammingDistance(self, x: int, y: int) -> int:
x = x ^ y
res = 0
while x:
if x & 1:
res += 1
x = x >> 1
return res
class Solution:
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
nums_set = set(nums)
n = len(nums)
res = []
for i in range(1, n+1):
if i not in nums_set:
res.append(i)
return res
class Solution:
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
for num in nums:
nums[abs(num) - 1] = -abs(nums[abs(num) - 1])
res = []
for i in range(len(nums)):
if nums[i] > 0: res.append(i+1)
return res
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
# 先计算前缀和数组
n = len(nums)
cul = [0] * (n + 1)
for i in range(n):
cul[i+1] = cul[i] + nums[i]
res = 0
# 两层循环遍历前缀和数组,
for i in range(n+1):
for j in range(i+1, n+1):
if cul[j] - cul[i] == k:
res += 1
return res
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
dic = collections.defaultdict(int)
dic[0] = 1
res = 0
sum = 0
for i, num in enumerate(nums):
sum += num
if sum - k in dic:
res += dic[sum - k]
dic[sum] += 1
return res
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
n = len(temperatures)
res = [0] * n
stack = []
for i in range(n):
while stack and temperatures[i] > temperatures[stack[-1]]:
prev_index = stack.pop()
res[prev_index] = i - prev_index
stack.append(i)
return res
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n == 0: return 0
# 定义一个dp数组,每个dp的元素都有四个元素,分别代表
# dp[i][0]: 买入股票状态(或者之前已经买入股票,今天保持买入状态)
# dp[i][1]: 前两天卖出了股票,已经度过了冷冻期,今天也不买入,保持卖出状态
# dp[i][2]: 今天卖出股票
# dp[i][3]: 今天为冷冻状态
dp = [[0] * 4 for _ in range(n)]
# 初始化dp,第一天不可能卖出,也不可能处于冷冻期,只有一种可能就是买入
dp[0][0] = -prices[0]
for i in range(1, n):
dp[i][0] = max(dp[i-1][0], max(dp[i-1][3], dp[i-1][1]) - prices[i]) # 昨天买入股票和今天买入股票能获得的最大收益,其中今天买入股票的最大收益是max(昨天为冷冻状态,前两天卖出了股票)
dp[i][1] = max(dp[i-1][1], dp[i-1][3])
dp[i][2] = dp[i-1][0] + prices[i]
dp[i][3] = dp[i-1][2]
return max(dp[-1][1], dp[-1][2], dp[-1][3])
class Solution:
def countSubstrings(self, s: str) -> int:
n = len(s)
if n < 2:
return n
# 可以使用中心扩展法,依次计算以i为中心的最长回文子串,当然要注意回文子串长度是偶数
res = 0
# 奇数
for i in range(n):
# 中心扩展
l, r = i, i
while 0 <= l < n and 0 <= r < n and s[l] == s[r]:
res += 1
l -= 1
r += 1
# 偶数
for i in range(n):
l, r = i, i + 1
while 0 <= l < n and 0 <= r < n and s[l] == s[r]:
res += 1
l -= 1
r += 1
return res
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
n = len(s)
p = sorted(p)
m = len(p)
res = []
if m > n: return []
if m == n: return [0] if sorted(s) == p else []
for i in range(n - m + 1):
if sorted(s[i:i+m]) == p:
res.append(i)
return res
首先,为什么需要按照身高从高到底进行排序,因为,我们优先给高个子排队,这样之后排到矮个子的时候,即便矮个子要插队,也不会影响高个子的k。
举个例子,例如[[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
,如果我们按照身高排序[[7,0], [7,1], [6,1], [5,0], [5,2], [4,4]]
。然后我们要按照从前往后的顺序进行插入,此时按照k进行插入的时候,会发现可能存在多个相同的k,比如k==0
的有两个。因此,我们先插入7,再插入5的时候,还按照下标0来插入,他就会排在7的前面,而由于7比5高,所以,5排在前面也不会影响7。可以认为矮个子会被高个子自动无视。
class Solution:
def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
# 先按照身高由高到低进行排序,如何身高相同,按照k从小到大进行排序
people.sort(key=lambda x: (-x[0], x[1]))
queue = []
for pair in people:
queue.insert(pair[1], pair)
return queue
笨方法,在其他语言里,比如c++,这种方法速度也很快,但是在python里,这种方法速度比较慢。当然理论上的时间复杂度是一样的,这个是最暴力的方法。
class Solution:
def countBits(self, n: int) -> List[int]:
# 这里面是有规律的。假设n的二进制是k位,那么其实在前面n-1的维度上加上1,具体的首位需要加1,每个位置也要加1
res = [self.countOne(i) for i in range(n+1)]
return res
def countOne(self, n):
res = 0
while n:
res += n & 1
n = n >> 1
return res
最优解,在计算 i i i的时候,通过计算一个比 i i i小的值来间接计算出 i i i。比如,i & (i - 1)
会消掉i的最右边的1,如果有一个1,就全部变成0了。i >> 1
向右移动一位也可以把i变小,只是,此时i的最后一位不一定是0还是1,因此可以使用i & 1
来判断。
class Solution:
def countBits(self, n: int) -> List[int]:
res = [0] * (n + 1)
for i in range(1, n+1):
res[i] = res[i & (i - 1)] + 1 # i & (i - 1) 可以去掉i最右边的一个1,如果只有一个1,就变成全0了,因为去掉了一个1,所以肯定比i小,那么肯定之前就计算过了,再加上去掉的右边那个1就行了
return res
class Solution:
def countBits(self, n: int) -> List[int]:
res = [0] * (n + 1)
for i in range(1, n+1):
res[i] = res[i >> 1] + (i & 1)
return res
class Solution:
def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
self.ma = 0
if not root: return 0
def dfs(root):
# 叶子节点
if not root.left and not root.right:
return 0
if root.left:
leftSize = dfs(root.left) + 1
else:
leftSize = 0
if root.right:
rightSize = dfs(root.right) + 1
else:
rightSize = 0
self.ma = max(self.ma, leftSize+rightSize)
return max(leftSize, rightSize)
dfs(root)
return self.ma
class Solution:
def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
self.ma = 0
if not root: return 0
def dfs(root):
if not root: return 0
l = dfs(root.left)
r = dfs(root.right)
self.ma = max(l + r + 1, self.ma)
return max(l, r) + 1
dfs(root)
return self.ma - 1
class Solution:
def rob(self, root: Optional[TreeNode]) -> int:
# 对于一个节点node,有两种选择,偷或者不偷
# 偷:能获得的最大收益是不偷左右子节点的最大收益加上当前节点的最大收益
# 不偷:那么子节点可以偷也可以不偷,因此node节点的最大收益就是计算出左右子节点偷或者不偷的最大收益
def dfs(root):
if not root: return (0, 0) # (偷的最大收益,不偷的最大收益)
l, r = dfs(root.left), dfs(root.right)
is_rob = l[1] + r[1] + root.val # 偷该节点,那么该节点的最大收益就是该节点的值加上不偷左子节点,不偷右子节点的最大收益
no_rob = max(l[0], l[1]) + max(r[0], r[1]) # 不偷该节点,那么该节点的最大收益就是左叶子节点的最大收益加上右叶子节点的最大收益(左右子节点偷或者不偷都可以)
return (is_rob, no_rob)
return max(dfs(root))
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
# 思路就是找到每个height位置左右两边比他低的第一个柱子,然后他们之间的差减1就是以height作为短板最大的矩形
n = len(heights)
left, right = [0] * n, [0] * n
stack = []
for i in range(n):
while stack and heights[i] <= heights[stack[-1]]:
stack.pop()
# 当循环结束的时候,栈顶就是比heights[i]小的最近的柱子
left[i] = stack[-1] if stack else -1
stack.append(i)
stack = []
for i in range(n-1, -1, -1):
while stack and heights[i] <= heights[stack[-1]]:
stack.pop()
# 当循环结束的时候,栈顶就是右边比heights[i]小的最近的柱子
right[i] = stack[-1] if stack else n
stack.append(i)
res = max([(right[i] - left[i] - 1) * heights[i] for i in range(n)]) if n > 0 else 0
return res
class Solution:
def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
self.total = 0
def reverse_inorder(root):
if not root: return
# global total
reverse_inorder(root.right)
root.val = root.val + self.total
self.total = root.val
reverse_inorder(root.left)
reverse_inorder(root)
return root
class Trie:
def __init__(self):
self.data = set()
def insert(self, word: str) -> None:
self.data.add(word)
def search(self, word: str) -> bool:
return word in self.data
def startsWith(self, prefix: str) -> bool:
# 这一步是缩短时间的核心
n = len(prefix)
for word in self.data:
if len(word) >= n:
if word[:n] == prefix:
return True
return False
# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)
上面的是最暴力的解法,肯定不是最优解,其中搜索前缀这一步复杂度太高了,对于前缀树的应用场景,就是一次建树,多次查询。所以需要将查询前缀的复杂度降下去。
最优实现就是实现一个26叉树,因为有26个字母,第i层节点保存所有word的第i个位置的字母。
class Solution:
def decodeString(self, s: str) -> str:
# 不能简单循环模拟,因为可能有嵌套,项示例2那种
n = len(s)
stack, res, multi = [], '', 0
for c in s:
if c == '[':
stack.append([res, multi])
res, multi = '', 0
elif c == ']':
last_res, cur_multi = stack.pop()
res = last_res + cur_multi * res
elif '0' <= c <= '9':
multi = 10 * multi + int(c)
else:
res += c
return res
class Solution:
def canPartition(self, nums: List[int]) -> bool:
# 0-1背包问题,背包容量是sum // 2,看看能否刚好装满一个sum // 2大小的背包,一旦可以装,那么剩下的自然可以装另一个了
if sum(nums) % 2 == 1: return False
total = sum(nums) // 2 # 背包容量
n = len(nums) # 总的物品,其价值等于其体积
# nums.sort()
dp = [[0] * (total + 1) for _ in range(n)]
for i in range(1, total+1):
dp[0][i] = nums[0] if i >= nums[0] else 0
# 开始dp
for i in range(1, n):
# 这里从后往前
for j in range(1, total+1):
if j < nums[i]:
dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i]] + nums[i]) # 这里都是用的是前一行的数据,因此可以使用滚动数组的方式,将二维dp转化到一维
# 只要最后一列有一个等于target的,理论上下面应该全是等于target
return dp[-1][-1] == total
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums) % 2 == 1: return False
target = sum(nums) // 2
n = len(nums)
dp = [0] * (target + 1)
# 初始化dp
for i in range(target + 1):
dp[i] = nums[0] if i >= nums[0] else 0
# 开始dp
for i in range(1, n):
# 从后往前遍历是因为后面的数要用前面的数来算,所以如果先算前面的话,之前的数就被顶掉了,
for j in range(target, nums[i]-1, -1):
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
return dp[-1] == target
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
total = sum(nums)
if abs(target) > total: return 0
if (total + target) % 2 == 1: return 0
n = len(nums)
bagSize = (total + target) // 2 # 注意背包容量的计算,因为背包问题都是加法,因此我们也要转换成加法,假设加法总和是x,减法总和是sum-x,那么x-(sum - x) = target, x=(sum + target) // 2,如果是奇数,肯定无解,所以上面提前返回
dp = [0] * (bagSize + 1)
dp[0] = 1
for i in range(n):
for j in range(bagSize, nums[i]-1, -1):
dp[j] += dp[j - nums[i]]
return dp[-1]
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next: return head
slow, fast = head, head.next
while fast and fast.next:
slow, fast = slow.next, fast.next.next
mid, slow.next = slow.next, None # 从中间断开
left, right = self.sortList(head), self.sortList(mid)
# 开始归并
h = res = ListNode(0)
while left and right:
if left.val < right.val: h.next, left = left, left.next
else: h.next, right = right, right.next
h = h.next
h.next = left if left else right
return res.next
class Solution:
def numTrees(self, n: int) -> int:
if n < 3: return n
dp = [0] * (n + 1)
dp[0] = 1
dp[1] = 1
for i in range(2, n+1):
for j in range(1, i+1):
dp[i] += dp[j-1] * dp[i-j]
return dp[-1]
删除一个节点之后,重新建立的树可能有多种,只要找到一种就行。那么最简单的一种就是找到目标节点的右子树上的最小节点的值,用它代替被删除的节点,因为右子树上最小的几点肯定是沿着右子树一直往左走,一直走到没有左子节点。也就是说右子树上最小的节点一定是左子节点为空,因此再回溯删除这个值的时候就不会再次进入这个if。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
# dfs
if not root: return
if root.val < key:
root.right = self.deleteNode(root.right, key)
elif root.val > key:
root.left = self.deleteNode(root.left, key)
else:
# 如果子节点有一个是空的
if not root.left or not root.right:
root = root.left if root.left else root.right
else:
# 找到右子树最小的节点,将其赋给当前节点,然后删除右子树的最小节点
cur = root.right
while cur.left: cur = cur.left
root.val = cur.val
root.right = self.deleteNode(root.right, cur.val)
return root
非最优解法:
class Solution:
def findUnsortedSubarray(self, nums: List[int]) -> int:
# 先排序,排完序之后进行对比,找到同样位置不相同的最后一个位置,以及倒着找最后一个就可以了。
n = len(nums)
sort_nums = sorted(nums)
l, r = -1, -1
for i in range(n):
if nums[i] != sort_nums[i] and l == -1:
l = i
if nums[i] != sort_nums[i]:
r = i
if l == r: return 0
return r - l + 1
class Solution:
def findUnsortedSubarray(self, nums: List[int]) -> int:
n = len(nums)
ma, mi = nums[0], nums[-1]
l, r = -1, -1
for i in range(n):
ma = max(nums[i], ma)
if nums[i] < ma:
r = i
for j in range(n-1, -1, -1):
mi = min(nums[j], mi)
if nums[j] > mi:
l = j
if l == r: return 0
return r - l + 1