哈希表用来快速判断一个元素是否出现集合里。例如要查询一个名字是否在这所学校里。要枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)就可以做到。
如果两个对象映射到同一个索引下标,这种现象叫做哈希碰撞。
常用解决方法:拉链法或线性探测法。
拉链法
拉链法的基本思路是将哈希表的每个槽变成一个链表或其他动态数据结构(如链表、平衡树等)。当多个键产生相同的哈希值时,它们将存储在该槽的链表中。每个槽不再只存储单一的元素,而是存储一个指向链表头部的指针。
线性探测法
开放地址法的核心思想是,当发生哈希碰撞时,不另用链表存储冲突元素,而是寻找哈希表中下一个可用的空槽,将冲突的元素存储在该空槽中。线性探测法的策略是,每次碰撞后,按固定步长(通常为1)继续查找下一个槽。
当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。
- 数组
- set(集合)
- map (映射)
总结一下,当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
如果在做面试题目的时候遇到需要判断一个元素是否出现过的场景也应该第一时间想到哈希法!
在编程领域,字母异位词是指两个字符串包含的字母相同,但排列顺序不同。这个问题是计算机科学中的经典问题,常用于面试和算法练习。
解法一: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
a_count = Counter(s)
这行代码使用 Counter 对字符串 s 进行计数。它会返回一个类似字典的对象,其中的键是 s 中的每个字母,值是该字母出现的次数。例如,如果 s = "listen",那么 a_count 会是 {'l': 1, 'i': 1, 's': 1, 't': 1, 'e': 1, 'n': 1}。
return a_count == b_count
这一行代码通过比较两个 Counter 对象 a_count 和 b_count 来判断它们是否相等。如果两个 Counter 对象相等,说明两个字符串中每个字母的出现次数完全相同(即它们是字母异位词)。否则,它们不是字母异位词。
第二次写:没想到是第一句导入包出错。记得是from collections,然后类会首字母大写。
解法二:使用数组作为哈希表
关键思路:在record = [0] * 26的数组中,s有的字母+1,t有的字母-1。如果最后26个字母都是0,说明两个字符串中的字母数量完全一致。
dmsxl在力扣的题解错误:return False之后没有必要接break,这是毫无意义的。这没有用多线程的库,这不是多线程的。不需要考虑线程安全。
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
record = [0] * 26
for i in s:
record[ord(i) - ord("a")] += 1
for i in t:
record[ord(i) - ord("a")] -= 1
for i in range(26):
if record[i] != 0:
return False
return True
进度差很多所以只看最短的解法了(大概可能不是代码随想录想教会的那个)
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
return list(set(nums1) & set(nums2))
但是很简便耶!
重点1:题目要求返回数组而不是集合,所以要用list()转换。
重点2:Python中的&和&&。python里没有&&。在其他编程语言中,&& 是逻辑与运算符,用于判断两个布尔表达式是否都为真。在 Python 中,and 是逻辑运算符,用于判断两个布尔表达式是否都为真。(短路计算,前面的条件为假就不会看后面的条件)
& 是按位与运算符,主要用于以下场景:
按位与运算(bitwise AND):对整数的二进制形式逐位进行“与”操作。
result = 5 & 3 # 0101 & 0011 = 0001 -> 结果是 1
集合交集:用于计算两个集合的交集。
result = {1, 2, 3} & {2, 3, 4} # 结果是 {2, 3}
布尔运算:可以用于对布尔值进行与运算,但与 and 不同,它不支持短路计算。
result = True & False # 结果是 False
补充:交集计算:result = set1.intersection(set2)
做题经验:“可能是 无限循环 但始终变不到 1” -->说明可能会出现重复的数字。重复出现时就是False。
直观的写法:
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
从力扣运算快的里面捞到的一个写法:
语法:在python中,x ** y 表示x 的 y 次方。(在C或者Java或者go中,幂运算都是用pow(x,y)的。)
class Solution:
def isHappy(self, n: int) -> bool:
seen = set()
while n != 1 and n not in seen:
# 这个while条件的巧妙之处在于,当n在set里面出现过时就会退出循环
# 而不是另外写 if n in seen
seen.add(n)
digit_sqr = [int(digit)**2 for digit in str(n)]
n = sum(digit_sqr)
return n == 1
# 这个也是如上的巧妙,直接返回 n == 1而不是if...
这道题放在这里主要是说明set的局限性。因为本题不止要返回数字,还要返回下标,所以set不能用。set只适合用在只需要返回数字的情况。(嗯?但是给的python代码是可以用set啊)
而如果用数组,数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
此时就要选择另一种数据结构:map ,map是一种key value的存储结构,可以用key保存数值,用value再保存数值所在的下标。(嗯?所以python没有map?)
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
seen = set()
for i, num in enumerate(nums):
# 有点分不清i和num分别是啥
complement = target - num
if complement in seen:
return [nums.index(complement), i]
seen.add(num)
很多疑问的一题。