查找3—hash表的使用
1、两数之和(1)
题目描述:
【简单题】
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
思路分析:
题解一:暴力法
\quad \quad 遍历数组A[i],如果(目标值-A[i])在数组中且两者的所在位置即索引不相等,则返回它们的位置。
【python 代码实现】
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
for i in range(len(nums)):
for j in range(i+1,len(nums)):
if nums[i]+nums[j]==target:
return [i,j]
return []
或
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
for i in range(len(nums)):
n=target-nums[i]
if n in nums and nums.index(n)!=i:
return [i,nums.index(n)]
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 空间复杂度:O(1)
题解二:排序+对撞指针
- 先copy原数组nums到一个新数组temp(因为最后返回的是原数组的索引,故不能对原数组排序)
- 对新数组temp进行排序
- 对排序后的新数组temp运用对撞指针方法查找i,j,使得
temp[i]+temp[j]=target
- 在原数组nums中查找temp[i]所在索引p
- 将原数组中p位置删除(为了避免解为相同数字时【如target为6,nums中有两个3】,不弹出的话,查找索引只能找到同一个数字的索引)
- 在原数组nums中查找temp[j]所在索引k
- 如果
k>=p
,由于原数组之前弹出一个元素,则索引k应加一 - 如果k<p,索引不受影响
- 返回
[p,k]
【python 代码实现】
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
temp=nums.copy()
temp.sort()
i=0
j=len(temp)-1
while i<j:
if temp[i]+temp[j]<target:
i+=1
elif temp[i]+temp[j]>target:
j-=1
else:
break
p=nums.index(temp[i])
nums.pop(p)
k=nums.index(temp[j])
if k>=p:
k+=1
return [p,k]
-
时间复杂度: O ( n ) + O ( n l o g n ) = O ( n l o g n ) O(n)+O(nlogn)=O(nlogn) O(n)+O(nlogn)=O(nlogn),其中内置函数copy、对撞指针复杂度分别为 O ( n ) O(n) O(n),内置函数sort时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
-
空间复杂度: O ( n ) O(n) O(n)
题解三:hash
-
利用哈希表的查找元素时间复杂度为O(1)的优点,可对此题建立哈希映射
-
遍历数组,建立哈希表(将数组的下标和值反向一一对应)。然后查找target-nums[i]是否在哈希表中,如果在,则返回两索引。
注意: 若数组中同一元素值有重复的话,在建立哈希表的过程中,会覆盖掉之前的记录;所以我们先查找,若没有该值,则再添加。
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hash_map={}
for i in range(len(nums)):
if hash_map.get(target-nums[i]) is not None:
return [hash_map.get(target-nums[i]),i]
hash_map[nums[i]]=i#这句不能放在if语句之前,解决数组中有重复值或target-x=x的情况
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)
2、三数之和(15)
题目描述:
【中等题】
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
思路分析:
- 本题与两数之和类似,但是解题过程有出入。
- 本题的难点在于如何去除重复值。
题解一:暴力法
\quad \quad 虽然可以使用暴力法即三层遍历,但是其复杂度为 O ( n 3 ) O(n^3) O(n3),超出时间限制,况且题目中要求找到所有不重复且和为0的三元组,这个不重复的要求使得我们无法简单地使用三重循环枚举所有的三元组。故不建议使用这种方法。
题解二:排序+双指针
\quad \quad 如何在遍历数组的时候才能保证可行解不重复?
- 遍历数组的时候,我们只需要保证第二次枚举到的元素不小于当前遍历元素,第三次枚举到的元素不小于当前第二次枚举到的元素。
- 也就是说,我们枚举的三元组(a,b,c)满足 a ≤ b ≤ c a\leq b \leq c a≤b≤c,保证了只有(a,b,c)这个顺序会被枚举到,而(b,a,c),(c,b,a)等等这些不会,这样就解决了有重复可行解的问题。
- 因此,要实现上述,可以对数组进行排序,然后对排序后的数组运用双指针方法查找可行解。
- 注意一点,对于每一重循环而言,相邻两次枚举的元素不能相同,否则也会造成重复。
算法流程:
-
特殊情况考虑,对于数组长度为n,如果数组为null或者数组长度小于3,返回 [ ]
-
对数组进行排序
-
新建并初始化数组res=[ ],存放可行解
-
遍历排序后数组:
-
如果num[i]>0,则无解,返回res。(因为已排序,故后面的元素必都大于0,则后面不可能有三个数之和为0,直接返回结果)
-
对于重复元素,直接跳过,结束循环,避免出现重复元素。
-
使用双指针,加上遍历索引为三指针,寻找可行解:令左指针
L=i+1
,右指针R=n-1
,当L<R
时,执行以下循环:- 当
nums[i]+nums[L]+nums[R]==0
,将[nums[i],nums[L],nums[R]]
添加到结果数组中并判断左指针和右指针位置是否分别和下一位置重复,若重复则分别+1即同时将L,R移到同一位置,目的是为了去除重复解,从而再去寻找新的解。 - 若和大于0,则右指针左移
- 若和小于0,则左指针右移
- 当
-
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
n=len(nums)
if n is None or n<3:
return []
nums.sort()
res=[]
for i in range(n):
if nums[i]>0:
return res
# 遇到重复值,则直接结束循环,避免出现重复解
if i>0 and nums[i]==nums[i-1]:
continue
l=i+1
r=n-1
while l<r:
if nums[i]+nums[l]+nums[r]==0:
res.append([nums[i],nums[l],nums[r]])
# 若左指针、右指针当前位置与下一位置元素相同,则分别将两指针下移一个位置
while l<r and nums[l]==nums[l+1]:
l+=1
while l<r and nums[r]==nums[r-1]:
r-=1
l+=1
r-=1
elif nums[i]+nums[l]+nums[r]>0:
r-=1
else:
l+=1
return res
-
时间复杂度: O ( n 2 ) O(n^2) O(n2)
-
数组排序: O ( n l o g n ) O(nlogn) O(nlogn)
-
遍历数组: O ( n ) O(n) O(n)
- 双指针: O ( n ) O(n) O(n)
-
总复杂度: O ( n l o g n ) + O ( n ) ∗ O ( n ) = O ( n 2 ) O(nlogn)+O(n)*O(n)=O(n^2) O(nlogn)+O(n)∗O(n)=O(n2)
-
-
空间复杂度: O ( 1 ) O(1) O(1)
题解三:hash法
未完待续
3、最接近的三数之和(16)
题目描述:
【中等题】
给定一个包括 n 个整数的数组 nums
和 一个目标值 target
。找出 nums
中的三个整数,使得它们的和与 target
最接近。返回这三个数的和。假定每组输入只存在唯一答案。
题目链接
思路分析:
排序和双指针
- 首先进行数组排序,时间复杂度
- 在数组 nums 中,进行遍历,每遍历一个值利用其下标i,形成一个固定值 nums[i]
- 再使用前指针指向 start = i + 1 处,后指针指向 end = nums.length - 1 处,也就是结尾处
- 根据 sum = nums[i] + nums[start] + nums[end] 的结果,判断 sum 与目标 target 的距离,如果更近则更新结果 ans
- 同时判断 sum 与 target 的大小关系,因为数组有序,如果 sum > target 则 end–,如果 sum < target 则 start++,如果 sum == target 则说明距离为 0 直接返回结果
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
n=len(nums)
if not nums or n<3:
return None
nums.sort()
res=float("inf")
for i in range(n):
if i>0 and nums[i]==nums[i-1]:
continue
L=i+1
R=n-1
while L<R:
cur_sum=nums[i]+nums[L]+nums[R]
if(cur_sum==target):
return target
if abs(cur_sum-target)<abs(res-target):
res=cur_sum
if(cur_sum-target<0):
L+=1
else:
R-=1
return res
-
时间复杂度: O ( n 2 ) O(n^2) O(n2)
-
数组排序: O ( n l o g n ) O(nlogn) O(nlogn)
-
遍历数组: O ( n ) O(n) O(n)
- 双指针: O ( n ) O(n) O(n)
-
总复杂度: O ( n l o g n ) + O ( n ) ∗ O ( n ) = O ( n 2 ) O(nlogn)+O(n)*O(n)=O(n^2) O(nlogn)+O(n)∗O(n)=O(n2)
-
-
空间复杂度: O ( 1 ) O(1) O(1)
4、四数之和(18)
题目描述:
【中等题】
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
思路分析:
1、两次套循环将其化简为两数之和求法
2、在每次枚举的时候,都要保证去重。
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
n=len(nums)
#特殊情况
if not nums or n<4:
return []
res=[]
nums.sort()
for i in range(n-3):
#去重
if i==0 or nums[i]!=nums[i-1]:
for j in range(i+1,n-2):
#去重
if j==i+1 or nums[j]!=nums[j-1]:
L=j+1
R=n-1
newtarget=target-nums[i]-nums[j]
while L<R:
if nums[L]+nums[R]==newtarget:
res.append([nums[i],nums[j],nums[L],nums[R]])
#去重
while L<R and nums[L]==nums[L+1]:
L+=1
while L<R and nums[R]==nums[R-1]:
R-=1
L+=1
R-=1
elif nums[L]+nums[R]<newtarget:
L+=1
else:
R-=1
return res
- 时间复杂度: O ( n 3 ) O(n^3) O(n3)
- 空间复杂度: O ( 1 ) O(1) O(1)