《极客时间--算法面试》-哈希表
哈希表
有效的字母异位词
两数相和
三数相和
四数相和
力扣242:有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram" 输出: true
示例 2:
输入: s = "rat", t = "car" 输出: false
说明:
思路:
一、排序
将两个词都排序,快排是nlogn,最终看两者是否相同。
二、map进行计数
对两个词分别map计数,最终比较两个字典是否相同
代码:
class Solution(object): def isAnagram(self, s, t): """ :type s: str :type t: str :rtype: bool """ ''' #方案一:采用排序的方式 return sorted(s) == sorted(t) #nlogn时间复杂度 ''' dict1, dict2 = {}, {} #两个字典存放两个字符串的键值对 for item in s: dict1[item] = dict1.get(item,0)+1 #get方法是查找指定键的值,如果不存在返回默认值,最开始即为0,计数+1 for item in t: dict2[item] = dict2.get(item,0)+1 return dict1 == dict2 #最终比较两个字典是否相同
两数相和
https://leetcode-cn.com/problems/two-sum/
思路:
一、暴力破解,采用两重循环,至少可以解决问题,时间复杂度为N的平方。
二、转换问题,y=target-x,遍历x然后在哈希表中查找是否有有,遍历为N,查找为1,总共的时间复杂度为N
代码:
class Solution(object): def twoSum(self, nums, target): """ :type nums: List[int] :type target: int :rtype: List[int] """ dic = {} #存放遍历元素之前的键值对 for i in range(len(nums)): #遍历全部元素 y = target - nums[i] #转换思路 if y in dic: return [dic[y],i] #返回下标 dic[nums[i]] = i #将当前元素之前的元素加入到字典中
三数求和
力扣:https://leetcode-cn.com/problems/3sum/submissions/
思路:
一、暴力破解,将会是立方级别的时间复杂度。
二、在此基础上,c = target -a -b,采用查询,这样就是平方级别的时间复杂度。
三、排序的基础上夹逼,会改变原始数据,时间复杂度较低(推荐)
首先对原数组进行排序,开始从头遍历第一个元素,第二和三值采用双指针,前后夹逼遍历。期间需要处理,如果中间值重复的话需要继续,减少计算量。
代码:
class Solution(object): def threeSum(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ nums.sort() #首先进行排序 n = len(nums) res = [] #存放中间结果值 for i in range(n): #遍历全部的数据 if i>0 and nums[i]==nums[i-1]: #如果这两个数重复,就继续向后遍历 continue left = i+1 #left是子数组的左指针,从i+1开始 right = n-1 #right是子数组的右指针,从n-1开始 while left < right: #大循环,查找子数组中的值 cur_sum = nums[i]+nums[left]+nums[right] #如果三者值等于目标值 if cur_sum == 0: #此时的目标值为0 temp = [nums[i],nums[left],nums[right]] res.append(temp) #将其结果保存 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 cur_sum < 0: #如果值三者和小于目标值,由于是已经排好序的 left += 1 #前值小于后值,那就寻找大的数 else: #同理,如果大于目标值,向前查找 right -= 1 return res #最终返回结果值
四数相和
https://leetcode-cn.com/problems/4sum/submissions/
思路:
根据上面的三数相和思路,双指针双向夹逼。
第一个数从1到倒数第四个值进行遍历
第二个值从第一个值后面到倒数第三个值进行遍历
第三和第四值采用双指针双向夹逼遍历。
中间需要进行判断是否重复代码和是否去掉一些冗余的计算,比如前面的值都大于了目标值,那么后面的计算是没有意义的,直接跳过即可。
代码:
class Solution(object): def fourSum(self, nums, target): """ :type nums: List[int] :type target: int :rtype: List[List[int]] """ n = len(nums) #获取数组的长度 if n<4: return [] #如果数组个数小于4个,直接返回空 nums.sort() #进行排序 res = [] #存放结果值 for i in range(n-3): #第一个值从,头到倒数第四个截止,【0,n-4】 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[n-1]+nums[n-2]+nums[n-3]<target: #如果后面的值小于目标值,则跳出下次循环 continue for j in range(i+1,n-2): #第二个值从[i+1,n-3]遍历 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[n-1]+nums[n-2]<target: #同理,如果后面的值小于了目标值,则跳出直接下个循环 continue left = j+1 #双指针,第三个数,向后夹逼遍历 right = n-1 #第四个数,向前夹逼遍历 while left<right: temp = nums[i]+nums[j]+nums[left]+nums[right] #四数相和 if temp == 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 temp<target: #如果小于,则向后夹逼 left += 1 else: #同上,向前夹逼 right -= 1 return res #最终返回结果值