目录
一、双指针
二、排序
三、贪心算法
四、二分查找
五、分治算法
六、搜索(DFS&BFS)
七、动态规划
八、递归
九、回溯
十、查并集
十一、滑动窗口
十二、记忆化搜索
一、双指针
题号:86、167、209
【典型题目】给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target
1 class Solution:
2
3 def twoSum(self, numbers: List[int], target: int) -> List[int]:
4
5 i = 0
6
7 j = len(numbers) - 1
8
9 while i < j:
10
11 if target - numbers[j] == numbers[i]:
12
13 return [i + 1, j + 1]
14
15 elif numbers[i] + numbers[j] > target:
16
17 j -= 1
18
19 else:
20
21 i += 1
22
23 return []
二、排序
题号:207、210
1. 转换成堆,堆 :时间复杂度 O(NlogK),空间复杂度 O(K)。
【典型题目】在某个集合中找出最大或最小的N个元素
1 import heapq 2 3 >>> nums=[1,8,2,23,7,-4,18,23,42,37,2] 4 5 >>> print(heapq.nlargest(3,nums)) 6 7 [42, 37, 23] 8 9 >>> print(heapq.nsmallest(3,nums)) 10 11 [-4, 1, 2]
【典型题目】如果正在寻找最大或者最小的N个元素,且同集合中元素的总数目相比,N很小,那么下面这些函数就可以提供更好的性能。
这些函数首先会在底层将数据转化成列表,且元素会以堆得顺序排列。例如:
1 >>> nums=[1,8,2,23,7,-4,18,23,42,37,2] 2 3 >>> import heapq 4 5 >>> heap=list(nums) 6 7 >>> heapq.heapify(heap)#将列表原地转换成堆 8 9 >>> heap 10 11 [-4, 2, 1, 23, 7, 2, 18, 23, 42, 37, 8]
2. 排序 :时间复杂度 O(NlogN),空间复杂度 O(1)
3. 快速选择 :时间复杂度 O(N),空间复杂度 O(1)
4. 拓扑排序:是专门应用于有向图的算法,BFS 的写法就叫「拓扑排序」,这里还用到了贪心算法的思想,贪的点是:当前让入度为 0 的那些结点入队
三、贪心算法
题号:322、1217、55
四、二分查找
题号:704、35、162、74
【典型题目】给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1
1 class Solution: 2 3 def search(self, nums: List[int], target: int) -> int: 4 5 left, right = 0, len(nums)-1 6 7 while left <= right: 8 9 mid = left + (right - left) // 2 10 11 if nums[mid] > target: 12 13 right = mid - 1 14 15 elif nums[mid] < target: 16 17 left = mid + 1 18 19 elif nums[mid] == target: 20 21 return mid 22 23 return -1
【典型题目】编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值
1 class Solution: 2 3 def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: 4 5 n , m = len(matrix) , len(matrix[0]) 6 7 if m == 0 or n == 0 :return False 8 9 l , r = 0 , m * n - 1 10 11 while (l <= r): 12 13 mid = (l + r) // 2 14 15 if(matrix[mid // m][mid % m] > target): 16 17 r = mid - 1 18 19 elif(matrix[mid // m][mid % m] < target): 20 21 l = mid + 1 22 23 else: 24 25 return True 26 27 return False
五、分治算法
题号:169、53
【典型题目】给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
注:便捷算法
1 class Solution: 2 3 def maxSubArray(self, nums: List[int]) -> int: 4 5 res = nums[0] 6 7 sum = 0 8 9 for num in nums: 10 11 if sum > 0 : 12 13 sum += num 14 15 else: 16 17 sum = num 18 19 res = max(res,sum) 20 21 return res
六、搜索(DFS&BFS)
1. DFS 题号:938、78、200
【典型题目】给定二叉搜索树的根结点 root,返回值位于范围 [low, high] 之间的所有结点的值的和。
1 class Solution: 2 3 def rangeSumBST(self, root: TreeNode, low: int, high: int) -> int: 4 5 if root==None: 6 7 return 0 8 9 ans_left = self.rangeSumBST(root.left, low, high) 10 11 ans_right = self.rangeSumBST(root.right, low, high) 12 13 rootval = root.val if low<=root.val<=high else 0 14 15 return ans_left + ans_right + rootval
【典型题目】给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
方法一==>DFS:
1 class Solution: 2 3 def dfs(self, res, nums, cur, index): 4 5 if index >= len(nums): 6 7 res.append(cur.copy()) # 注意这里要用copy 要不然cur的改变会影响到res里的cur 8 9 return 10 11 # 下面有两个dfs 表示每个状态有两个子状态可供选择 12 13 # 每个index前后保证状态不变 14 15 cur.append(nums[index]) 16 17 self.dfs(res, nums, cur, index + 1) 18 19 cur.pop() 20 21 self.dfs(res, nums, cur, index + 1) 22 23 24 25 def subsets(self, nums: List[int]) -> List[List[int]]: 26 27 # 本题定义状态为(cur, index) 28 29 res = [] 30 31 self.dfs(res, nums, [], 0) 32 33 return res
方法二==>递归:
1 class Solution(object): 2 3 def subsets(self, nums): 4 5 if nums ==[]: 6 7 return [[]] 8 9 return self.subsets(nums[:-1]) + [i+[nums[-1]] for i in self.subsets(nums[:-1])]
【典型题目】岛屿数量
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
方法一==>DFS
1 class Solution: 2 3 def numIslands(self, grid: List[List[str]]) -> int: 4 5 if not grid: return 0 6 7 8 9 count = 0 10 11 for i in range(len(grid)): 12 13 for j in range(len(grid[0])): 14 15 if grid[i][j] == '1': 16 17 self.dfs(grid, i, j) 18 19 count += 1 20 21 22 23 return count 24 25 26 27 def dfs(self, grid, i, j): 28 29 if i < 0 or j < 0 or i >= len(grid) or j >= len(grid[0]) or grid[i][j] != '1': 30 31 return 32 33 grid[i][j] = '0' 34 35 self.dfs(grid, i + 1, j) 36 37 self.dfs(grid, i - 1, j) 38 39 self.dfs(grid, i, j + 1) 40 41 self.dfs(grid, i, j - 1)
方法二==>BFS
1 class Solution: 2 3 def numIslands(self, grid: List[List[str]]) -> int: 4 5 count = 0 6 7 for row in range(len(grid)): 8 9 for col in range(len(grid[0])): 10 11 if grid[row][col] == '1': # 发现陆地 12 13 count += 1 # 结果加1 14 15 grid[row][col] = '0' # 将其转为 ‘0’ 代表已经访问过 16 17 # 对发现的陆地进行扩张即执行 BFS,将与其相邻的陆地都标记为已访问 18 19 # 下面还是经典的 BFS 模板 20 21 land_positions = collections.deque() 22 23 land_positions.append([row, col]) 24 25 while len(land_positions) > 0: 26 27 x, y = land_positions.popleft() 28 29 for new_x, new_y in [[x, y + 1], [x, y - 1], [x + 1, y], [x - 1, y]]: # 进行四个方向的扩张 30 31 # 判断有效性 32 33 if 0 <= new_x < len(grid) and 0 <= new_y < len(grid[0]) and grid[new_x][new_y] == '1': 34 35 grid[new_x][new_y] = '0' # 因为可由 BFS 访问到,代表同属一块岛,将其置 ‘0’ 代表已访问过 36 37 land_positions.append([new_x, new_y]) 38 39 return count
2. BFS 题号:102、107、200
【典型题目】102题:给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
当遍历到新的一层的时候(也就是depth>=res数组的长度的时候),就往res里append一个空列表用于存储新的一层的结点值
同时往该结点对应那一层的列表里(res[depth])append结点的值对该结点的左子节点和右子节点重复以上工作,由于是从上到下存储的各层元素,所以最后需要倒转一下列表。
DFS
1 class Solution: 2 3 def levelOrderBottom(self, root: TreeNode) -> List[List[int]]: 4 5 res = [] 6 7 def dfs(root, depth): 8 9 if not root: 10 11 return 12 13 if len(res)<depth+1: 14 15 res.append([]) 16 17 res[depth].append(root.val) 18 19 dfs(root.left, depth+1) 20 21 dfs(root.right, depth+1) 22 23 dfs(root,0) 24 25 return res[::-1]
七、动态规划
题号:62、322、1143、509、121、70、279、22
【典型题目】给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。你可以认为每种硬币的数量是无限的。
1 class Solution: 2 3 def coinChange(self, coins: List[int], amount: int) -> int: 4 5 dp = [float('inf')] * (amount + 1) 6 7 dp[0] = 0 8 9 10 11 for coin in coins: 12 13 for i in range(coin, amount + 1): 14 15 dp[i] = min(dp[i], 1 + dp[i - coin]) 16 17 18 19 return dp[amount] if dp[amount] != float('inf') else -1
【典型题目】给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
若这两个字符串没有公共子序列,则返回 0。
1 class Solution(object): 2 3 def longestCommonSubsequence(self, text1, text2): 4 5 """ 6 7 :type text1: str 8 9 :type text2: str 10 11 :rtype: int 12 13 """ 14 15 if not text1 or not text2: 16 17 return 0 18 19 #base case 20 21 n = len(text1) 22 23 m = len(text2) 24 25 dp = [[0 for x in range(n+1)] for y in range(m+1)] 26 27 for i in range(1,m+1): 28 29 for j in range(1,n+1): 30 31 if text1[j-1] == text2[i-1]: 32 33 dp[i][j] = dp[i-1][j-1] + 1 34 35 else: 36 37 dp[i][j] = max(dp[i-1][j], dp[i][j-1]) 38 39 return dp[m][n]
八、递归
题号:509、206、344、687
【典型题目】斐波那契数列
方法一==>纯递归
1 class Solution: 2 3 def fib(self, n: int) -> int: 4 5 if n == 0 or n == 1 : return n 6 7 else: 8 9 return self.fib(n - 1) + self.fib( n - 2)
方法二==>递归+记忆法(数组)
1 class Solution: 2 3 def fib(self, n: int) -> int: 4 5 memo=[0,1] 6 7 i=2 8 9 if n<2: 10 11 return memo[n] 12 13 while i<=n: 14 15 memo.append(memo[i-1]+memo[i-2]) 16 17 i+=1 18 19 return memo[-1]
方法三==>动态规划
1 class Solution: 2 3 def fib(self, n: int) -> int: 4 5 if n == 0: 6 7 return 0 8 9 if n <= 2: 10 11 return 1 12 13 first = 1 14 15 second = 1 16 17 for i in range(3, n+1): 18 19 first, second = second, first + second 20 21 return second
【典型题目】反转一个链表
1 class Solution: 2 3 def reverseList(self, head: ListNode) -> ListNode: 4 5 if not head or head.next == None: return head 6 7 res = self.reverseList(head.next) 8 9 head.next.next = head 10 11 head.next = None 12 13 return res
【典型题目】反转字符串
双指针法
1 class Solution: 2 3 def reverseString(self, s: List[str]) -> None: 4 5 """ 6 7 Do not return anything, modify s in-place instead. 8 9 """ 10 11 # double pointer 12 13 i,j = 0,len(s)-1 14 15 while i<j: 16 17 s[i],s[j] = s[j],s[i] 18 19 i+=1;j-=1
九、回溯
题号:22、78、77、46
【典型题目】给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
1 class Solution: 2 3 def combine(self, n: int, k: int) -> List[List[int]]: 4 5 res = [] 6 7 def backtrack(i, k, tmp): 8 9 if k == 0: 10 11 res.append(tmp) 12 13 return 14 15 for j in range(i, n + 1): 16 17 backtrack(j+1, k-1, tmp + [j]) 18 19 backtrack(1, k, []) 20 21 return res
十、并查集
题号:200、547、721
十一、滑动窗口
题号:209、1456
十二、记忆化搜索
题号:509、322