【菜鸡刷题-牛客网NC61】两数之和 || 哈希表 || python

本文详细解析了在整数数组中寻找两数之和为目标值的问题,提供了两种高效的算法实现方案,包括遍历建立哈希表及边建立边查找的优化方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


@ author = YHR | 原创不易,转载请注明来源!



题目描述

给出一个整数数组,请在数组中找出两个加起来等于目标值的数
给出的函数twoSum 需要返回这两个数字的下标(index1,index2),需要满足 index1 小于index2。注意:下标是从1开始的。
假设给出的数组中只存在唯一解,例如: 给出的数组为 {20, 70, 110, 150},目标值为90,输出 index1=1, index2=2


2021/1/26 马一下
明天写了 要搬家啦!


2021/1/27
讲讲我的两个方法把,方法2是方法1的精进版本哦~

方法一: 遍历两次原数组,全部建立完hash table, key=number, value=[index(s)],再开始处理

思路 1 讲解:

  • 一、第一遍,遍历原数组,生成 完整的 hash table: 示例如下图
  1. hash table 初始为空,分为key 与 value, 开始遍历 data 数组
    在这里插入图片描述
  2. 遍历 data 数组,index=0,数字为20, 此时hash Table的 key 里没有 “20” 这个键,于是把他插入hash table --> key=20, value列为列表形式! 插入index = 0
    在这里插入图片描述
  3. 遍历 data 数组,index=1,数字为30, 此时hash Table的 key 里没有 “30” 这个键,于是把key=30, 插入index = 1 (value列为列表形式!
    在这里插入图片描述
  4. 遍历 data 数组,index=2,数字为20, 此时hash Table的 key 里已经有 “20” 这个键,也就是发生了 碰撞 (collision) ,于是只需要把当前的 index=2 新添入 key=20对应的value列表 中。
    在这里插入图片描述
  • 二、第二次,遍历原数组,对 target - 当前数 = remainder ,即当前数的 , 进行判断。
			remainder = target - numbers[_]

情况1 : remainder == 当前数number[_] , 即值相同

那么只需判定 remainder 是否是 numbers[ _ ]自己:

若 remainder 是numbers[_]自己,则跳过;

如果remainder是本数组里另外一个和numbers[ _ ]值相同的数则通过,即 remainder 不是 numbers[ _ ] 自己, 且此时 remainder的 index 一定在numbers[ _ ] 的index 的后面!

具体如何判断呢? 刚才 第一步建立好的, 完整的 hashTable 这不就帮上忙了!

hash table 里面的 value是一个列表,当不同index的数如果值相同时,
也即发生碰撞的话,就把对应的index都存进 key 对应的 value 来。
  • 此时,如果 remainder == 当前数numbers[ _ ] , 假设他俩值是 key_i, 那么在 hash table 里:
  1. 如果 key_i 对应的value 这个列表,长度只有1 (只有一个数), 那么 remainder就是 numbers[ _ ] 本身,跳过!
  2. 但如果 key_i 对应的value 这个列表,长度>=2 (至少2个数), 即 remainder 不是 numbers[ _ ] 自己, 那么 remainder就是 value里_的后一个数字, 也就是: numbers[ _ ]的下标 _ 在 value下标为 value.index( _ ), 那么 remainder的下标就是 value[value.index( _ )+1]
            if remainder == numbers[_]:
                value_list = hashMap[remainder]
                if len(value_list) == 1:
                    '''remainder 是numbers[_]自己, 不成立, 跳过'''
                    pass
                else:
                    remainder_index = value_list[value_list.index(_) + 1]
                    '''remainder是 numbers[_]后面的 数 '''
                    ''' +1 是因为 返回的index 要求从1开始'''
                    return [_ + 1, remainder_index + 1]

例子:

  • target = 40, 当前数=20,index=0

在这里插入图片描述

  1. remainder= 40 - 20= 20 --> remainder == 当前数numbers[ _ ]
  2. 查看hash Table 里 key =20 对应的value的长度,发现 len(value) = 2 > 1,说明有俩数,那么remainder的index就是 value_list[value_list.index(0) = 2,最后返回 [0+1, 2+1]


情况2 : remainder != 当前数number[_] and remainder in hashMap, 值不同 但在 hashMapL可以找到remainder

此时说明直接找到了数组里,当前数的补数啦~
			elif remainder != numbers[_] and remainder in hashMap:
                # remainder 在hashMap里
                value_list = hashMap[remainder]  # a list of remain index
                ''' +1 是因为 返回的index 要求从1开始'''
                return [_ + 1, value_list[0] + 1]

例子:

  • target = 50, 当前数=20,index=0

在这里插入图片描述

  1. remainder= 50 - 20= 30 --> remainder != 当前数numbers[ _ ] ,且 remainder 在hash Map里。
  2. 直接得到 remainder 的index = hashMap[30] [0]= 1
  3. 返回 [0+1, 1+1]


情况3 : remainder != 当前数number[_] and remainder not in hashMap, 值不同 , hashMapL也找不到remainder

此时说明:当前数组里 **不存在** 当前数的补数, 开始遍历下一个数~

思路一代码

异常情况处理:至少保证数组里有两个数

class Solution:
    def twoSum(self, numbers, target):
        # write code here
        if len(numbers) > 1:
            return self.getHash(numbers, target)
        else:
            return []
        pass

处理代码:


	def getHash(self, numbers, target):
        hashMap = {}

        for _ in range(len(numbers)):
            if numbers[_] in hashMap:
                hashMap[numbers[_]].append(_)
            else:
                hashMap[numbers[_]] = [_]

        for _ in range(len(numbers)):
            remainder = target - numbers[_]

            if remainder == numbers[_]:
                value_list = hashMap[remainder]
                if len(value_list) == 1:
                    '''remainder 是numbers[_]自己, 不成立, 跳过'''
                    pass
                else:
                    remainder_index = value_list[value_list.index(_) + 1]
                    '''remainder是 numbers[_]后面的 数 '''
                    return [_ + 1, remainder_index + 1]
            elif remainder != numbers[_] and remainder in hashMap:
                # remainder 在hashMap里
                value_list = hashMap[remainder]  # a list of remain index
                return [_ + 1, value_list[0] + 1]
        return []

更高效的方法二: 遍历1次原数组,边建立hashMap,边查找~

刚才的方法是1. 需要遍历两遍原数组,然后 2.全部建立hashMap以后,才开始 3. 查找。

谁说,一定要全部建立好hashMap以后才能开始查找呢?

废话不多说,先放代码!

思路二代码

大家可以观察一下,以下的代码,为啥这么快就搞定咯!
异常情况处理:至少保证数组里有两个数

class Solution:
    def twoSum(self, numbers, target):
        # write code here
        if len(numbers) > 1:
            return self.getHash_traverseOnce(numbers, target)
        else:
            return []
        pass

正式代码

	def getHash_traverseOnce(self, numbers, target):
        hashMap = {}

        for _ in range(len(numbers)):
            remainder = target - numbers[_]
            if remainder in hashMap:
                '''+1 是因为返回的index 要求从1开始'''
                return [hashMap[remainder]+1, _+1]
            else:
                hashMap[numbers[_]] =  _ 
        return []

思路 2 讲解:

这个思路有俩好处:

1. 只需遍历一次原数组
2. 没必要等到 建立完整的hashMap再查找,我们可以边查边找,找到了就不继续建立咯!

思路2最大的不同,就是:

  1. 我们每次 插入一个新的数 进入hashMap, 我们在插入之前,就看看他的补数是不是已经在数组里了?
  2. 如果在的话,直接返回结果。
  3. 如果不在的话,就正常插入,毕竟,咱们不能排除这个数可能是,后来还没插入数的补数的可能性。
  4. 此外,不同于思路1, 思路2 的value 不需要是列表,仅仅存一个数值就可以啦!

图解, target=40 为例!

初始化:
在这里插入图片描述

  1. 准备插入 key= 20 & index=0, remainder = 40 - 20 = 20,而 20此时并不在hash map里,正常插入 key=20, index = 0

    不同于思路1, 思路2 的value 不需要是列表,仅仅存一个数值就可以啦!

在这里插入图片描述
2. 准备插入 key= 30 & index=1, remainder = 40 - 30 =10, 10 并不在hash map里,正常插入 key=10, index =1

在这里插入图片描述
3. 准备插入 key= 20 & index=2, remainder = 40 - 20 =20, 而 20此时已经有一个存在在hash map里,说明找到了 remainder = 20, index=0!

那现在就停止插入key= 20 & index=2,因为已经找到啦他的补数 remainder = 20, index=0 啦~ 可以返回啦

在这里插入图片描述

return [ remainder的index=0+1, 当前数的index=2+1]
(+1 是因为要求返回的index 从1开始)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值