classSolution: deftwoSum(self, nums: List[int], target: int) -> List[int]: mem = defaultdict() for i inrange(len(nums)): if target - nums[i] in mem: return [mem[target-nums[i]], i] mem[nums[i]] = i return []
49. 字母异位词分组
排序然后用dict存起来就行了
1 2 3 4 5 6 7 8 9 10 11
from collections import defaultdict
classSolution: defgroupAnagrams(self, strs: List[str]) -> List[List[str]]: seen = defaultdict(list) for st in strs: seen["".join(sorted(st))].append(st) ans = [] for k, v in seen.items(): ans.append(v) return ans
128. 最长连续序列
先用一个 set 来去重,然后对于每一个元素,如果有比它刚好-1的元素在set里面就继续(因为这样必然会有长度大于该起点的序列);如果没有的话,就+1的向后遍历即可。
1 2 3 4 5 6 7 8 9 10 11 12
classSolution: deflongestConsecutive(self, nums: List[int]) -> int: set_nums = set(nums) ans = 0 for num in set_nums: if num-1in set_nums: continue y = num+1 while y in set_nums: y += 1 ans = max(ans, y-num) return ans
283. 移动零
left 指针指向的是已经处理好的序列的末尾, right 指针指向的是待处理序列的头部。
左指针左边均为非零数;右指针左边直到左指针处均为零。
1 2 3 4 5 6 7 8 9 10 11 12
classSolution: defmoveZeroes(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ n = len(nums) left, right = 0, 0 while right < n: if nums[right] != 0: nums[left], nums[right] = nums[right], nums[left] left += 1 right += 1
classSolution: defthreeSum(self, nums: List[int]) -> List[List[int]]: ans = [] n = len(nums) nums = sorted(nums) for k inrange(n): # 去重 if k > 0and nums[k] == nums[k-1]: k += 1 continue if nums[k] > 0: continue
left, right = k+1, n-1
while left < right: sum_up = nums[left]+nums[right]
if sum_up == -nums[k]: ans.append([nums[left], nums[right], nums[k]]) # 去重 while left < right and nums[left] == nums[left + 1]: left += 1 while left < right and nums[right] == nums[right - 1]: right -= 1 left += 1 right -= 1
elif sum_up < -nums[k]: left += 1 else: right -= 1 return an
classSolution: deftrap(self, height: List[int]) -> int: n = len(height) ans = 0 left_max, right_max = [0for i inrange(n)], [0for i inrange(n)] left_max[0] = height[0] right_max[n-1] = height[n-1] for i inrange(n-1): left_max[i+1] = max(left_max[i], height[i+1]) for i inrange(n - 2, -1, -1): right_max[i] = max(right_max[i + 1], height[i]) for i inrange(n): ans += min(left_max[i], right_max[i]) - height[i] return ans
classSolution: defnumIslands(self, grid: List[List[str]]) -> int: m, n = len(grid), len(grid[0]) res = 0 defcheck(i, j, m, n): return (0<=i<=m-1) and (0<=j<=n-1) defdfs(grid, i, j): m, n = len(grid), len(grid[0]) ifnot check(i, j, m, n) or grid[i][j] != '1': return0 grid[i][j] = '2'
dfs(grid, i-1, j) dfs(grid, i, j+1) dfs(grid, i, j-1) dfs(grid, i+1, j) for i inrange(m): for j inrange(n): if grid[i][j] == '1': dfs(grid, i, j) res += 1 return res
from collections import deque classSolution: deforangesRotting(self, grid: List[List[int]]) -> int: m, n = len(grid), len(grid[0]) res = 0 defcheck(i,j): return (0<=i<=m-1) and (0<=j<=n-1)
queue = deque() fresh_oranges = 0 for i inrange(m): for j inrange(n): if grid[i][j] == 2: queue.append((i,j)) if grid[i][j] == 1: fresh_oranges += 1 while queue: new_queue = [] old_queue_len = len(queue) while old_queue_len: old_queue_len -= 1 cur_orange = queue.popleft() i, j = cur_orange for di, dj in [(-1,0),(1,0),(0,-1),(0,1)]: ni, nj = i + di, j + dj if check(ni, nj) and grid[ni][nj] == 1: grid[ni][nj] = 2 fresh_oranges -= 1 new_queue.append((ni, nj)) if new_queue: res += 1 queue = deque(new_queue) else: break
classSolution: defcanFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: graph = {i: [] for i inrange(numCourses)} for a, b in prerequisites: graph[b].append(a)
state = [0] * numCourses defdfs(node): if state[node] == 2: returnTrue if state[node] == 1: returnFalse neighbours = graph[node] state[node] = 1 for n in neighbours: ifnot dfs(n): returnFalse state[node] = 2 returnTrue res = True for n in graph.keys(): if state[n] == 0: res = dfs(n) and res
from collections import deque classSolution: defcanFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: graph = {i: [] for i inrange(numCourses)} indegree = [0] * numCourses for a, b in prerequisites: graph[b].append(a) indegree[a] += 1 queue = deque([i for i inrange(numCourses) if indegree[i] == 0]) res = 0 while queue: cur = queue.popleft() res += 1 for nei in graph[cur]: indegree[nei] -= 1 if indegree[nei] == 0: queue.append(nei) return res == numCourses
208. 实现 Trie (前缀树)
前缀树的核心思想是让前缀一样的单词共享前缀一样的部分,这样能够加快进行查找的速度。实例如下:
1 2 3 4 5 6 7 8
(root) ├── c │ └── a │ ├── t (is_end=True) │ └── r (is_end=True) └── d └── o └── g (is_end=True)
classSolution: defcombinationSum(self, candidates: List[int], target: int) -> List[List[int]]: n = len(candidates) res = []
defdfs(path, total, start): if total == target: res.append(path[:]) return if total > target: return for i inrange(start, n): path.append(candidates[i]) dfs(path, total+candidates[i], i) path.pop()
classSolution: defpartition(self, s: str) -> List[List[str]]: n = len(s) res = [] defcheck(path): s_in = ''.join(path) left, right = 0, len(s_in)-1 while left <= right: if s_in[left] != s_in[right]: returnFalse left += 1 right -= 1 returnTrue
defdfs(start, path): if start == n: res.append(path[:]) return for i inrange(start, n): substring = s[start:i+1] if check(substring): path.append(substring) dfs(i+1, path) path.pop()
classSolution: defsearchRange(self, nums: List[int], target: int) -> List[int]: defbinary_search_left(nums, target): left, right = 0, len(nums)-1 while left <= right: mid = (left+right) // 2 if nums[mid] >= target: right = mid - 1 else: left = mid + 1 return left defbinary_search_right(nums, target): left, right = 0, len(nums)-1 while left <= right: mid = (left+right) // 2 if nums[mid] <= target: left = mid + 1 else: right = mid - 1 return right left = binary_search_left(nums, target) right = binary_search_right(nums, target) if left <= right and right < len(nums) and nums[left] == target and nums[right] == target: return [left, right] else: return [-1, -1]
33. 搜索旋转排序数组
无论如何,在mid分开的部分总有一边是有序的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
classSolution: defsearch(self, nums: List[int], target: int) -> int: left, right = 0, len(nums)-1 while left <= right: mid = (left+right) // 2 if nums[mid] == target: return mid
if nums[0] <= nums[mid]: if nums[left] <= target <= nums[mid]: right = mid-1 else: left = mid+1 else: if nums[mid] <= target <= nums[len(nums)-1]: left = mid+1 else: right = mid-1 return -1
classSolution: deffindMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float: m, n = len(nums1), len(nums2) if m > n: nums1, nums2 = nums2, nums1 m, n = n, m left_size = (m+n+1)//2 left, right = 0, m while left < right: i = (left+right+1) // 2 j = left_size - i if nums1[i-1] > nums2[j]: right = i - 1 else: left = i i = left j = left_size - i nums1_left = nums1[i-1] if i > 0elsefloat("-inf") nums1_right = nums1[i] if i < m elsefloat("inf") nums2_left = nums2[j-1] if j > 0elsefloat("-inf") nums2_right = nums2[j] if j < n elsefloat("inf")
for i inrange(n): while stack and temperatures[i] > temperatures[stack[-1]]: answer[stack[-1]] = i - stack[-1] stack.pop() stack.append(i) return answer
whileTrue: while i<=j and nums[j] > pivot: j -= 1 while i<=j and nums[i] < pivot: i += 1 if i >= j: break nums[i], nums[j] = nums[j], nums[i] j -= 1 i += 1 nums[left], nums[j] = nums[j], nums[left] return j left, right = 0, n-1 while left<=right: index = partition(nums, left, right) if index == n-k: return nums[index] elif index < n-k: left = index+1 else: right = index-1
classSolution: deftopKFrequent(self, nums: List[int], k: int) -> List[int]: cnt = {} for num in nums: cnt[num] = cnt.get(num, 0) + 1 res, li = [], [] for count in cnt.values(): li.append(count) defpartition(nums, left, right): pivot_index = random.randrange(left, right+1) nums[left], nums[pivot_index] = nums[pivot_index], nums[left] pivot = nums[left] i, j = left+1, right
whileTrue: while i<=j and nums[j] < pivot: j -= 1 while i<=j and nums[i] > pivot: i += 1 if i >= j: break nums[i], nums[j] = nums[j], nums[i] i += 1 j -= 1 nums[left], nums[j] = nums[j], nums[left] return j
defquick_sort(nums, left, right): while left <= right: index = partition(nums, left, right) if index == k-1: break# 前面都是出现次数比k还大的 elif index < k-1: left = index + 1 else: right = index - 1 quick_sort(li, 0, len(li)-1) for key, v in cnt.items(): if v >= li[k-1]: res.append(key) return res
桶排序, 0(n) 复杂度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution: deftopKFrequent(self, nums: List[int], k: int) -> List[int]: cnt = {} for num in nums: cnt[num] = cnt.get(num, 0) + 1 bucket = [[] for _ inrange(len(nums)+1)] res = [] for key, value in cnt.items(): bucket[value].append(key) for i inrange(len(bucket)-1, -1, -1): if bucket[i] != [] andlen(res) < k: for num in bucket[i]: res.append(num) iflen(res) == k: break return res
classSolution: defmaxProfit(self, prices: List[int]) -> int: cur_min, ans = float("inf"), 0 for p in prices: if p < cur_min: cur_min = p ans = max(ans, p-cur_min) return ans if cur_min != float("inf") else0
55. 跳跃游戏
每次能够到达的最远距离为 max(当前所在的位置+每次能够移动的最大值,之前的最远到达距离)
1 2 3 4 5 6 7 8 9 10
classSolution: defcanJump(self, nums: List[int]) -> bool: reachable = 0 for i, step inenumerate(nums): if reachable < i: returnFalse reachable = max(reachable, i+step) if reachable > len(nums): returnTrue returnTrue
classSolution: defpartitionLabels(self, s: str) -> List[int]: last, res = {}, [] for i, c inenumerate(s): last[c] = i res, prev, furtherest = [], 0, 0 for i, c inenumerate(s): furtherest = max(furtherest, last[c]) if i == furtherest: res.append(i - prev + 1) prev = i+1 return res
70. 爬楼梯
1 2 3 4 5 6 7 8 9
classSolution: defclimbStairs(self, n: int) -> int: if n == 1: return1 dp = [0] * n dp[0] = 1 dp[1] = 2 for i inrange(2, n): dp[i] = dp[i-1] + dp[i-2] return dp[n-1]
118. 杨辉三角
1 2 3 4 5 6 7 8 9 10 11 12
classSolution: defgenerate(self, numRows: int) -> List[List[int]]: if numRows == 1: return [[1]] res = [[1]] for i inrange(2, numRows+1): cur = [1] last = res[-1] for j inrange(1, i-1): cur.append(last[j]+last[j-1]) cur.append(1) res.append(cur) return res
classSolution: defcoinChange(self, coins: List[int], amount: int) -> int: if amount == 0: return0 dp = [float("inf")] * (amount+1) dp[0] = 0 for coin in coins: for i inrange(coin, amount+1): dp[i] = min(dp[i], dp[i-coin]+1) return dp[amount] if dp[amount]!=float("inf") else -1
139. 单词拆分
记忆化搜索,遍历过的串就存起来就行了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classSolution: defwordBreak(self, s: str, wordDict: List[str]) -> bool: memo = {} defdfs(s): iflen(s) == 0: returnTrue if s in memo: return memo[s] for word in wordDict: if s[:len(word)] == word: if dfs(s[len(word):]): memo[s] = True returnTrue memo[s] = False returnFalse return dfs(s)
动态规划
dp[i] 表示 s 的前 i 个字符能否被字典切分;对于每个可切分的位置 i,尝试用字典中的单词向后扩展,如果 s[i:j] 是单词,就让 dp[j] = True。
1 2 3 4 5 6 7 8 9 10 11 12 13
classSolution: defwordBreak(self, s: str, wordDict: List[str]) -> bool: n = len(s) wordDict = set(wordDict) dp = [True] + [False] * n max_len = 0 for word in wordDict: max_len = max(max_len, len(word)) for i inrange(n): for j inrange(i+1, min(n+1, i+max_len+1)): if s[i:j] in wordDict and dp[i]: dp[j] = True return dp[n]
300. 最长递增子序列
题目只要求写出数值即可,这里同时还打印出来了序列应该是什么样的。
具体做法是用一个dp数组来表示当前数字可以到达的最大递增子序列。对于每一个位置,都从0开始遍历到当前位置,如果满足 $nums[j] < nums[i] and dp[j]+1 > dp[i]$ 就是更新当前的dp[i]。这里的时间复杂度是 O(n)
defbinary_search(nums, x): ifnot nums: return0 left, right = 0, len(nums)-1 while left <= right: mid = (left+right) // 2 if nums[mid] < x: left = mid+1 else: right = mid-1 return left
for num in nums: position = binary_search(tails, num) if position == len(tails): tails.append(num) else: tails[position] = num returnlen(tails)
classSolution: defcanPartition(self, nums: List[int]) -> bool: n = len(nums) nums.sort(reverse=True) total = sum(nums) if total % 2 != 0: returnFalse target = total // 2 dp = [False]*(target+1) dp[0] = True for num in nums: for j inrange(target, num-1, -1): dp[j] = dp[j] or dp[j-num] return dp[target]
classSolution: deflongestPalindrome(self, s: str) -> str: res = [] n = len(s) max_length = 0 for i inrange(2*n-1): left = i // 2 right = left + i % 2 while left >=0and right <= n-1and s[left] == s[right]: if right-left+1 > max_length: max_length = right-left+1 res = s[left: right+1] left -= 1 right += 1 return res
classSolution: defsingleNumber(self, nums: List[int]) -> int: iflen(nums) == 1: return nums[0] ans = nums[0] for i inrange(1, len(nums)): ans = ans^nums[i] return ans
169. 多数元素
留下来的最终的肯定是投票数最多的。
1 2 3 4 5 6 7 8 9 10 11 12
classSolution: defmajorityElement(self, nums: List[int]) -> int: candidate = None count = 0 for num in nums: if count == 0: candidate = num # 更换候选人 if num == candidate: count += 1 else: count -= 1 return candidate
75. 颜色分类
由于只有三种颜色,遍历数组排序好其中的两个就行了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution: defsortColors(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ ptr = 0 n = len(nums) for i inrange(n): if nums[i] == 0: nums[i], nums[ptr] = nums[ptr], nums[i] ptr += 1 # 此时ptr前面应该全都是0了 for i inrange(ptr, n): if nums[i] == 1: nums[i], nums[ptr] = nums[ptr], nums[i] ptr += 1
31. 下一个排列
从右往左找第一个递增对 x[i] < x[i+1]
找到以后在 i 之后寻找第一个大于 x[i] 的数字
然后交换这两个数字,并把 i 后面的序列全部翻转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
classSolution: defnextPermutation(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ n = len(nums) if n == 1: return nums[0] index = -1 for i inrange(n-2, -1, -1): if nums[i] < nums[i+1]: index = i break if index == -1: return nums.reverse() for j inrange(n-1, index, -1): if nums[j] > nums[index]: nums[j], nums[index] = nums[index], nums[j] break nums[index+1:] = reversed(nums[index+1:]) return nums
287. 寻找重复数
最简单的方法就是排序然后遍历, 时间复杂度 O(nlogn)
这里用快慢指针实现 O(n) 的做法。实际上就是把这一题看作环形链表来做。
1 2 3 4 5 6 7 8 9 10 11 12 13
classSolution: deffindDuplicate(self, nums: List[int]) -> int: slow, fast = 0, 0 whileTrue: slow = nums[slow] fast = nums[nums[fast]] if slow == fast: break head = 0 while head!=slow: head = nums[head] slow = nums[slow] return slow