代码随想录学习Day 5

1.哈希表理论基础

哈希表是根据关键码的值而直接进行访问的数据结构。数组就是一张哈希表。哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素。

一般哈希表都是用来快速判断一个元素是否出现集合里。

哈希函数则是通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,从而映射到哈希表的索引数字。如果hashCode得到的数值大于哈希表的大小,则会进行取模操作。

哈希碰撞是指不同元素映射到了哈希表中同一个索引下标的位置,一般有两种解决方法:拉链法和线性探测法。

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

线性探测法(右图):必须保证tablesize>datasize,因为需要利用哈希表中的空位来解决碰撞问题。如果冲突的位置已经有了元素,那么就向下找一个空位放置碰撞的另一个元素。

常用的三种哈希结构包括:数组、集合(set)、映射(map)

数据小用数组,数据大用set,数据比较散用map

当遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法

但是哈希法也是牺牲了空间换取了时间,因为要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。

242.有效的字母异位词

题目链接

讲解链接

由于题目规定输入的字符串均由小写字母组成,所以可以直接定义一个长度为26的全零数组作为哈希表,用来记录字符串中字符出现的次数。因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下标0,相应的字符z映射为下标25。在遍历字符串时,只需将 s[i] - ‘a’ 所在的元素做+1操作即可,并不需要记住字符a的ASCII,只须求出一个相对数值。

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        hash = [0] * 26  # 创建长度为26的全零数组作为哈希表
        for i in s:
            hash[ord(i) - ord('a')] += 1  # 字符串中每一个字母的ascii码减去‘a’的asdii码,哈希表中对应位置+1
        for i in t:
            hash[ord(i) - ord('a')] -= 1  # 第二个字符串同理,但是要在哈希表中对应位置-1,若有与上一个字符串出现相同次数的字母,则哈希表中该位置抵消变为0
        for i in hash:
            if i != 0:  # 最后遍历哈希表,若某位不为零则说明两个字符串未能完全抵消
                return False
        return True

其实最容易想到的办法就是直接把两个字符串排序,然后判断是否相等即可 。

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        s = sorted(s)
        t = sorted(t)
        if s == t:
            return True
        else:
            return False

 python中还有一种方法可以直接使用collection库中的Counter函数来对字符串中每个字母出现的次数进行统计,然后返回统计的结果。这样的话只需要对比两个字符串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

349.两个数组的交集 

题目链接

讲解链接

输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的,同时可以不考虑输出结果的顺序。当题目限制了数值的大小时,可以优先考虑用数组来做哈希。但如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。本题中采用数组和set的方法都可以。

首先能想到的最简单解法就是调用python中的intersection函数,直接返回集合的交集。

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        s1 = set(nums1)
        s2 = set(nums2)
        return list(set.intersection(s1, s2))  # 最后返回值要改为list型

另一种方法是结合使用字典和集合,用字典来存储元素,然后用集合存储结果并实现去重操作:

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  # 取字典中key=num位置的value,初始为零,num每出现一次,该位置的value+1
        # 使用集合存储结果
        res = set()
        for num in nums2:
            if num in table:
                res.add(num)
                del table[num]        
        return list(res)

由于力扣上给出了本题数组长度小于等于1000,所以也可以采用数组来做哈希:

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        arr1 = [0] * 1010  # 多开一点防止溢出
        arr2 = [0] * 1010
        result = []
        for i in nums1:  # 遍历数组,记录每个数字出现的次数,保存到arr中对应下标位置
            arr1[i] += 1
        for i in nums2:
            arr2[i] += 1
        for k in range(1010):
            if arr1[k] * arr2[k] != 0:  # 若乘积不为零,则说明两边均不是0,该元素属于交集
                result.append(k)
        return result

202.快乐数

题目链接

本题需要先编写一个求数字各位平方和的求和函数,然后反复判断求和得到的sum是否为1,如果是1,则返回True。如果不是1,那么就需要对sum再次求各位平方和。

重点:主要考虑的就是题目中的无限循环问题。什么情况下会出现无限循环,一定是求出的sum值重复出现的情况下会导致无限循环,如果sum始终没有重复出现,那么最后一定可以算出1。

class Solution:
    def sum(self, n: int) -> int:  # 求各位平方和的函数
        sum = 0
        while n:
            sum += (n % 10) * (n % 10)
            n //= 10
        return sum

    def isHappy(self, n: int) -> bool:
        s = set()  # 创建集合用于保存每次得到的sum
        result = self.sum(n)
        while 1:
            result = self.sum(result)
            if result == 1:
                return True
            elif result in s:  # 如果sum重复出现过,则说明不是快乐数
                return False
            else:
                s.add(result)  # 若sum此前未出现过,则将其加入集合
                continue

1.两数之和

题目链接

讲解链接

重点:

①为什么会想到用哈希表:

当需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,第一时间想到哈希法

②哈希表为什么用map:

本题不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,所以要使用map。

③本题map是用来存什么的:

map用来存放访问过的元素,因为遍历数组的时候,需要记录之前遍历过哪些元素和对应的下标,这样才能找到与当前元素相匹配的(也就是相加等于target)

④map中的key和value用来存什么的:

判断元素是否出现,元素作为key,所以数组中的元素作为key,有key对应的就是value,value存下标。所以 map中的存储结构为 {key:数据元素,value:数组元素对应的下标}。

 在python中使用dict()来实现map的存储:

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        records = dict()  # 创建字典用来保存访问过的元素及其对应下标
        for key, value in enumerate(nums):  # 遍历数组
            if target - value in records:  # 如果差值在字典中
                return [records[target - value], key]  # 返回当前遍历元素的下标以及与其和为目标值的元素下标
            records[value] = 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]:
        for i in range(len(nums)):
            for j in range(i+1, len(nums)):
                if nums[i] + nums[j] == target:
                    return [i,j]

 

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值