系列文章目录
代码随想录算法训练营第一天|数组理论基础,704. 二分查找,27. 移除元素
代码随想录算法训练营第二天|977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II
代码随想录算法训练营第三天|链表理论基础,203.移除链表元素,707.设计链表,206.反转链表
代码随想录算法训练营第四天|24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II,总结
文章目录
- 系列文章目录
- 哈希表理论基础
- (一)哈希表的概念
- (二)哈希函数
- (三)哈希碰撞
- (四)总结
- 242.有效的字母异位词
- 349. 两个数组的交集
- 202. 快乐数
- 1. 两数之和
哈希表理论基础
当遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
(一)哈希表的概念
- 哈希表的含义:根据关键码的值而直接进行访问的数据结构;
- 数组就是一张哈希表,哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素;
- 哈希表的作用:一般哈希表用来快速判断一个元素是否出现集合里;
- 枚举查询的时间复杂度是O(n),但使用哈希表的时间复杂度为O(1)。
(二)哈希函数
哈希函数通过hashCode的特定编码方式将其他数据格式转化为不同的数值,从而把需要存储的内容映射为哈希表上的索引数字。
(三)哈希碰撞
哈希碰撞的现象:由于内容数比哈希表数多,导致不同内容同时映射到哈希表同一个索引下标的情况;
哈希碰撞的两种解决方法:拉链法、线性探测法;
-
拉链法:发生冲突的元素都被存储在链表中
拉链法需要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。 -
线性探测法:
线性探测法要保证tableSize大于dataSize,依靠哈希表中的空位来解决碰撞问题。
冲突的位置,放了小李,那么就向下找一个空位放置小王的信息:
(四)总结
- 使用哈希法解决问题时,一般会选择如下三种数据结构:数组、set (集合)、map(映射);
- 数组:哈希值比较小,范围也比较小
- set:数值比较大
- map:key,value
- 哈希法是牺牲空间换取时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找;
- 做题时遇到需要判断一个元素是否出现过的场景应该第一时间想到哈希法。
242.有效的字母异位词
题目链接: 242.有效的字母异位词
题目内容: 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。(若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词)
视频讲解:学透哈希表,数组使用有技巧!Leetcode:242.有效的字母异位词
核心思路:定义一个数组叫做record用于遍历增加记录字符串s里字符出现的次数,然后遍历减少记录字符串t里字符出现的次数,最后判断record中的记录是否为0。
class Solution:def isAnagram(self, s: str, t: str) -> bool:#创建一个长度为26的新数组record = [0] * 26#遍历字符串sfor i in s:record[ord(i)-ord('a')]+=1 #ord()返回字符串的ASCII数值#遍历字符串tfor i in t:record[ord(i)-ord('a')]-=1#查看数组是否为0for i in record:if i != 0:return Falsereturn True
349. 两个数组的交集
题目链接: 349.两个数组的交集
题目内容: 给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
视频讲解:学透哈希表,set使用有技巧!Leetcode:349. 两个数组的交集
用数组做:
初步思路为创建一个hash数组,然后将nums1的元素加进去,遍历nums2的元素,在hash数组中出现了,就存到结果中,但是在python中这样实现最后出来的结果不唯一。
看了代码随想录的方法,创建了两个哈希数组,分别遍历nums1和nums2的元素,如果两个哈希数组对应取值的乘积大于0,说明两个哈希数组里都有该值,可以输出,此时的输出结果唯一,满足题意。
class Solution:def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:count1=[0]*1005count2=[0]*1005record=[]for i in nums1:count1[i]+=1for i in nums2:count2[i]+=1for k in range(1005):if count1[k]*count2[k]>0:record.append(k)return record
用set做:
class Solution:def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:return list(set(nums1) & set(nums2))
用set+字典做:
主要思路在于用哈希表存储nums1的元素,判断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#使用集合存储结果result=set()for num in nums2:if num in table:result.add(num)del table[num]return list(result)
202. 快乐数
题目链接: 202.快乐数
题目内容: 编写一个算法来判断一个数 n 是不是快乐数。(快乐数定义:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1,也可能是无限循环 但始终变不到 1。如果这个过程结果为 1,那么这个数就是快乐数。如果 n 是快乐数就返回 true ;不是,则返回 false)
用数组做:
核心思想:使用哈希法判断某一次求和的结果是否重复出现,如果重复了就是return false, 否则一直找到和为1为止。
class Solution:def isHappy(self, n: int) -> bool:record=[]while n not in record:record.append(n)new_num=0n_str=str(n)for i in n_str:new_num+=int(i)**2if new_num ==1:return Trueelse:n=new_numreturn False
用集合做:
核心思想与用数组做类似
class Solution:def isHappy(self, n: int) -> bool:record=set()while n not in record:record.add(n)sum=0n_str=str(n)for i in n_str:sum+=int(i)**2if sum == 1:return Trueelse:n=sumreturn False
1. 两数之和
题目链接: 1.两数之和
题目内容: 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target的那两个整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。
视频讲解:梦开始的地方,Leetcode:1.两数之和,学透哈希表,map使用有技巧!
核心思想:需要一个集合来存放遍历过的元素,然后在遍历数组的时候去询问这个集合,某元素是否遍历过,也就是是否出现在这个集合。
使用字典:
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 []
使用集合:
class Solution:def twoSum(self, nums: List[int], target: int) -> List[int]:records=set()for index,value in enumerate(nums):if target-value in records:return [nums.index(target-value),index]records.add(value) return []
双指针法:
核心思想:对数组排序之后,采用缩小区间的方式
class Solution:def twoSum(self, nums: List[int], target: int) -> List[int]:nums_sorted=sorted(nums)left=0right=len(nums_sorted)-1while 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+1return [left_index,right_index]elif current_sum<target:left += 1else: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]