《代码随想录》Day6
1. 哈希表理论基础
哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素,一般遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法,但哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
哈希函数
可以将关键码映射为哈希表的索引,后续就可以通过查询索引下标快速找到关键码
哈希碰撞
多个关键码映射到了同一索引位置,一般用拉链法或线性探测法解决
拉链法
线性探测法
将多余的关键码放到哈希表的空位中
哈希表结构
一般有三种数据结构:数组、Set(集合)、map(映射)
2. Q242.有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1: 输入: s = “anagram”, t = “nagaram” 输出: true
示例 2: 输入: s = “rat”, t = “car” 输出: false
排序
将s与t分别排序字母,若排序后相等则是字母异位词
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
if sorted(s) == sorted(t):
return True
else:
return False
或者直接
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
return sorted(s) == sorted(t)
Counter() 函数
Counter()可以用来统计序列中每个元素出现的次数。
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
s = Counter(s) #可以用来统计序列中每个元素出现的次数
t = Counter(t)
return s == t
defaultdict()函数
defaultdict()函数在collections包中
defaultdict(int) 可创建一个value为整数类型的字典
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
from collections import defaultdict
s_dict = defaultdict(int) # 创建一个value为整数类型的字典
t_dict = defaultdict(int)
for i in s:
s_dict[i] +=1
for j in t:
t_dict[j] +=1
return s_dict == t_dict
哈希表
- 构建一个26位字母的数组来对字符串中出现过的字母计数
- 利用ord()将字母转化为ASCII格式的10进制数字
- 遍历字符串s,出现某字母则数组该字母对应位置+1
- 遍历字符串t,出现某字母则数组该字母对应位置-1
- 最后若数组不全为0则s和t不一致
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
count = [0]*26
for i in s:
count[ord(i)-ord('a')] += 1
for j in t:
count[ord(j)-ord('a')] -= 1
for i in range(26):
if count[i] != 0:
return False
return True
3. Q349. 两个数组的交集
set()函数
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
a = set(nums1) #set() 函数创建一个无序不重复元素集,重复的被删掉
b = set(nums2)
return list(a & b)
哈希表 - 数组
遍历数组 nums1,对于其中的每个元素,遍历数组 nums2 判断该元素是否在数组 nums2 中,如果存在,则将该元素添加到返回值。
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
arr = [0]*1001 #因为两数组范围都为[1,1000]
for num in nums1:
arr[num] = 1 #储存nums1中元素
ans = []
for num in nums2:
if arr[num] == 1:
ans.append(num) #判断是否在nums2中
arr[num] += 1 #该数字已被计算,则改变数值防止重复计算
return ans
哈希表 - set
如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。则可考虑用set,但直接使用set 不仅占用空间比数组大,而且速度要比数组慢
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
val_dict = {} #set
ans = []
for num in nums1:
val_dict[num] = 1
for num in nums2:
if num in val_dict.keys() and val_dict[num] ==1:
ans.append(num)
val_dict[num] = 0
return ans
4. Q202. 快乐数
思路
- 各数位平方和的写法
1. `sum( int(i)**2 for i in str(num))`
2.
while num:
sum += num % 10 #取num除以10的余数,相当于取个位数
num = num //10 #取商的整数部分,相当于每次往前一个数位
- 无限循环
算快乐数的时候有可能陷入无限循环,所以需要一个set来储存sum的结果,若sum的结果之间出现过,则代表已陷入循环
哈希表-set
我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法了,此时用record = set()
,添加元素用record.add(n)
class Solution:
def isHappy(self, n: int) -> bool:
record = set() #创建一个set来储存结果
sum_ = n
while True:
sum_ = sum(int(i)**2 for i in str(sum_)) #各数位平方和
if sum_ in record:
return False
elif sum_ == 1:
return True
else:
record.add(sum_)
5. Q1. 两数之和
给定一个整数数组 nums 和一个目标值 target,在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
暴力解法
两层for循环
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]
哈希表- map
- 优化思路:
用哈希表可以快速查找,可以将遍历过的元素记录在哈希表内,每次搜索时看哈希表内能否出现匹配元素,若能凑成target,则返回对应元素组下标;该方法节约了再回到nums中查找数值和索引的时间 - 哈希表使用:
此题中需要存两类数据,数值和索引值,所以考虑使用 map 的key value结构,key用来存元素,value用来存下标
{key:数据元素,value:数组元素对应的下标}- 数组和set来做哈希法的局限。
- 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
- set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。
- map中key和value分别表示什么
- key的元素为需要判断是否出现过的元素,所以数组中数值作为key
- 对应的value就是下标了
- 数组和set来做哈希法的局限。
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
record = {} #{key:数据元素,value:数组元素对应的下标}
for index, value in enumerate(nums): #遍历nums
if target - value in record: #需查找的是target - value,即为record中的key
return [record[target - value],index] #若满足条件,返回对应数组对下标
record[value] = index #将这次遍历到的元素index作为map值记入record中
return []