目录

11.盛最多水的容器

15. 三数之和

16. 最接近的三数之和

18. 四数之和

31. 下一个排列

33. 搜索旋转排序数组

34. 在排列数组中查找元素的第一个和最后一个位置

36. 有效的数独

39. 组合总和

40. 组合总和

45. 跳跃游戏

46. 全排列

47. 全排列(含重复元素)

48. 旋转图像

54. 螺旋矩阵

55. 跳跃游戏

56. 合并区间

57. 插入区间​

59. 螺旋矩阵

63. 不同路径

64. 最小路径和

73. 矩阵置零

74. 搜索二维矩阵

75. 颜色分类

77. 组合

78. 子集

79. 单词搜索

80. 删除有序数组中的重复项​

81. 搜索旋转排序数组

90. 子集


11.盛最多水的容器

# 超出时间限制
class Solution(object):def maxArea(self, height):""":type height: List[int]:rtype: int"""n = len(height)li = []for i in range(n-1):area = 0for j in range(i+1, n):area = max(area, (j-i) * min(height[i], height[j]))li.append(area)return max(li)

稍微动脑子想想就知道中等题不能再暴力解法了......利用双指针解法,每次移动较小的边界进行遍历(假设一定要移动边界的话,移动较小的那一边损失较小)

class Solution(object):def maxArea(self, height):""":type height: List[int]:rtype: int"""head = 0tail = len(height)-1area = 0big = max(height) * tail  # 标记该示例中可能出现的最大值while head < tail:area = max(area, (tail-head)*min(height[head], height[tail]))if area >= big:  # 出现最大值后终止循环return areaif height[head] < height[tail]:head += 1else:tail -= 1return area

15. 三数之和

