- 类型:缩减搜索空间,缩短时间复杂度
- 方法:哈希表、首尾双指针
题目1:1. 两数之和
-
题目:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
-
类型:无序查找用哈希表
-
思路:01-记录所有数字的索引和值;02-找到num在nums中,target-num也在的情况,同时两者的索引不能相同。
-
经验:(1)for遍历只能针对range或者enumerate对象,不能针对字典;(2)哈希表查找时间复杂度为O(n),如果是两层for循环时间复杂度为O(n^2)。 查询的方式是if value in hashmap
def twoSum(self, nums: List[int], target: int) -> List[int]:
hashmap={}
for index,value in enumerate(nums):
hashmap[value]=index
for index,value in enumerate(nums): #字典不能用于遍历,要用enumerate函数
if (target-value in hashmap) and (hashmap[target-value]!=index):
return [index,hashmap[target-value]]
题目2:167. 两数之和 II - 输入有序数组
-
题目:给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
-
类型:有序查找用首尾双指针
-
思路:01-初始化首尾;02-由于left和right分别向中间走,所以用while最方便;03-判断三种情况,只有找到目标才输出结果。
-
经验:(1)首尾双指针法查找每次更新的步数是1步;(2)首尾双指针用while left<right作为循环结束条件。
def twoSum(self, numbers: List[int], target: int) -> List[int]:
left,right=0,len(numbers)-1
while left<right:
sum_=numbers[left]+numbers[right]
if sum_==target:return [left+1,right+1]
elif sum_<target:left+=1
else:right-=1
题目3:三数之和
- 题目:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
- 类型:查找符合条件的三个数
- 思路:01-排序+for+双指针的结构;02-for和双指针都要去重;03-循环前和循环后都要剪枝。
- 经验:(1)单层for循环,break和return的作用一致;(2)当外层限制了left和right的关系,内层只更新left,一定要在内层的条件中加上left和right的关系,防止溢出。(3)每个指针都要求在这次循环能找到的组合中,针对这个指针来说,这次try的数和上次try的数不一样。
def threeSum(self, nums: List[int]) -> List[List[int]]:
if not nums or len(nums)<3:return []
res=[]
nums=sorted(nums)
#第一个数用for遍历
for i in range(len(nums)):
if i>0 and nums[i]==nums[i-1]:continue
if nums[i]+nums[i+1]+nums[i+2]>0:break #可以用break或者return两种方式来结束循环
if nums[i]+nums[len(nums)-1]+nums[len(nums)-2]<0:continue
#其他两个数用双指针
left=i+1
right=len(nums)-1
while left<right:
if (nums[i]+nums[left]+nums[right]==0): #left和right之后都要更新,不能在上面就定义sum_
res.append([nums[i],nums[left],nums[right]])
#滑动窗口,只要不合法(相等),就一直滑动
while left<right and nums[left]==nums[left+1]:left+=1 #一定要加上left<right的限制!!!
while left<right and nums[right]==nums[right-1]:right-=1
left+=1
right-=1
elif nums[i]+nums[left]+nums[right]<0:left+=1
else:right-=1
return res
题目4:四数之和
-
题目:给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (不重复指的是每个组合的唯一性)
-
类型:查找符合条件的四个数
-
思路:01-排序+两层for循环嵌套双指针的结构;02-循环和双指针中的去重;03-循环开始前以及每一层循环开始后有的剪枝;
-
经验:(1)查找最一般用for循环,找多个数就嵌套多个for循环。但是可以用排序、指针代替双重循环、剪枝三种技巧来简化多数查找。也就是说,最后的呈现结构是for里面嵌套双指针;(2)continue指的是跳出本次循环,break指的是跳出本层循环,return指的是跳出所有循环;(3)剪枝指的是遇到某种情况就跳出本次/本层/所有循环。剪枝的思路:整个循环开始前以及每一层循环之后都可以剪枝
a.空集或者长度不符合就直接输出空集
b.遍历过程中最左边四个>target,跳出本层循环(i层/j层)
c.遍历过程中最右边四个<target,跳出本次循环
(4)去重复:
a.for循环中:continue直到没有重复
b.双指针中:while直到没有重复
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
res=[]
if not nums or len(nums)<4:return res #剪枝
nums.sort()
if nums[0]+nums[1]+nums[2]+nums[3]>target:return res
if nums[len(nums)-1]+nums[len(nums)-2]+nums[len(nums)-3]+nums[len(nums)-4]<target:return res#循环前剪枝
for i in range(len(nums)-3):
if i>0 and nums[i]==nums[i-1]:continue #不重复
if nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target:break
if nums[i]+nums[len(nums)-1]+nums[len(nums)-2]+nums[len(nums)-3]<target:continue#第一个数后剪枝
for j in range(i+1,len(nums)-2):
if j>i+1 and nums[j]==nums[j-1]:continue #不重复
if nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target:break
if nums[i] + nums[j] + nums[len(nums)-2] + nums[len(nums)-1] < target:continue
left=j+1
right=len(nums)-1
while left<right:
if nums[i]+nums[j]+nums[left]+nums[right]==target:
res.append([nums[i],nums[j],nums[left],nums[right]])#找到了一个结果之后要继续找其他结果
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 nums[i]+nums[j]+nums[left]+nums[right]<target:left+=1
else:right-=1
return res
明日计划:
11. 盛最多水的容器:双指针
633. 平方数之和