1.哈希表理论基础
哈希表是根据关键码的值而直接进行访问的数据结构。数组就是一张哈希表。哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素。
一般哈希表都是用来快速判断一个元素是否出现集合里。
哈希函数则是通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,从而映射到哈希表的索引数字。如果hashCode得到的数值大于哈希表的大小,则会进行取模操作。
哈希碰撞是指不同元素映射到了哈希表中同一个索引下标的位置,一般有两种解决方法:拉链法和线性探测法。
拉链法(左图):要选择适当的哈希表大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。
线性探测法(右图):必须保证tablesize>datasize,因为需要利用哈希表中的空位来解决碰撞问题。如果冲突的位置已经有了元素,那么就向下找一个空位放置碰撞的另一个元素。
常用的三种哈希结构包括:数组、集合(set)、映射(map)
数据小用数组,数据大用set,数据比较散用map
当遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
但是哈希法也是牺牲了空间换取了时间,因为要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
242.有效的字母异位词
由于题目规定输入的字符串均由小写字母组成,所以可以直接定义一个长度为26的全零数组作为哈希表,用来记录字符串中字符出现的次数。因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下标0,相应的字符z映射为下标25。在遍历字符串时,只需将 s[i] - ‘a’ 所在的元素做+1操作即可,并不需要记住字符a的ASCII,只须求出一个相对数值。
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
hash = [0] * 26 # 创建长度为26的全零数组作为哈希表
for i in s:
hash[ord(i) - ord('a')] += 1 # 字符串中每一个字母的ascii码减去‘a’的asdii码,哈希表中对应位置+1
for i in t:
hash[ord(i) - ord('a')] -= 1 # 第二个字符串同理,但是要在哈希表中对应位置-1,若有与上一个字符串出现相同次数的字母,则哈希表中该位置抵消变为0
for i in hash:
if i != 0: # 最后遍历哈希表,若某位不为零则说明两个字符串未能完全抵消
return False
return True
其实最容易想到的办法就是直接把两个字符串排序,然后判断是否相等即可 。
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
s = sorted(s)
t = sorted(t)
if s == t:
return True
else:
return False
python中还有一种方法可以直接使用collection库中的Counter函数来对字符串中每个字母出现的次数进行统计,然后返回统计的结果。这样的话只需要对比两个字符串Counter函数的返回值是否相同,就可以得知是不是字母异位词了。
class Solution(object):
def isAnagram(self, s: str, t: str) -> bool:
from collections import Counter
a_count = Counter(s)
b_count = Counter(t)
return a_count == b_count
349.两个数组的交集
输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的,同时可以不考虑输出结果的顺序。当题目限制了数值的大小时,可以优先考虑用数组来做哈希。但如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。本题中采用数组和set的方法都可以。
首先能想到的最简单解法就是调用python中的intersection函数,直接返回集合的交集。
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
s1 = set(nums1)
s2 = set(nums2)
return list(set.intersection(s1, s2)) # 最后返回值要改为list型
另一种方法是结合使用字典和集合,用字典来存储元素,然后用集合存储结果并实现去重操作:
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
# 使用哈希表存储一个数组中的所有元素
table = {}
for num in nums1:
table[num] = table.get(num, 0) + 1 # 取字典中key=num位置的value,初始为零,num每出现一次,该位置的value+1
# 使用集合存储结果
res = set()
for num in nums2:
if num in table:
res.add(num)
del table[num]
return list(res)
由于力扣上给出了本题数组长度小于等于1000,所以也可以采用数组来做哈希:
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
arr1 = [0] * 1010 # 多开一点防止溢出
arr2 = [0] * 1010
result = []
for i in nums1: # 遍历数组,记录每个数字出现的次数,保存到arr中对应下标位置
arr1[i] += 1
for i in nums2:
arr2[i] += 1
for k in range(1010):
if arr1[k] * arr2[k] != 0: # 若乘积不为零,则说明两边均不是0,该元素属于交集
result.append(k)
return result
202.快乐数
本题需要先编写一个求数字各位平方和的求和函数,然后反复判断求和得到的sum是否为1,如果是1,则返回True。如果不是1,那么就需要对sum再次求各位平方和。
重点:主要考虑的就是题目中的无限循环问题。什么情况下会出现无限循环,一定是求出的sum值重复出现的情况下会导致无限循环,如果sum始终没有重复出现,那么最后一定可以算出1。
class Solution:
def sum(self, n: int) -> int: # 求各位平方和的函数
sum = 0
while n:
sum += (n % 10) * (n % 10)
n //= 10
return sum
def isHappy(self, n: int) -> bool:
s = set() # 创建集合用于保存每次得到的sum
result = self.sum(n)
while 1:
result = self.sum(result)
if result == 1:
return True
elif result in s: # 如果sum重复出现过,则说明不是快乐数
return False
else:
s.add(result) # 若sum此前未出现过,则将其加入集合
continue
1.两数之和
重点:
①为什么会想到用哈希表:
当需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,第一时间想到哈希法。
②哈希表为什么用map:
本题不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,所以要使用map。
③本题map是用来存什么的:
map用来存放访问过的元素,因为遍历数组的时候,需要记录之前遍历过哪些元素和对应的下标,这样才能找到与当前元素相匹配的(也就是相加等于target)
④map中的key和value用来存什么的:
判断元素是否出现,元素作为key,所以数组中的元素作为key,有key对应的就是value,value存下标。所以 map中的存储结构为 {key:数据元素,value:数组元素对应的下标}。
在python中使用dict()来实现map的存储:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
records = dict() # 创建字典用来保存访问过的元素及其对应下标
for key, value in enumerate(nums): # 遍历数组
if target - value in records: # 如果差值在字典中
return [records[target - value], key] # 返回当前遍历元素的下标以及与其和为目标值的元素下标
records[value] = key # 如果不在字典中,则将其添加进去
return []
使用集合的方法
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
#创建一个集合来存储我们目前看到的数字
seen = set()
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [nums.index(complement), i]
seen.add(num)
暴力解法:
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]