哈希表理论基础
哈希表是根据关键码的值而直接进行访问的数据结构。
那么哈希表能解决什么问题呢,一般哈希表都是用来快速判断一个元素是否出现集合里。哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
哈希碰撞:小李和小王都映射到了索引下标 1 的位置,这一现象叫做哈希碰撞。
一般哈希碰撞有两种解决方法, 拉链法和线性探测法。
拉链法:刚刚小李和小王在索引1的位置发生了冲突,发生冲突的元素都被存储在链表中。 这样我们就可以通过索引找到小李和小王了
线性探测法
使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。
常见的三种哈希结构
当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。
1、数组
2、set (集合)
3、map(映射)
242. 有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
题目链接:链接
自己理解题目:两个字符串由相同的字母构成,首先判断字符串长度必须一致,同时每个字母出现的次数要一致,于是有了如下代码:
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
if len(s)!=len(t):
return False
else:
for i in s:
if i not in t:
return False
else:
if s.count(i)==t.count(i):
continue
else:
return False
return True
反思总结:用时过长,没考虑到可以用哈希。。。
参考答案:
class Solution:
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
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
record = [0] * 26
for i in s:
#并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
record[ord(i) - ord("a")] += 1 #ord(i)的索引值是对应的ASCII索引,如A是65,如果不包含后面减a索引部分,数组下标会越界
for i in t:
record[ord(i) - ord("a")] -= 1 #ord(i)的索引值是对应的ASCII索引,如A是65,如果不包含后面减a索引部分,数组下标会越界
for i in range(26):
if record[i] != 0:
#record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
return False
return True
349. 两个数组的交集
题目链接:链接
自己理解题目:数组交集定义:交集内的元素都在两个数组内,且要求输出元素不重复,利用集合定义交集,分别遍历两个数组,判断每个元素是否在另一个数组中,在就加入集合,最后转为列表。得到如下代码:
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
ll=set()
for i in nums1:
if i in nums2:
ll.add(i)
for i in nums2:
if i in nums1:
ll.add(i)
return list(ll)
参考答案:
class Solution:#集合
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
return list(set(nums1) & set(nums2))
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
# 使用集合存储结果
res = set()
for num in nums2:
if num in table:
res.add(num)
del table[num]
return list(res)
class Solution:#数组
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
count1 = [0]*1001
count2 = [0]*1001
result = []
for i in range(len(nums1)):
count1[nums1[i]]+=1
for j in range(len(nums2)):
count2[nums2[j]]+=1
for k in range(1001):
if count1[k]*count2[k]>0:
result.append(k)
return result
反思总结:多种方式都可以实现,主要是找到对应的规则。
202. 快乐数
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
题目链接:链接
自己理解题目:除数,余数还不知道怎么求解。。。
class Solution:
def isHappy(self, n: int) -> bool:
ll=set()
def fun(m):
sun=0
while m>0:
m=m/10
sun+=m**2
m=m%10
sun+=m**2
return sun
while n!=1 and n not in ll:
ll.add(n)
n=fun(n)
return n==1 #编译只能通过部分测试用例
参考答案:
class Solution:
def isHappy(self, n: int) -> bool:
record = set()
while True:
n = self.get_sum(n)
if n == 1:
return True
# 如果中间结果重复出现,说明陷入死循环了,该数不是快乐数
if n in record:
return False
else:
record.add(n)
def get_sum(self,n: int) -> int:
new_num = 0
while n:
n, r = divmod(n, 10)
new_num += r ** 2
return new_num
class Solution:
def isHappy(self, n: int) -> bool:
record = set()
while n not in record:
record.add(n)
new_num = 0
n_str = str(n)
for i in n_str:
new_num+=int(i)**2
if new_num==1: return True
else: n = new_num
return False
class Solution:#使用数组
def isHappy(self, n: int) -> bool:
record = []
while n not in record:
record.append(n)
new_num = 0
n_str = str(n)
for i in n_str:
new_num+=int(i)**2
if new_num==1: return True
else: n = new_num
return False
class Solution:#使用快慢指针
def isHappy(self, n: int) -> bool:
slow = n
fast = n
while self.get_sum(fast) != 1 and self.get_sum(self.get_sum(fast)):
slow = self.get_sum(slow)
fast = self.get_sum(self.get_sum(fast))
if slow == fast:
return False
return True
def get_sum(self,n: int) -> int:
new_num = 0
while n:
n, r = divmod(n, 10)
new_num += r ** 2
return new_num
反思总结:除法,商,余数还没搞明白。
1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
题目链接:链接
自己理解题目:两个数之和,用tmp做中间变量(和-数组元素),判断tmp是否在数组中,在的话满足题目要求加入结果列表,不在就跳过;这里由于要求返回索引,所以利用字典,既可以存储值又可以存储索引。
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
dic={}
res=[]
for i in range(len(nums)):
tmp=target - nums[i]
if tmp not in dic:
dic[nums[i]]=i
else:
res.append(dic[tmp])
res.append(i)
return res
反思总结:解法很多,暂未完全掌握,继续理解代码。
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: # 遍历当前元素,并在map中寻找是否有匹配的key
return [records[target- value], index]
records[value] = index # 遍历当前元素,并在map中寻找是否有匹配的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]:
# 对输入列表进行排序
nums_sorted = sorted(nums)
# 使用双指针
left = 0
right = len(nums_sorted) - 1
while left < right:
current_sum = nums_sorted[left] + nums_sorted[right]
if current_sum == target:
# 如果和等于目标数,则返回两个数的下标
left_index = nums.index(nums_sorted[left])
right_index = nums.index(nums_sorted[right])
if left_index == right_index:
right_index = nums[left_index+1:].index(nums_sorted[right]) + left_index + 1
return [left_index, right_index]
elif current_sum < target:
# 如果总和小于目标,则将左侧指针向右移动
left += 1
else:
# 如果总和大于目标值,则将右指针向左移动
right -= 1
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]