算法理论——哈希表(附多例题)

一、什么是哈希表

哈希表也叫散列表,哈希表是一种数据结构,它提供了快速的插入操作和查找操作,无论哈希表总中有多少条数据,插入和查找的时间复杂度都是为O(1),因为哈希表的查找速度非常快,所以在很多程序中都有使用哈希表,例如拼音检查器。

哈希表也有自己的缺点,哈希表是基于数组的,我们知道数组创建后扩容成本比较高,所以当哈希表被填满时,性能下降的比较严重。

哈希表采用的是一种转换思想,其中一个中要的概念是如何将「键」或者「关键字」转换成数组下标?在哈希表中,这个过程有哈希函数来完成,但是并不是每个「键」或者「关键字」都需要通过哈希函数来将其转换成数组下标,有些「键」或者「关键字」可以直接作为数组的下标。我们先来通过一个例子来理解这句话。

我们上学的时候,大家都会有一个学号「1-n号」中的一个号码,如果我们用哈希表来存放班级里面学生信息的话,我们利用学号作为「键」或者「关键字」,这个「键」或者「关键字」就可以直接作为数据的下标,不需要通过哈希函数进行转化。如果我们需要安装学生姓名作为「键」或者「关键字」,这时候我们就需要哈希函数来帮我们转换成数组的下标。

总之,哈希表是一个便于记录和高效查询的数据结构,插入和查找的时间复杂度都是为O(1), 使用时可以粗略的认为哈希表记录的键是问题的条件,值是问题的解,通过不断插入和搜索最终获得所需答案。

二、哈希表解题实例

1. 两数之和

题目描述

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]

思路

暴力思路是最容易想到的方法是枚举数组中的每一个数 x,寻找数组中是否存在 target - x。
当我们使用遍历整个数组的方式寻找 target - x 时,需要注意到每一个位于 x 之前的元素都已经和 x 匹配过,因此不需要再进行匹配。而每一个元素不能被使用两次,所以我们只需要在 x 后面的元素中寻找 target - x。
但是这种思路时间复杂度太高,我们使用哈希表法解决本题:

注意到暴力方法的时间复杂度较高的原因是寻找 target - x 的时间复杂度过高。因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。

使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O(N)降低到 O(1)。

这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。

代码

# 两数之和
def twoSum(nums, target):
    h = dict()
    for i in range(len(nums)):
        if target - nums[i] in h:
            return [i, h[target - nums[i]]]
        else:
            h[nums[i]] = i

2. 整数转罗马字母

题目描述:

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给你一个整数,将其转为罗马数字。

思路

首先建立题目中已经给定的数值与罗马字符对应的哈希表,然后再此表中根据规律找到解。

我们用来确定罗马数字的规则是:对于罗马数字从左到右的每一位,选择尽可能大的符号值。对于 140,最大可以选择的符号值为 C=100。接下来,对于剩余的数字 40,最大可以选择的符号值为 XL=40。因此,140 的对应的罗马数字为C+XL=CXL。

代码

def intToRoman(num):
    # 构建相应哈希表
    h = dict()
    h[1] = 'I'
    h[5] = 'V'
    h[10] = 'X'
    h[50] = 'L'
    h[100] = 'C'
    h[1000] = 'M'
    h[500] = 'D'
    h[4] = 'IV'
    h[9] = 'IX'
    h[40] = 'XL'
    h[90] = 'XC'
    h[400] = 'CD'
    h[900] = 'CM'
    if num in h.keys():
        return h[num]
    # 答案
    res = ''
    # 模拟,在哈希表中找到最大值,num减去最大值,结果res加上哈希表中最大值对应的字符
    while num != 0:
        for i in sorted(h, reverse=True):
            if num >= i:
                num -= i
                res += h[i]
                break
    return res

3.二倍数对数组

题目描述

给定一个长度为偶数的整数数组 arr,只有对 arr 进行重组后可以满足 “对于每个 0 <= i < len(arr) / 2,都有 arr[2 * i + 1] = 2 * arr[2 * i]” 时,返回 true;否则,返回 false。

示例

输入:arr = [3,1,3,6]
输出:false

输入:arr = [4,-2,2,-4]
输出:true
解释:可以用 [-2,-4][2,4] 这两组组成 [-2,-4,2,4] 或是 [2,4,-2,-4]

思路

哈希表中按小到大排列记录出现个数,遍历哈希表,每个数都和它的两倍的个数或者它的一半的个数相等,当前个数小于0说明不够用,不能组合,如果不存在当前数的两倍或一半也不能组合,否则当前数的两倍或一半的数减去当前数的个数。

代码

# 二倍数对数组 哈希表的应用
def canReorderDoubled(arr):
    ant = Counter(arr)
    if ant[0] % 2 != 0:
        return False
    for i in sorted(ant, key=abs):
        if ant[2 * i] < ant[i]:
            return False
        else:
            ant[2 * i] -= ant[i]
            print(ant)
    return True
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hyk今天写算法了吗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值