学习笔记@代码随想录day5:哈希表part01

学习笔记@代码随想录day5:哈希表part01

哈希表理论基础

哈希表是根据关键码的值而直接进行访问的数据结构。
简单理解,数组就是一张哈希表:哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素。
在这里插入图片描述
用来干啥?一般哈希表都是用来快速判断一个元素是否出现集合里。
例如要查询一个名字是否在这所学校里。
要枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)就可以做到。
在这里插入图片描述
通过哈希函数直接将学生的名字映射为哈希表上的索引,然后通过查询索引下标快速知道某位同学是否在这个学校里。其中hashCode可以把名字转为数值。为了保证得到的数值都落在哈希表上,对数值做取模操作。
如果学生的数据大于哈希表,会产生哈希碰撞问题。

哈希碰撞

在这里插入图片描述
上图中小李和小王同时映射到一个地址上,即哈希碰撞现象。一般哈希碰撞有两种解决方法, 拉链法和线性探测法。
拉链法
在这里插入图片描述
线性探测法
使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。
在这里插入图片描述
常见的三种数据结构有数组、set(集合)、map(映射)。
集合(Set)- 在Python中,集合可以使用set来表示。集合是一种无序的数据结构,可以存储不重复的元素。可以使用add()方法向集合中添加元素,可以使用remove()方法移除集合中的元素。

fruits = {"apple", "banana", "orange"}
fruits.add("grape")
fruits.remove("banana")
print(fruits) # 输出{'orange', 'grape', 'apple'}

映射(Map)- 在Python中,映射可以使用字典(Dictionary)来表示。字典是一种无序的键值对数据结构,可以存储多个元素,每个元素由一个键和一个值组成。可以使用键来访问字典中的值。

person = {"name": "Tom", "age": 20, "gender": "male"}
print(person["name"]) # 输出Tom

总结:当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。

有效的字母异位词 (力扣242)

下边是自己学习前写的暴力代码,测试时存在问题,对于类似于输入为"aacc","ccac"是错误的,因为我只判断数组里是否存在某个字符,判断完成后并没有去掉这个字符,还可以作为下次判断的条件,所以出错。

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        s_time=0
        t_time=0
        for i in s:
            if i in t:
                s_time+=1
            else:
                return False
        for i in t:
            if i in s:
                t_time+=1
            else:
                return False
        if s_time==t_time:
            return True
        else:
            return False

没有找到合适的移除元素的方法,不做深度研究了。
哈希表方法
在这里插入图片描述

1.定义一个数组来记录字符串s出现次数。
2.需要把字符映射到数组也就是哈希表的索引下标上,因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下标0,相应的字符z映射为下标25。
3。再遍历 字符串s的时候,只需要将 s[i] - ‘a’ 所在的元素做+1 操作即可,并不需要记住字符a的ASCII,只要求出一个相对数值就可以了。 这样就将字符串s中字符出现的次数,统计出来了。
4.那看一下如何检查字符串t中是否出现了这些字符,同样在遍历字符串t的时候,对t中出现的字符映射哈希表索引上的数值再做-1的操作。
5.那么最后检查一下,record数组如果有的元素不为零0,说明字符串s和t一定是谁多了字符或者谁少了字符,return false。

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
        for i in t:
            record[ord(i)-ord("a")]-=1
        for i in range(26):
            if record[i]!=0:
                # record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
                return False
        return True

代码中的ord(i) - ord(“a”)用于计算当前字符相对于小写字母"a"的偏移量,即"a"对应的偏移量为0,"b"对应的偏移量为1,"c"对应的偏移量为2,以此类推。

两个数组的交集 (力扣349)

#题目描述
给定两个数组 nums1 和 nums2 ,返回 它们的交集 。
输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

用数组自己写了个暴力的解法。

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        same_list=[]
        for i in nums1:
            if i in nums2:
                if i not in same_list:
                    same_list.append(i)
        return same_list

下面是set解决此问题:

 # 将两个数组转换为set类型
        set1, set2 = set(nums1), set(nums2)
        # 求两个set的交集
        #在Python中,使用&运算符可以对集合进行按位与(Bitwise AND)运算,
        #得到两个集合中共同存在的元素。
        intersection = set1 & set2
        # 将交集转换为列表类型并返回
        return list(intersection)
        #或使用以下方法
        #set1, set2 = set(nums1), set(nums2)
        #在Python中,set类提供了intersection()方法来返回两个集合的交集。
        #intersection = set1.intersection(set2)
        #return list(intersection)
 

随想录上的代码,自己手敲理解了下。

'''使用字典和集合'''
class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        # 使用字典存储nums1中的元素及其出现次数
        table = {}
        for num in nums1:
        	#如果num在字典中已经存在,将其对应的值加1;
        	#否则,将其作为新的key插入字典,并将对应的值初始化为1。
            table[num] = table.get(num, 0) + 1        
        # 使用集合存储结果
        res = set()
        for num in nums2:
            # 如果num在字典中出现过,则将其加入结果集合,并从字典中删除
            if num in table:
                res.add(num)
                del table[num]    
        # 将集合转换为列表并返回
        return list(res)
 

快乐数 (202)

'''
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
输入:n = 19
输出:true
解释:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 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)
    #get_sum()方法使用while循环遍历整数n的每个位,将各个位上数字的平方累加到一个新的变量new_num中,并返回new_num。
    def get_sum(self,n:int)->int:
        new_num=0
        while n:
            #divmod()函数的用法是:divmod(a, b),其中a和b都是数字,表示对a进行b的除法运算
            #返回一个元组,第一个元素为a除以b的商,第二个元素为a除以b的余数。
            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)
           #将整数n转换为字符串n_str是为了方便遍历n的各个位上的数字。
           for i in n_str:
               new_num+=int(i)**2
           if new_num==1: 
               return True
           else: 
               n = new_num
       return False

两数之和(1)

'''
给定一个整数数组 nums 和一个整数目标值 target
请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
'''
'''暴力'''
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]
'''使用字典'''
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
    	#定义一个字典
        records = dict()
        #在遍历列表时,可以使用enumerate()函数同时获取列表元素的下标和值
        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

总结

题量有点多,累了o(╥﹏╥)o,加油加油!

参考链接:https://www.bilibili.com/video/BV1ba411S7wu/?vd_source=0ca19358f6d978ba61da9f4823f97c25

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值