LeetCode Day5|242.有效的字母异位词、349.两个数组的交集、202.快乐数、1.两数之和

哈希表理论基础:

哈希表:是一种根据关键码的值而进行直接访问的数据结构,例如数组就是一张哈希表,其中关键码就是数组的索引下标,通过下标可以直接访问数组中的元素,如下图:

哈希表的作用:一般用来快速判断一个元素是否出现在集合里,时间复杂度为O(1)

哈希函数:

例如查询一个学生名字,是否在这个学校里,哈希函数(hash function) 把学生的姓名直接映射为哈希表上的索引,然后就可以通过查询索引下标快速知道结果。

哈希函数如下图所示,通过hashCode把名字转化为数值,一半hashCode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。

如果hashCode得到的数值大于哈希表的大小了,也就是大于tableSize了,怎么办呢?

此时为了保证映射出来的索引数值都落在哈希表上,我们会再次对数值做一个取模的操作,这样我们就保证了学生姓名一定可以映射到哈希表上了。

此时问题又来了,哈希表我们刚刚说过,就是一个数组。

如果学生的数量大于哈希表的大小怎么办,此时就算哈希函数计算的再均匀,也避免不了会有几位学生的名字同时映射到哈希表 同一个索引下标的位置。

这就是哈希碰撞。

如下图所示,小李和小王都被映射到了索引下标1的位置,这一现象叫做哈希碰撞

一半哈希碰撞有两种解决办法,拉链法和线性探测法。

拉链法:

对于发生碰撞的小李和小王,发生冲突的元素都被存储在链表中,这样我们就可以通过索引找到小李和小王了

(数据规模是dataSize, 哈希表的大小为tableSize)

拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间

 线性探测法:

使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。

例如冲突的位置,放了小李,那么就向下找一个空位放置小王的信息。所以要求tableSize一定要大于dataSize ,要不然哈希表上就没有空置的位置来存放 冲突的数据了。如图所示:

常见的三种哈希结构:

当我们想使用哈希法来解决问题的时候一般用三种数据结构:数组,set(集合),map(映射)

 题目链接:242. 有效的字母异位词 - 力扣(LeetCode)

思路:

首先是暴力解法,两层for循环,同时要记录字符是否重复出现,时间复杂度为O(n²)

更优的方式,数组就是一个简单的哈希表,而且在此题中只有小写字符,那么可以定义一个数组,来记录字符串s中字符出现的次数,只需定义record大小为26即可,初始化为0,因为字符a到字符z的ASCⅡ码也是26个连续的数值。

在遍历字符串s时,用s[i]的ASCⅡ码减去字符“a”的ASCⅡ码,即得到 [0, 25]之间的一个整数,再让record对应位置+1;遍历字符串t时同理record对应位置-1,最后判断得到的record数组中有无非零元素即可完成。

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        record  = [0] * 26
        for i in s:
            # 并不需要记住字符a的ASCⅡ码,只要求出相对数值就可以了
            record[ord(i) - ord("a")] += 1    # ord(i)为求字符i的Unicode编码
        for i in t:
            record[ord(i) - ord("a")] -= 1
        for i in range(26):
            if record[i] != 0:
                # record数组如果有不为0的元素,则一定有一个多了或少了字母
                return False
        return True

题目链接:349. 两个数组的交集 - 力扣(LeetCode)

思路:

注意题目特别要求输出结果中的每个元素都是唯一的,那么就要求输出的结果是去重的,同时可以不考虑输出结果的顺序,这道题暴力解法时间复杂度为O(n²)

在C++中可以使用set结构来解决问题,在python中则是使用字典。

首先定义字典val_dict来存放nums1中出现的数值,并完成了去重处理,再判断是否有既在val_dict中出现又在nums2中出现的数值,存入数组ans中,最后返回ans

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        val_dict = {}
        ans = []
        for num in nums1:
            val_dict[num] = 1

        for num in nums2:
            if num in val_dict.keys() and val_dict[num] == 1:
                ans.append(num)
                val_dict[num] = 0
        return ans

题目链接:202. 快乐数 - 力扣(LeetCode)

思路:

题目中提到可能会无限循环,也就是求和过程中中间值eql会重复出现,遇到这种需要快速判断一个元素是否在集合里,就要考虑哈希法,判断eql是否重复出现,若是则返回false,不是则继续计算。

# 版本一
class Solution:
    def isHappy(self, n: int) -> bool:
        eql = 0
        val = {}

        while(n > 1):  
            if n in val.keys() and val[n] == 1:    # 如果等于中间结果说明陷入循环返回false
                return False
            val[n] = 1                             # 记录中间结果
            eql = (n % 10) ** 2
            while(n > 1):
                n = n // 10
                eql += (n % 10) ** 2
            n = eql

        return True
    
# 版本二__代码随想录
class Solution:
    def isHappy(self, n: int) -> bool:
        def calculate_happy(num):
            sum_ = 0
            
            # 从个位开始依次取,平方求和
            while num:
                sum_ += (num % 10) ** 2
                num = num // 10
            return sum_

        # 记录中间结果
        record = set()

        while True:
            n = calculate_happy(n)
            if n == 1:
                return True
            
            # 如果中间结果重复出现,说明陷入死循环了,该数不是快乐数
            if n in record:
                return False
            else:
                record.add(n)

题目链接:1. 两数之和 - 力扣(LeetCode)

思路:

首先暴力解法为两层for循环,时间复杂度为O(n²)

在python中哈希法的使用则可以利用字典来完成,利用key来保存数值,key的值来保存索引号

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 []

总结:

在遇到类似数值需要快速查找时,可以考虑使用哈希法,代替循环法使用可以节省很多时间

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值