class Solution(object):def threeSum(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""nums.sort()li = []n = len(nums)if n < 3:return []for v1 in range(n-2):v2 = v1 + 1v3 = n - 1while v2 < v3:if nums[v2] + nums[v3] < -nums[v1]:v2 += 1elif nums[v2] + nums[v3] > -nums[v1]:v3 -= 1elif nums[v2] + nums[v3] == -nums[v1]:if [nums[v1], nums[v2], nums[v3]] not in li:li.append([nums[v1], nums[v2], nums[v3]])v2 += 1v3 -= 1return li

排序后设置三个指针v1,v2,v3,v2从前向后遍历,v3从后向前遍历,使得nums[v2] + nums[v3] == -nums[v1],用时较长。

16. 最接近的三数之和

nums.sort()n = len(nums)count = nums[0] + nums[1] + nums[2]for v1 in range(n-2):if v1 > 0 and nums[v1] == nums[v1-1]:continue # 如果移动v1后数值不变,继续执行下一次for循环v2 = v1 + 1v3 = n - 1while v2 < v3:tmp = nums[v1] + nums[v2] + nums[v3]if tmp == target:return targetif abs(tmp-target) < abs(count-target):count = tmpif tmp > target:v3 -= 1while v2 < v3 and nums[v3] == nums[v3+1]:v3 -= 1else:v2 += 1while v2 < v3 and nums[v2] == nums[v2 - 1]:v2 += 1return count

18. 四数之和

class Solution(object):def fourSum(self, nums, target):""":type nums: List[int]:type target: int:rtype: List[List[int]]"""nums.sort()n = len(nums)li = []if n < 4:return []for v1 in range(n-3):for v2 in range(v1+1, n-2):v3 = v2+1v4 = n-1while v3 < v4:tmp = nums[v1] + nums[v2] + nums[v3] + nums[v4]if tmp == target:if [nums[v1], nums[v2], nums[v3], nums[v4]] not in li:li.append([nums[v1], nums[v2], nums[v3], nums[v4]])v3 += 1v4 -= 1elif tmp < target:v3 += 1elif tmp > target:v4 -= 1return li

类似前两题吧反正就是,讨论特殊情况太麻烦了直接用个not in语句判定一下,缺点是时间和内存都挺大。

class Solution(object):def fourSum(self, nums, target):""":type nums: List[int]:type target: int:rtype: List[List[int]]"""li = []  # 定义一个返回值if not nums or len(nums) < 4:return linums.sort()n = len(nums)for v1 in range(n - 3):# 当v1的值与前面的值相等时忽略if v1 > 0 and nums[v1] == nums[v1 - 1]:continue  # continue,跳出本次for循环继续下一次for循环# 获取当前最小值,如果最小值比目标值大,忽略if nums[v1] + nums[v1 + 1] + nums[v1 + 2] + nums[v1 + 3] > target:break  # break,直接退出所有for循环# 获取当前最大值,如果最大值比目标值小,忽略if nums[v1] + nums[n - 3] + nums[n - 2] + nums[n - 1] < target:continue  # continue,跳出本次for循环继续下一次for循环for v2 in range(v1 + 1, n - 2):if v2 > v1 + 1 and nums[v2] == nums[v2 - 1]:continueif nums[v1] + nums[v2] + nums[v2 + 1] + nums[v2 + 2] > target:breakif nums[v1] + nums[v2] + nums[n - 2] + nums[n - 1] < target:continueleft, right = v2 + 1, n - 1while left < right:tmp = nums[v1] + nums[v2] + nums[left] + nums[right]if tmp == target:li.append([nums[v1], nums[v2], nums[left], nums[right]])left += 1while left < right and nums[left] == nums[left - 1]:left += 1right -= 1while left < right and nums[right] == nums[right + 1]:right -= 1elif tmp < target:left += 1else:right -= 1return li

一个重点是:break跳出所有循环,continue跳出本次循环,数值更新后继续下一次循环,两个语句都是针对while和for来说的。通过跳出不必要的循环以及判断特殊情况(而不是懒鬼方法直接not in)可以大大缩减时间和占用的内存。

31. 下一个排列

# 判决思路在笔记上,不知道为什么leetcode会报错,在电脑上跑是对的
class Solution(object):# 将比a大的最小元素提取到前面,其他元素从大到小排列def sort1(self, a, li):  # 排序函数,a为非倒序元素,li为全倒序列表tmp = li[0]          # tmp初始化为li中最大值head_v = 0           # head_v为指向tmp的指针for i in range(len(li)-1, -1,-1):  # 在li中寻找比a大的最小元素if li[i] >= a:tmp = li[i]head_v = ili.pop(head_v)  # 从li中删除比a大的最小元素li.append(a)    # 将a添加到lili.sort()       # 从大到小排序sortli = [tmp]  # 存储排序结果for val in li:sortli.append(val)return sortlidef nextPermutation(self, nums):""":type nums: List[int]:rtype: None Do not return anything, modify nums in-place instead."""n = len(nums)if n == 1:      # 列表长度为1时直接返回return numselif n == 2:    # 列表长度为2时交换元素位置返回nums[0], nums[1] = nums[1], nums[0]return numselse:           # 列表长度大于2时if nums[n-1] > nums[n-2]:  # 末尾两个元素为正序,直接交换后返回nums[n-1], nums[n-2] = nums[n-2], nums[n-1]return numselse:                      # 末尾两个元素为倒序li = [nums[n-2], nums[n-1]]      # 存储倒序列表i = 3flag = 1while i<=n and flag:             # 向前遍历直到找到非倒序的元素if nums[n-i] > nums[n-i+1]:li.insert(0, nums[n-i])i += 1else:flag = 0if flag:  # 未找到非倒序元素(整个列表为倒序),直接从小到大排序后返回nums.sort()return numstmp_li = self.sort1(nums[n-i], li)con = nums[0: n-i]for val in tmp_li:  # 连接前面不需要改变的列表切片,以及重新排序后的列表con.append(val)return con

官方解法的思路类似,先找到一个“较右数”也就是“非倒序排列的最靠右的元素”,再在右面找到一个比“较右数”稍大的元素“较小数”(因为只需要比较右数稍大一点点,所以称为“较小数”),交换“较小数”和“较右数”,再重新排列“较小数”右侧的序列。(下面是另一个解法里的代码,感觉更加清晰)

class Solution:def nextPermutation(self, nums):""":type nums: List[int]:rtype: None Do not return anything, modify nums in-place instead."""n = len(nums)if n == 0:return []elif n == 1:return numselse:v1 = n - 2  # 倒数第二个数,获得“较右数”v2 = n - 1  # 倒数第一个数,获得“较小数”while v1 >= 0 and nums[v1] >= nums[v1 + 1]:v1 -= 1if v1 == -1:  # while循环被v1>=0终止,整个序列倒序排列return nums.sort()else:while nums[v1] >= nums[v2]:# 不需要限制v2>v1是因为v1右侧的数一定比它大v2 -= 1nums[v1], nums[v2] = nums[v2], nums[v1]# 交换较小数和较右数后,v1右侧依旧严格倒序排列,前后亮亮交换即可left = v1 + 1right = n - 1while left < right:nums[left], nums[right] = nums[right], nums[left]left += 1right -= 1return nums

33. 搜索旋转排序数组

class Solution(object):def search(self, nums, target):""":type nums: List[int]:type target: int:rtype: int"""if nums.count(target) == 0:return -1else:return nums.index(target)

偷懒解法↑(而且耗时更短哈哈哈)

class Solution:def search(self, nums, target):if not nums:return -1left, right = 0, len(nums) - 1while left <= right:mid = (left + right) // 2if nums[mid] == target:return midif nums[0] <= nums[mid]:  # 假如左半边有序if nums[0] <= target < nums[mid]:  # target在左半边right = mid - 1                # 在左半边二分查找else:                              # target在右半边left = mid + 1                 # 在右半边二分查找else:                     # 假如右半边有序if nums[mid] < target <= nums[len(nums) - 1]:left = mid + 1else:right = mid - 1return -1

34. 在排列数组中查找元素的第一个和最后一个位置

class Solution(object):def searchRange(self, nums, target):""":type nums: List[int]:type target: int:rtype: List[int]"""count = nums.count(target)if not count:       # 不存在targetreturn [-1,-1]if len(nums) == 1:  # 存在target但nums中只有一个数return [0,0]# 二分法left = 0right = len(nums) - 1flag = 1while flag:  # 前面已经判定过一定存在target,left一定小于rightmid = (left + right) // 2if nums[mid] == target:   # 找到target,停止循环flag = 0elif nums[mid] < target:  # target在右半部分left = mid + 1else:                     # target在左半部分right = mid - 1while mid > 0:  # 找到第一个出现的targetif nums[mid-1] == target:mid -= 1else:breakreturn [mid, mid+count-1]

官方思路,leftindex是寻找第一个等于target的下标减一,rightindex是寻找第一个大于target的下标减一。

class Solution(object):# 二分查找# leftindex寻找第一个大于等于target的位置(lower = true)# rightindex寻找第一个大于targrt的位置减一(lower = false)def binarySearch(self, nums, target, lower):left = 0right = len(nums) - 1ans = len(nums)while left <= right:mid = (left + right) // 2# leftindex:target在mid处或在左半边# rightindex:target在左半边if nums[mid] > target or (lower and nums[mid] >= target):right = mid - 1ans = midelse:left = mid + 1return ansdef searchRange(self, nums, target):""":type nums: List[int]:type target: int:rtype: List[int]"""leftindex = self.binarySearch(nums, target, True)rightindex = self.binarySearch(nums, target, False) - 1# 判断是否存在targetif leftindex <= rightindex and rightindex <= len(nums) - 1 and nums[leftindex] == nums[rightindex] == target:return [leftindex, rightindex]else:return [-1,-1]

36. 有效的数独

class Solution(object):def isValidSudoku(self, board):""":type board: List[List[str]]:rtype: bool"""# 每行是否有重复for i in range(9):tmp = board[i][0:9]while tmp.count("."):tmp.remove(".")tmp_set = set(tmp)if len(tmp) != len(tmp_set):return False# 每列是否有重复for i in range(9):# tmp = board[0:8][i]  这种写法取的还是第一排,奇怪tmp = []for j in range(9):tmp.append(board[j][i])while tmp.count("."):tmp.remove(".")tmp_set = set(tmp)if len(tmp) != len(tmp_set):return False# 每个方块是否有重复for i in range(9):tmp = []if i % 3 == 0:  # 第一竖列的三个方块n = i // 3for j in range(3):tmp.append(board[3 * n][j])tmp.append(board[3 * n + 1][j])tmp.append(board[3 * n + 2][j])elif i % 3 == 1:  # 第二竖列的三个方块n = i // 3for j in range(3,6):tmp.append(board[3 * n][j])tmp.append(board[3 * n + 1][j])tmp.append(board[3 * n + 2][j])elif i % 3 == 2:  # 第三竖列的三个方块n = i // 3for j in range(6,9):tmp.append(board[3 * n][j])tmp.append(board[3 * n + 1][j])tmp.append(board[3 * n + 2][j])while tmp.count("."):tmp.remove(".")tmp_set = set(tmp)if len(tmp) != len(tmp_set):return Falsereturn True

利用哈希表的解法,设置三个哈希表记录数字出现情况(内存消耗很大):

class Solution(object):def isValidSudoku(self, board):""":type board: List[List[str]]:rtype: bool"""row = [[0 for _ in range(10)] for _ in range(9)]  # 9*10列表,存储每一行的每个数是否出现过col = [[0 for _ in range(10)] for _ in range(9)]  # 9*10列表,存储每一列的每个数是否出现过box = [[0 for _ in range(10)] for _ in range(9)]  # 9*10列表,存储每方块的每个数是否出现过for i in range(9):for j in range(9):if board[i][j] == ".":continuecurnumber = int(board[i][j])if row[i][curnumber]:return Falseif col[j][curnumber]:return False# n1 = i // 3  # 方块在第几行# n2 = j // 3  # 方块在第几列# n = 3 * n1 + n2if box[3*(i//3) + j//3][curnumber]:return Falserow[i][curnumber] = 1col[j][curnumber] = 1box[3*(i//3) + j//3][curnumber] = 1return True

39. 组合总和

class Solution(object):def combinationSum(self, candidates, target):""":type candidates: List[int]:type target: int:rtype: List[List[int]]"""dict = {i:[] for i in range(target+1)}for val in sorted(candidates, reverse=True):for i in range(val, target+1):if i == val:dict[i] = [[val]]else:for x in dict[i-val]:dict[i].extend([x + [val]])return dict[target]

或者使用标准的回溯结构:

class Solution:def combinationSum(self, candidates, target):def dfs(candidates, begin, size, path, res, target):# begin存储遍历起点,避免重复遍历if target < 0:   # 当target比抽取元素和小的时候返回returnif target == 0:  # 当target等于抽取元素和的时候,在res中保存结果res.append(path)returnfor index in range(begin, size):# 继续搜索下一层dfs(candidates, index, size, path + [candidates[index]], res, target - candidates[index])size = len(candidates)  # 决定树的宽度if size == 0:return []path = []  # 存储单条路经res = []  # 存储所有满足要求的路径dfs(candidates, 0, size, path, res, target)return res

e.g. candiates = [2,3,6,7], target = 7

这组代码还有优化的余地,比如在path = [2,2]时的下一层for循环中[2,2,6]已经超出target,[2,2,7]一定会超过target因此不需要执行。

# 增加判决条件(剪枝操作)
class Solution:def combinationSum(self, candidates, target):def dfs(candidates, begin, size, path, res, target):# begin存储遍历起点,避免重复遍历# if target < 0:   # 当target比抽取元素和小的时候返回#     returnif target == 0:  # 当target等于抽取元素和的时候,在res中保存结果res.append(path)returnfor index in range(begin, size):residue = target - candidates[index]if residue < 0:break# dfs(candidates, index, size, path + [candidates[index]], res, target - candidates[index])dfs(candidates, index, size, path + [candidates[index]], res, residue)size = len(candidates)  # 决定树的宽度if size == 0:return []candidates.sort()path = []  # 存储单条路经res = []  # 存储所有满足要求的路径dfs(candidates, 0, size, path, res, target)return res

回溯方法效率并不高,相当于穷举法,关键是要结合剪枝算法降低复杂度。该方法中使用begin变量标记下一次遍历的开始元素,即剪枝的思想,避免重复遍历。题解中提到了另一种方法使用used数组记录使用过的数字,其区别在于:

40. 组合总和

和上一题的区别时每个数字在每个组合只能使用一次,更改begin位置并增加重复组合的判决语句即可。对于重复组合的判决思路如下:在同一层节点中只保留同样的数一次。

class Solution:def combinationSum2(self, candidates, target):def dfs(candidates, begin, size, path, res, target):# begin存储遍历起点,避免重复遍历if target == 0:  # 当target等于抽取元素和的时候,在res中保存结果res.append(path)returnfor index in range(begin, size):residue = target - candidates[index]if residue < 0:breakif index > begin and candidates[index] == candidates[index-1]:continuedfs(candidates, index+1, size, path + [candidates[index]], res, residue)size = len(candidates)  # 决定树的宽度if size == 0:return []candidates.sort()path = []  # 存储单条路经res = []  # 存储所有满足要求的路径dfs(candidates, 0, size, path, res, target)return res

45. 跳跃游戏

class Solution(object):def jump(self, nums):""":type nums: List[int]:rtype: int"""n = len(nums)-1def dfs(nums, now, path, res):if now > n:returnif now == n:  # 到达终点res.append(path)returnfor step in range(1, nums[now]+1):dfs(nums, now + step, path + [now + step], res)path = [0]  # 存储单条路经res = []  # 存储所有满足要求的路径dfs(nums, 0, path, res)minjump = min(len(s) for s in res) - 1return minjump

利用穷举法,会超出时间限制。

采用贪心算法,假设每次走最远距离nums[i],在最远距离中间节点(i+1, ...... , i+nums[i])检验第二步距离是否比最远距离nums[i]的第二步距离更远:

class Solution(object):def jump(self, nums):""":type nums: List[int]:rtype: int"""n = len(nums)-1i = 0count = 0while i <= n:if i == n :return countif i + nums[i] < n:maxrange = i + nums[i] + nums[i + nums[i]]tmp = nums[i]for step in range(1, nums[i]+1):twostep = i + step + nums[i+step]if twostep > maxrange:maxrange = twosteptmp = stepi += tmpcount += 1else:count += 1return count

46. 全排列

class Solution(object):def permute(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""size = len(nums)n = sizefor i in range(1, size):n *= idef dfs(nums, used, path, res):if len(res) == n:returnif len(path) == size:res.append(path)returnfor index in range(size):if index not in used:used.append(index)dfs(nums, used, path + [nums[index]], res)used.pop()path = []res = []used = []dfs(nums, used, path, res)return res

采用之前提到的用used存储已经在path里的元素的方法。

官方题解:

class Solution(object):def permute(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""# first表示从左往右填完第first个位置def dfs(first = 0):if first == n:  # 一组path填完res.append(nums[:])  # 如果不加[:],res里的结果会随着nums变化# 将nums划分为左右两个部分,左边已填右边未填,回溯时动态维护numsfor i in range(first, n):nums[first], nums[i] = nums[i], nums[first]dfs(first + 1)nums[first], nums[i] = nums[i], nums[first]# 回溯结束后交换回来n = len(nums)res = []dfs()return res

47. 全排列(含重复元素)

懒蛋判定法:

if nums[:] not in res:res.append(nums[:])

或者:

for i in range(first, n):if i != first and nums[i] == nums[first]:continuenums[first], nums[i] = nums[i], nums[first]dfs(first + 1)nums[first], nums[i] = nums[i], nums[first]

题解里看到的一种解法:

class Solution:def permuteUnique(self, nums):nums.sort()self.res = []used = [0 for _ in range(len(nums))]  self.backtrack([], nums, used)return self.resdef backtrack(self, path, nums, used):if len(path) == len(nums):self.res.append(path)returnfor i in range(len(nums)):if used[i] == 1:continueif i > 0 and nums[i] == nums[i - 1] and used[i - 1] == 0:continueused[i] = 1self.backtrack(path + [nums[i]], nums, used)used[i] = 0

引入一个长度和nums一致的全0列表标记nums中的元素是否出现过,采用这种思路改进一下第一种方案,效果类似,看来使用列表标记是否出现可以降低耗时:

class Solution:def permuteUnique(self, nums):def dfs(nums, used, path, res):if len(path) == size:res.append(path)returnfor index in range(size):if used[index] == 1:continueif index > 0 and nums[index] == nums[index-1] and used[index-1] == 0:continueused[index] = 1dfs(nums, used, path + [nums[index]], res)used[index] = 0size = len(nums)path = []res = []used = [0 for _ in range(size)]nums.sort()dfs(nums, used, path, res)return res

48. 旋转图像

class Solution(object):def rotate(self, matrix):""":type matrix: List[List[int]]:rtype: 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, n):matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]return matrix

54. 螺旋矩阵

class Solution(object):def spiralOrder(self, matrix):""":type matrix: List[List[int]]:rtype: List[int]"""n_row = len(matrix)n_col = len(matrix[0])tmp = [[0 for _ in range(n_col)] for _ in range(n_row)]li = [matrix[0][0]]now_row = 0now_col = 0tmp[now_row][now_col] = 1while len(li) < n_row * n_col:if now_col < n_col-1:if tmp[now_row][now_col+1] == 0 and (now_row == 0 or tmp[now_row-1][now_col] != 0):  # 向右遍历now_col += 1tmp[now_row][now_col] = 1li.append(matrix[now_row][now_col])continueif now_row < n_row-1:if tmp[now_row+1][now_col] == 0 and (now_col == n_col-1 or tmp[now_row][now_col+1] != 0):  # 向下遍历now_row += 1tmp[now_row][now_col] = 1li.append(matrix[now_row][now_col])continueif now_col > 0:if tmp[now_row][now_col-1] == 0 and (now_row == n_row-1 or tmp[now_row+1][now_col] != 0):  # 向左遍历now_col -= 1tmp[now_row][now_col] = 1li.append(matrix[now_row][now_col])continueif now_row > 0:if tmp[now_row-1][now_col] == 0 and (now_col == 0 or tmp[now_row][now_col-1] != 0):  # 向上遍历now_row -= 1tmp[now_row][now_col] = 1li.append(matrix[now_row][now_col])continuereturn li

官方题解的写法更加简单,把上下左右存储在directions中不需要分开写:

class Solution(object):def spiralOrder(self, matrix):""":type matrix: List[List[int]]:rtype: List[int]"""if not matrix or not matrix[0]:return list()n_row, n_col = len(matrix), len(matrix[0])tmp = [[False] * n_col for _ in range(n_row)]total = n_row * n_colli = [0] * totaldirections = [[0, 1], [1, 0], [0, -1], [-1, 0]]  # 右,下,左,上now_row, now_col = 0, 0directionIndex = 0for i in range(total):li[i] = matrix[now_row][now_col]tmp[now_row][now_col] = TruenextRow, nextColumn = now_row + directions[directionIndex][0], now_col + directions[directionIndex][1]if not (0 <= nextRow < n_row and 0 <= nextColumn < n_col and not tmp[nextRow][nextColumn]):# 如果[nextRow][nextColumn]不在界内,或者[nextRow][nextColumn]已经遍历过,转向directionIndex = (directionIndex + 1) % 4now_row += directions[directionIndex][0]now_col += directions[directionIndex][1]return li

55. 跳跃游戏

维护一个“最远可以到达的位置”,对于一个可到达的位置x,在x+1,......,x+nums[i]范围内都是可到达的位置,当“最远可以到达的位置”大于等于数组最后一个位置,则返回True。

class Solution(object):def canJump(self, nums):""":type nums: List[int]:rtype: bool"""n = len(nums)maxrange = 0for i in range(n):if i <= maxrange:maxrange = max(maxrange, i+nums[i])if maxrange >= n-1:return Truereturn False

56. 合并区间

class Solution(object):def merge(self, intervals):""":type intervals: List[List[int]]:rtype: List[List[int]]"""def compare(a):return a[0]intervals.sort(key=compare)n = len(intervals)index = 1while index < n:if intervals[index-1][1] >= intervals[index][0]:tmp = [intervals[index-1][0], max(intervals[index-1][1], intervals[index][1])]intervals.pop(index)intervals.pop(index-1)intervals.insert(index-1, tmp)n -= 1else:index += 1return intervals

使列表按照每个元素的第一位排列的方法:

或者:

intervals.sort(key=lambda x: x[0])

57. 插入区间

class Solution(object):def insert(self, intervals, newInterval):""":type intervals: List[List[int]]:type newInterval: List[int]:rtype: List[List[int]]"""left, right = newIntervalplaced = Falseans = []for li, ri in intervals:if li > right:# 在插入区间右侧且无交集if not placed:ans.append([left, right])placed = Trueans.append([li, ri])elif ri < left:# 在插入区间左侧且无交集ans.append([li, ri])else:# 与插入区间有交集left = min(left, li)right = max(right, ri)if not placed:ans.append([left, right])return ans

56. 57.的思路:

59. 螺旋矩阵

相当于螺旋排列的转化为顺序输出

class Solution(object):def generateMatrix(self, n):""":type n: int:rtype: List[List[int]]"""li = [[0 for _ in range(n)] for _ in range(n)]now_row = 0now_col = 0for i in range(1, n**2 + 1):li[now_row][now_col] = iif now_col < n-1 and (now_row == 0 or li[now_row-1][now_col] != 0):  # 右if li[now_row][now_col+1] == 0:now_col += 1continueif now_row < n-1 and (now_col == n-1 or li[now_row][now_col+1] != 0):  # 下if li[now_row+1][now_col] == 0:now_row += 1continueif now_col > 0 and (now_row == n-1 or li[now_row+1][now_col] != 0):  # 左if li[now_row][now_col-1] == 0:now_col -= 1continueif now_row > 0 and (now_col == 0 or li[now_row][now_col-1] != 0):  # 上if li[now_row-1][now_col] == 0:now_row -= 1continuereturn li

或者使用dirs存储方向:

class Solution(object):def generateMatrix(self, n):""":type n: int:rtype: List[List[int]]"""dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)]  # 右,下,左,上li = [[0] * n for _ in range(n)]now_row, now_col, dirIdx = 0, 0, 0for i in range(n * n):li[now_row][now_col] = i + 1dx, dy = dirs[dirIdx]next_row, next_col = now_row + dx, now_col + dyif next_row < 0 or next_row >= n or next_col < 0 or next_col >= n or li[next_row][next_col] > 0:dirIdx = (dirIdx + 1) % 4  # 顺时针旋转至下一个方向dx, dy = dirs[dirIdx]now_row, now_col = now_row + dx, now_col + dyreturn li

63. 不同路径

动态规划的题目分为两大类:

1. 求最优解类,典型问题是背包问题;

2. 计数类,比如这里的统计方案数的问题。

它们都存在一定的递推性质,前者的递推性质还有一个名字,叫做 「最优子结构」 ——即当前问题的最优解取决于子问题的最优解,后者类似,当前问题的方案数取决于子问题的方案数。所以在遇到求方案数的问题时,我们可以往动态规划的方向考虑。

由于机器人只能向下或向右移动,所以(0,0)到(i,j)的路径总数取决于(0,0)到(i-1,j)的路径总数与(0,0)到(i,j-1)的路径总数,综上所述得到动态规划转移方程:

class Solution(object):def uniquePathsWithObstacles(self, obstacleGrid):""":type obstacleGrid: List[List[int]]:rtype: int"""n_row, n_col = len(obstacleGrid), len(obstacleGrid[0])def dfs(i, j, count):if obstacleGrid[i][j] == 1:return 0if i == j == 0:count = 1return countif i == 0 and j != 0:return dfs(i, j-1, count)elif j == 0 and i != 0:return dfs(i-1, j, count)elif i != 0 and j != 0:return dfs(i-1, j, count) + dfs(i, j-1, count)count = 0num = dfs(n_row-1, n_col-1, count)return num

这种属于自底向上的递归方法(从终点推回起点),从结果倒推会产生大量的重复计算,因此会超出时间限制。

对自底向上递归方法的改进:增加字典d作为缓存避免重复计算:

class Solution(object):def uniquePathsWithObstacles(self, obstacleGrid):""":type obstacleGrid: List[List[int]]:rtype: int"""n_row, n_col = len(obstacleGrid), len(obstacleGrid[0])d = {}  # 设置一个字典存储路径数# dfs返回(i,j)到终点的路径数def dfs(i, j):if (i, j) in d:  # (i,j)为键,这种方法只能判断键是否在字典中return d[(i, j)]  # 返回键(i,j)对应的值if i >= n_row or j >= n_col or obstacleGrid[i][j] == 1:return 0if i == n_row-1 and j == n_col-1:return 1d[(i,j)] = dfs(i+1, j) + dfs(i, j+1)return d[i,j]return dfs(0,0)

还可以与空间优化相结合,利用滚动数组可以把二维数组改为一维数组,一维数组的大小为列的长度(列数)。由于每次更新时只需要上面一排的数据不需要上上排的数据,因此可以进行空间优化。

class Solution(object):def uniquePathsWithObstacles(self, obstacleGrid):""":type obstacleGrid: List[List[int]]:rtype: int"""n_row, n_col = len(obstacleGrid), len(obstacleGrid[0])dp = [0 for _ in range(n_col)]dp[0] = 1 if obstacleGrid[0][0] == 0 else 0for i in range(n_row):for j in range(n_col):if obstacleGrid[i][j] == 1:dp[j] = 0elif obstacleGrid[i][j] == 0 and j-1 >= 0:dp[j] += dp[j-1]return dp[-1]

在二维数组中改写为自顶向下的递归(即将dfs()的结果值存放到一个二维数组dp中,先填充左边一列和上边一排,根据左边和上边的路径数推导中间的路径数,也就是从起点向终点推):

class Solution(object):def uniquePathsWithObstacles(self, obstacleGrid):""":type obstacleGrid: List[List[int]]:rtype: int"""n_row, n_col = len(obstacleGrid), len(obstacleGrid[0])dp = [[0 for _ in range(n_col)] for _ in range(n_row)]if obstacleGrid[0][0] == 0:dp[0][0] = 1else:return 0for i in range(1, n_row):  # 如果没有障碍物,到最左一列路径均为1if obstacleGrid[i][0] == 0 and dp[i-1][0] != 0:dp[i][0] = 1else:dp[i][0] = 0for j in range(1, n_col):  # 如果没有障碍物,到最上一排路径均为1if obstacleGrid[0][j] == 0 and dp[0][j-1] != 0:dp[0][j] = 1else:dp[0][j] = 0if dp[n_row-1][n_col-1]:return dp[n_row-1][n_col-1]for i in range(1, n_row):for j in range(1, n_col):if obstacleGrid[i][j] == 1:dp[i][j] = 0else:dp[i][j] = dp[i-1][j] + dp[i][j-1]return dp[n_row-1][n_col-1]

64. 最小路径和

自顶向下

class Solution(object):def minPathSum(self, grid):""":type grid: List[List[int]]:rtype: int"""n_row, n_col = len(grid), len(grid[0])dp = [[0 for _ in range(n_col)] for _ in range(n_row)]dp[0][0] = grid[0][0]for i in range(1, n_row):dp[i][0] = dp[i-1][0] + grid[i][0]for j in range(1, n_col):dp[0][j] = dp[0][j-1] + grid[0][j]for i in range(1, n_row):for j in range(1, n_col):dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]return dp[n_row-1][n_col-1]

73. 矩阵置零

class Solution(object):def setZeroes(self, matrix):""":type matrix: List[List[int]]:rtype: None Do not return anything, modify matrix in-place instead."""n_row , n_col = len(matrix), len(matrix[0])row = []col = []for i in range(n_row):for j in range(n_col):if matrix[i][j] == 0:row.append(i)col.append(j)for val in row:for j in range(n_col):matrix[val][j] = 0for val in col:for i in range(n_row):matrix[i][val] = 0return matrix

官方题解:可以用一个列表标记原来的第一列是否有0,遍历剩下的数,如果遇到0把该行第一个数置零、该列第一个数置零,需要更少的额外空间。

74. 搜索二维矩阵

class Solution(object):def searchMatrix(self, matrix, target):""":type matrix: List[List[int]]:type target: int:rtype: bool"""n_row, n_col = len(matrix), len(matrix[0])if matrix[0][0] > target or matrix[n_row-1][n_col-1] < target:return Falseif n_row == 1:i = 0else:for i in range(n_row):  # 在第i行if i == n_row-1 and matrix[i][0] <= target:breakif matrix[i][0] <= target and matrix[i+1][0] > target:breakfor j in range(n_col):if matrix[i][j] == target:return Truereturn False

或者采用二分查找降低时间复杂度,二维列表转换为一维列表:

class Solution(object):def searchMatrix(self, matrix, target):""":type matrix: List[List[int]]:type target: int:rtype: bool"""n_row, n_col = len(matrix), len(matrix[0])li = []for val in matrix:li.extend(val)left = 0right = n_row * n_col - 1while left < right:mid = (left + right) // 2if left == mid or right == mid:breakif li[mid] == target:return Trueelif li[mid] > target:right = mid - 1else:left = mid + 1if li[left] != target and li[right] != target:return Falseelse:return True

75. 颜色分类

class Solution(object):def sortColors(self, nums):""":type nums: List[int]:rtype: None Do not return anything, modify nums in-place instead."""n = len(nums)mark0, mark1 = 0, 0for i in range(n):if nums[i] == 0:nums[i], nums[mark0] = nums[mark0], nums[i]if mark0 < mark1:# 如果此时有排列好的1,即mark0 < mark1,把0交换到前面的过程中会把1放到后面,所以要放回来nums[i], nums[mark1] = nums[mark1], nums[i]mark0 += 1mark1 += 1elif nums[i] == 1:nums[i], nums[mark1] = nums[mark1], nums[i]mark1 += 1return nums

77. 组合

class Solution(object):def combine(self, n, k):""":type n: int:type k: int:rtype: List[List[int]]"""path = []res = []total = 1for i in range(n-k+1, n+1):total *= ifor i in range(1, k+1):total /= idef choose(n, k, begin, path, res):if len(path) == k:res.append(path)returnif len(res) == total:returnfor index in range(begin, n+1):choose(n, k, index+1, path+[index], res)choose(n, k, 1, path, res)return res

78. 子集

区别于之前用回溯算法解的题,这题的终止条件是对于每个叶节点来说的。

class Solution(object):def subsets(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""n = len(nums)# def countTotal(n, k):#     total = 1#     for i in range(n - k + 1, n + 1):#         total *= i#     for j in range(1, k + 1):#         total /= j#     return int(total)## totalpath = 1  # 子集为空集时有一个# for index in range(1, n + 1):#     totalpath += countTotal(n, index)def choose(nums, begin, path, res):res.append(path)# if len(res) == totalpath:#     returnfor element_i in range(begin, n):choose(nums, element_i+1, path+[nums[element_i]], res)path = []res = []choose(nums, 0, path, res)return res

之前为了写出一个终止条件计算子集数量,后来发现没有终止条件也可以正常运行,choose函数里的for循环运行完就自动跳出了。

题解中一种极简的迭代写法:

class Solution(object):def subsets(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""res = [[]]for i in nums:res = res + [[i] + num for num in res]return res

或者使用库函数:

import itertools
class Solution(object):def subsets(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""res = []for i in range(len(nums) + 1):for tmp in itertools.combinations(nums, i):res.append(tmp)return res

79. 单词搜索

class Solution(object):def exist(self, board, word):""":type board: List[List[str]]:type word: str:rtype: bool"""directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]# 从board的(i,j)位置出发是否能搜索到单词,k代表word从第k个字符开始的后缀子串def check(nowIndex_row, nowIndex_col, begin):if board[nowIndex_row][nowIndex_col] != word[begin]:  # 判断首字母是否匹配return Falseif begin == len(word) - 1:  # 判断到最后一个返回Truereturn Truevisited.add((nowIndex_row, nowIndex_col))  # 存储走过的路径result = Falsefor dx, dy in directions:nextIndex_row, nextIndex_col = nowIndex_row + dx, nowIndex_col + dyif 0 <= nextIndex_row < len(board) and 0 <= nextIndex_col < len(board[0]) \and (nextIndex_row, nextIndex_col) not in visited:if check(nextIndex_row, nextIndex_col, begin + 1):result = Truebreak# 如果没有return True说明这条路径是不对的,返回上一层visited.remove((nowIndex_row, nowIndex_col))return resultn_row, n_col = len(board), len(board[0])visited = set()for i in range(n_row):for j in range(n_col):if check(i, j, 0):return Truereturn False

用check函数检验board每一个位置开始是否能够检索到word,检索到直接return true,如果遍历完所有位置没有return true说明检索不到,return false。

80. 删除有序数组中的重复项

class Solution(object):def removeDuplicates(self, nums):""":type nums: List[int]:rtype: int"""n = len(nums)i = 2while i < n:if nums[i] == nums[i-1] == nums[i-2]:nums.pop(i)i -= 1n -= 1i += 1return len(nums)

官方题解中定义i为慢指针,标记处理过的数组长度,n为快指针,标记数组总长度。

81. 搜索旋转排序数组

90. 子集

class Solution(object):def subsetsWithDup(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""res = [[]]nums.sort()for i in range(len(nums)):if i == 0 or (i > 0 and nums[i] != nums[i-1]):li = [[nums[i]] + num for num in res]res = res + [[nums[i]] + num for num in res]else:res = res + [[nums[i]] + num for num in li]li = [[nums[i]] + num for num in li]return res

或者使用回溯的方法:

class Solution(object):def subsetsWithDup(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""res = []  # 存放符合条件结果的集合path = []  # 用来存放符合条件结果def backtrack(nums,startIndex):res.append(path[:])for i in range(startIndex,len(nums)):if i > startIndex and nums[i] == nums[i - 1]: # 我们要对同一树层使用过的元素进行跳过continuepath.append(nums[i])backtrack(nums,i+1)  # 递归path.pop()  # 回溯nums.sort()  # 去重需要排序backtrack(nums,0)return res

leetcode-数组知识点(中等)相关推荐

  1. LeetCode(SQL)难度-中等

    LeetCode(SQL)难度-中等 注:排名知识点(题目1->思路来源于牛客-小数志(公众号)) 连续排名,例如3000,2000,2000,1000排名结果为1-2-3-4,体现同薪不同名, ...

  2. 面试官系列 - LeetCode链表知识点题型总结

    文章目录 前言 知识点 什么是链表 类别 单向链表 循环链表 双向链表 双向循环链表 与数组的性能对比 优缺点 常用技巧 题型总结 基本操作 删除类 翻转类题型 合并链表 环形链表 拆分链表 排序链表 ...

  3. JavaScript学习(八十八)—数组知识点总结,超详细!!!

    JavaScript学习(八十八)-爆肝 数组知识点总结,超详细!!! 每天都要进步一点点 小王加油!!! 一.数组的概念 所谓数组就是指内存中开辟出来的用来存储大量数据的连续的存储空间 数组可以把一 ...

  4. python三维数组知识点

    python三维数组知识点 三维数组中每一个元素表示 代码块 结果图: 示意图: 第一个平面中的数字表示: 第二个平面中的数字表示: 第三个(维度 )平面中的数字表示: 三维切片: 结果图 三维数组中 ...

  5. c语言二维数组作用,C语言二维数组知识点介绍

    C语言二维数组知识点介绍 数组可以看作是一行连续的数据,只有一个下标,称为一维数组.在实际问题中有很多量是二维的或多维的,因此C语言允许构造多维数组.多维数组元素有多个下标,以确定它在数组中的位置.本 ...

  6. LeetCode 2017. Grid Game【前缀和/数组】中等

    本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12.由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止:由于LeetCode还在不断地创建新 ...

  7. leetcode数组汇总_[LeetCode] 300. 最长上升子序列

    题目链接: https://leetcode-cn.com/problems/longest-increasing-subsequence 难度:中等 通过率:43.0% 题目描述: 给定一个无序的整 ...

  8. leetcode数组汇总_LeetCode刷题:前言

    LeetCode刷题:前言 前言 作为一个对编程超级不通的小白,在2020年11月开始打算正式的刷LeetCode. (PS:前面有刷过,但是都是随机,看心情乱刷的,刷完后也没有什么感觉,该不会的还是 ...

  9. 六十七、Leetcode数组系列(下篇)

    @Author:Runsen @Date:2020/6/19 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏 ...

  10. 六十六、Leetcode数组系列(中篇)

    @Author:Runsen @Date:2020/6/8 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

最新文章

  1. 困扰程序员的30种软件开发问题,你是否时曾相识?
  2. Vue.js学习系列(二十七)-- 计算属性(一)
  3. CNN目标检测(二):YOLO
  4. Mac OS X下安装和配置Maven
  5. Codeforces Round 504
  6. TensorFlow 2.0 快速上手教程与手写数字识别例子讲解
  7. 析构函数和动态内存管理
  8. javascript计算小数保留两位小数,多位小数的方法
  9. Oracle中给表添加主键 外键,给表中添加主键、外键
  10. 更靠谱的横竖屏检测方法
  11. 百度云云盘搜索助手可查询提取码 内置5个搜索引擎
  12. 详细SpringBoot教程之入门(一)
  13. 深度图像修复的回顾和改进:使用生成对抗网络基于Patch的图像修复
  14. UNIX下修改时间简单一例
  15. PPT进阶篇---如何让你的声音好听
  16. cat5和cat5e的区别_具有功能以及CAT5与CAT6的CAT5电缆标准是什么?
  17. 机器视觉实验四: 为人脸添加装饰物特效实验(OpenCV-python代码)
  18. C语言求最大公约数及最小公倍数
  19. IDC:云效产品能力No.1,领跑中国DevOps市场
  20. 点击按钮打开windows计算器,python代码示例,有GUI界面,直接写代码

热门文章

  1. 直播教育平台开发—老师学生上课学习的好帮手
  2. ldap服务器配置信息错误,客户域控启用了LDAP服务器签名要求导致配置域信息失败...
  3. 可爱又迷人的反叛角色
  4. 我的世界古代战争模组介绍java版_我的世界古代战争2mod教程零基础到专属军队...
  5. 手写一个栈 java,数据结构|用java自己手写实现一个栈
  6. 很期待的一个车模打印作品
  7. 基于压缩感知的单像素相机
  8. 华为机试python3题解(17题 持续更新ing)
  9. elementui中多选下拉框 multiple属性时 编辑表单如何显示默认值问题解决 以及校验验证问题
  10. www.14zz.com www.14zz.com 免费 资料库