(补卡,落了一个月的课终于开始补了orz)
哈希表
- 简单来说数组就是一张哈希表。当我们遇到了要快速判断一个元素是否出现在集合里的时候,就要考虑哈希法。枚举的时间复杂度是O(n),而哈希法只是O(1)。但是哈希法也是牺牲了空间换取了时间。
- 具体讲解可以详见网址:代码随想录 (programmercarl.com)
- 解题方法:
- 数组:使用下标做哈希映射。适用于题目给出的数据的长度较小、其中元素的值比较均衡不分散跨度小的情况;
- set:适用于数据长度没有限制或其中元素较为分散的情况,例如:[0, 5, 1000000]的情况。但是只能存放一种元素。(在c++中有三种结构,如下表)
- map:(在c++中有三种结构,如下表)
242.有效的字母异位词
初始思路:一开始把题理解错了,以为是元素之间两两交换后得到的字符串是异位词...
思路更新:因为题目中有一个“注意”是说只要两个字符串中的字母数量一致就可以认为是有效字母异位词。
代码:
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
record = [0] * 26 # 申请一个全为0的大小为26的数组
s1, t1 = list(s), list(t) # 将字符串转为列表
for i in s1:
record[ord(i) - ord("a")] += 1 # 在对应元素的位置记录其个数(ord为计算其ASCII码的函数)
for i in t1:
record[ord(i) - ord("a")] -= 1 # 在对应位置数量-1
for i in record:
if i != 0:
return False # 但凡有一个数量不是0的就返回False
return True
349. 两个数组的交集
coding tips:可以用 { } 或者 set( ) 函数创建集合,但是创建一个空集合只能使用set(),不能使用{ },因为'{ }'是创建空字典。
根据题目中的要求,数据长度和大小均小于1000,因此此题目可以采用数组的方法解决;如果题目没有限制大小与长度时,可以考虑使用set解决。(发问:那不管什么情况都适用set不好吗?答:直接使用set不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的,当数据量比较大时,差异还是显著的。)
对此,我们尝试使用两种方法解决:
- 数组
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
number1 = [0] * 1001 # 数组要比实际所需大一点
number2 = [0] * 1001
result = []
for i in nums1:
number1[i] = 1
# print(number1)
for i in nums2:
number2[i] = 1
for i in range(len(number2)):
if number1[i] * number2[i] > 0:
result.append(i)
return result
- set
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
result = set(nums1) & set(nums2)
return list(result)
特意去查了一下python中set的用法,set是一个无序的不重复元素序列。set的用法可以参考:Python3 集合 | 菜鸟教程 (runoob.com),或者直接在浏览器中搜索都可以。
注意的是:最后返回的应该是一个list,刚开始我没写list(),结果报错了,忘记最后在函数中要返回一个List[int]。
202. 快乐数
coding tips:需要写一个额外的函数时就写,与需要调用函数完成某个功能类似,一切为了解题。
思路更新:因为它是一个无限循环的过程,在这种情况下,使用“while True”就比较合适,结束循环的条件是:1)当出现重复的和时→返回False;2)当和为1时→返回True。如果这两种情况都不是,就将结果存下来。set使用在了结果存储的地方(因为结束条件1,所以此处使用list也是一样的)。在循环中需要调用一个计算平方和的函数。
代码:
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:
res = 0
while n:
# res += (n % 10) ** 2
# n = (n - (n % 10)) // 10
n, r = divmod(n, 10)
res += r ** 2
return res
本题还有用快慢指针的循环链表的方法,浅浅理解为:如果陷入无限循环,相当于找到开始进入循环的节点。方法可以参考day4的循环链表II。
1. 两数之和
初始思路:将nums的两数之和保存于record中,当某一个保存的结果与target一致时,返回对应两数的下标。但是在实现的过程中...使用了sort,把原始的顺序排序了,所以导致最后错了,忘记不能改变原顺序啦!!!
课程:学透哈希表,map使用有技巧!LeetCode:454.四数相加II_哔哩哔哩_bilibili
思路更新:按照课程进展本题应该会使用map。map的key中存储的是已经遍历过的元素,而不是我之前想的“和”。在遍历数组的时候,只需要向map去查询是否有和目前遍历元素匹配的数值,就是target与当前元素做减法,在map的key中遍历是否有与“差”相同的数值(妙!!!)。如果有,返回key对应的value和当前元素的下标,如果没有,就把目前遍历的元素放进map中,因为map存放的就是我们访问过的元素。
本题需要弄清楚四个问题:
- 为啥用哈希法:因为题目中说到判断两数之和是否满足target,与“判断一个元素是否出现过”的思想相似;
- 为啥用map:我们不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,需要使用 key-value结构来存放,key存元素,value存下标;
- map在其中的作用是什么:存放我们访问过的元素;
- map中key、value分别表示什么:key存放遍历过的元素、value存放对应元素的下标;
代码:
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中
return []
c++中的map就是Python中的dict,而Python中的map是另一个东西,详见博客:详细分析Python遇到的各种数据结构Map、Dict、Set、DataFrame、Series、Zip_python map结构-CSDN博客