哈希表理论基础
了解哈希表的内部实现原理,哈希函数,哈希碰撞,以及常见哈希表的区别,数组,set 和map。
什么时候想到用哈希法,当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法 。
什么是哈希表:
一个数组就是一张哈希表,,通过下标直接访问数组中的元素
例如要查询一个名字是否在这所学校里。
要枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)就可以做到。
我们只需要初始化把这所学校里学生的名字都存在哈希表里,在查询的时候通过索引直接就可以知道这位同学在不在这所学校里了。
哈希函数
哈希函数,把学生的姓名直接映射为哈希表上的索引,然后就可以通过查询索引下标快速知道这位同学是否在这所学校里了。
哈希函数如下图所示,通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。

如果学生数量大于哈希表大小,怎么办,哈希碰撞
哈希碰撞
两个学生都同时索引到一个下标,就是哈希碰撞
2种解决办法:拉链法,线性探测法
拉链法

线性探测法
例如冲突的位置,放了小李,那么就向下找一个空位放置小王的信息。所以要求tableSize一定要大于dataSize。

常见的3种哈希结构
常见的三种哈希结构核心作用都是快速判断元素是否在集合/快速做键值映射 ,用大白话解释如下:
-
数组:最基础的哈希结构,直接用数组下标 当作哈希表的"键",数组元素当作"值",通过下标一步找到对应内容,查询/增删都是O(1),缺点是下标只能是整数,且数组大小固定,容易浪费空间。
-
set(集合):专门存不重复的单个元素 (部分变体如multiset可存重复),不用自己维护下标,直接存数据本身作为"键",能快速判断某个元素是否存在,适合只需要存值、不需要关联其他信息的场景。
-
map(映射):存键值对(key-value),一个key对应一个value,通过key能快速找到对应的value,适合需要把两个相关数据关联起来的场景(比如存学生学号和对应姓名),key不能重复(multimap可重复),value无限制。
简单记:只存唯一值用set,存键值对应关系用map,下标能直接用、数据范围固定时用最基础的数组。
242.有效字母异位词
题目链接: 242. 有效的字母异位词 - 力扣(LeetCode)
判断两个字符串是不是由相同的字母但是可能不同顺序组成
没有用哈希,直接写,但是用了库函数

python
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
if sorted(s)==sorted(t):
return True
else:
return False
思路
用一个26大小的哈希表存储,对应a-z,26个不同的字母。
先统计其中一个字符串每个字母出现的次数,放到对应的哈希表位置中。
在第二个字符串统计时,减去对应字母出现的次数
最后验证这个哈希表的所有值是不是为0,用于判断两个字符串的字母是否相同。
伪代码
python
int hash[26];
for(i=0;i<s.size;i++):
hash[s[i]-'a']++;//字母到下标的映射,把a对应到下标0的位置
for(i=0;i<t.size;i++):
hash[s[i]-'a']--;
for(i=0;i<26;i++):
if(hash[i]!=0):
return false
return true
写题


python
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
hash=[0]*26
i=0
while(i<len(s)):
hash[ord(s[i])-ord('a')]+=1
i+=1
i=0
while(i<len(t)):
hash[ord(t[i])-ord('a')]-=1
i+=1
i=0
while(i<26):
if hash[i]!=0:
return False
i+=1
return True
- 核心逻辑错误 :处理字符串
t时,误将代码中的t[i]写成了s[i]。这会导致程序用s的字符去抵消计数,而非t的字符。 - 索引边界错误 :最初的代码中所有
while循环条件用了<=(如i<=len(s)、i<=26),而字符串 / 数组的索引范围是0 ~ 长度-1,这会触发索引越界异常,导致程序直接崩溃。 - 直接用s[i]-'a'也会报错,在python里面
349. 两个数组的交集
题目链接349. 两个数组的交集 - 力扣(LeetCode)

python
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
res=[]
i=0
if len(nums1)<len(nums2):
while(i<len(nums1)):
if nums1[i] in nums2:
res.append(nums1[i])
i+=1
else:
while(i<len(nums2)):
if nums2[i] in nums1:
res.append(nums2[i])
i+=1
res=list(set(res))
return res
思路
我还是想不到用哈希表,我刚开始就是像上面那样写的。
哈希表思路:
把nums1处理并放到哈希表里面,再去查nums2是否在哈希表里面出现过,如果出现过,我们就把结果放到result数组里面。
202. 快乐数

python
class Solution:
def isHappy(self, n: int) -> bool:
seen=[]
while(n!=1):
n=sum(int(i)**2 for i in str(n))
if n in seen:
return False
seen.append(n)
return True
1.两数之和
python
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
res=[]
i=0
for i in range(len(nums)):
if target-nums[i] in nums:
res.append(i)
elif nums[i]==target-nums[res[0]]:
res.append(i)
return res

刚开始看到自己写,
1.想要一个可以从数组值映射到数组下标的
2.单纯用if target-nums[i] in nums:判断是不行的,3,2,4和为6,会把下标0加入结果。
思路
map存放我们遍历过的元素,数组值作为键,下标作为值
2,7,3,6
找到2,想找7,查询是否遍历过7,没有遍历过,把2的键值对存入map
现在到7,想找2,查询是否遍历过7,遍历过,把map里面的2的下标放入结果。
伪代码
python
unordered-map(int,int) map
for(i=0;i<nums.size;i++):
s=target-nums[i];
iter=map.find(s);
if(iter!=map.end()):
return iter->value,i
map.insert(nums[i],i);
写题
1.给records定义错类型了,把dict弄成了list
2.index和vlaue的顺序
python
for index,value in enumerate(nums):

python
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
records=dict()
for index,value in enumerate(nums):
if target-value in records:
return [records[target-value],index]
records[value]=index
return []