leetcode_1_两数之和 刷题 打天下 不会写至少也要看懂吧

两数之和

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

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例

在这里插入图片描述

解决方案

1.暴力法

暴力法很简单。遍历每个元素 xx,并查找是否存在一个值与 target - x相等的目标元素。

代码

暴力啊,就是 干!

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        # 函数参数后的冒号:表示注释, ->表示 返回值的形式
        for i in range(len(nums)):
            cut = nums[i+1:]
            for j in range(len(cut)):
                if nums[i]+cut[j] == target:
                    return [i,i+j+1]

复杂度分析

时间复杂度:O(n2),对于每个元素,我们试图通过遍历数组的其余部分来寻找它所对应的目标元素,这将耗费 O(n)的时间。 因此时间复杂度为 O(n2)。
空间复杂度:O(1)。

在这里插入图片描述

2. 两遍哈希表

为了对运行时间复杂度进行优化,我们需要一种更有效的方法来检查数组中是否存在目标元素。如果存在,我们需要找出它的索引。保持数组中的每个元素与其索引相互对应的最好方法是什么?哈希表。
一个简单的实现使用了两次迭代。在第一次迭代中,我们将每个元素的值和它的索引添加到表中。然后,在第二次迭代中,我们将检查每个元素所对应的目标元素(target - nums[i])是否存在于表中。注意,该目标元素不能是 nums[i]本身!

什么是哈希表?

  • 关于 哈希表 ,参考 谈谈哈希表,下面是我的一些总结。

哈希表是数组的升级。在数组中,数组存储了一堆数据,但数据与它的位置之间没有什么关系。而哈希表在存储了数据之后,给数据与它的位置建立了一个确定的关系,这个确定的关系称为 哈希函数(Hash)

比如,在一个表格中,我知道一个数据,我想知道这个数据在表格的哪个地方(这个数据的位置)。如果是数组的话,我就只能拿着数据从第一个数据开始,一个个地比对,找出位置。那如果是哈希表呢,我就将这个数据代入哈希函数,得到的结果就是这个数据的位置,也就是说我只用一个步骤就找到了数据的位置。

总结一下,哈希函数就是 关键字key(数据)和存储位置Hash(key)之间的对应关系
哈希函数的定义很灵活,只要关键字通过哈希函数能够唯一确定一个哈希地址,而且改地址在数组的大小范围内就可以了。当然数组的长度也是我们自己设定的。
那么,有人会问,如果我想查找的关键字是文字或其它不是数字类型的呢?很简单,制定一个对应法则,将其它类型的数据转为数字,再利用哈希函数,就能得到位置了。如我们利用学生姓名作为关键字,我们首先要得到学生姓名小写拼音的ASCII码,再用ASCII码作为哈希函数的自变量,从而得到哈希地址。

那么又有人会问了,同姓的学生这么多,一个位置只能存一个数据啊,别的同姓的学生怎么办?
对于不同的关键字,可能得到同一个哈希地址,这种关键字我们称之为同义词,这种情况我们叫作冲突,一般情况下,冲突只能尽可能地减少并不能完全避免。
处理冲突问题的一个最常见的方法是链地址法。
所以哈希表就是:通过一个哈希函数Hash(key)和处理冲突方法将一个关键字映射到一个连续的地址集上,并以关键字在地址集上的像作为记录在表中的存储位置。这种表或者结构称为哈希表。通俗点讲就是利用关键字通过一个函数Hash(key)确定记录在数组中的位置。

那么哈希函数如何构造呢?构造方法(具体看链接):
(1)直接定址法
(2)除留余数法
(3)数字分析法
(4)平方取中法

处理冲突的方法:
(1)开放地址法
(2)再哈希法
(3)链地址法
(4)建立公共溢出区

理论上,哈希表访问某一个关键字的时间开销是O(1),直接通过哈希函数和关键字找到记录的存储地址,但是由于冲突的产生,使得哈希表的查找过程仍然需要关键字去和一个哈希表中的元素来进行比较。也就是说哈希表尽可能地减少我们要比较的次数,如果能够经过计算查询到我们要访问的记录那是最理想的情况。但是由于冲突导致我们还需要进行“比较”。

python中的字典和集合都是基于哈希表实现的,所以我们可以直接使用。

代码

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        # 函数参数后的冒号:表示注释, ->表示 返回值的形式
        dic = {}
        for i in range(len(nums)): #将每个元素的值和它的索引添加到哈希表(字典)中
            dic[nums[i]] = i
            
        for i in range(len(nums)):
            x = target - nums[i]
            if (x in dic) and (dic[x] != i):
                return [i,dic[x]]

复杂度分析

时间复杂度:O(n), 我们把包含有 n个元素的列表遍历两次。由于哈希表将查找时间缩短到 O(1),所以时间复杂度为 O(n)。
空间复杂度:O(n), 所需的额外空间取决于哈希表中存储的元素数量,该表中存储了n个元素。
在这里插入图片描述

3.一遍哈希表

事实证明,我们可以一次完成。在进行迭代并将元素插入到表中的同时,我们还会回过头来检查表中是否已经存在当前元素所对应的目标元素。如果它存在,那我们已经找到了对应解,并立即将其返回。

代码

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        # 函数参数后的冒号:表示注释, -> 返回值的形式
        dic={} # 字典
        for index,x in enumerate(nums):
        # enumerate函数将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在for循环中
            a = target-x
            if a in dic:
                return [dic[a],index]
            else:
                dic[x] = index

觉得单词难背的话,这样也是可以的,都一样

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        # 函数参数后的冒号:表示注释, -> 返回值的形式
        dic={} # 字典
        for i in range(len(nums)):
            a = target - nums[i]
            if a in dic:
                return [dic[a],i]
            else:
                dic[nums[i]] = i

复杂度分析

时间复杂度:O(n), 我们只遍历了包含有n个元素的列表一次。在表中进行的每次查找只花费O(1) 的时间。
空间复杂度:O(n), 所需的额外空间取决于哈希表中存储的元素数量,该表最多需要存储n个元素.
方法1:
在这里插入图片描述
方法2:
在这里插入图片描述

引用

参考资料:
1: https://zhuanlan.zhihu.com/p/30121142
2: https://leetcode-cn.com/problems/two-sum/solution/
坚持就是胜利!!!跟着锦堂打天下hhhhhhhhh
所以下午就写了一篇总结,emmmm

发布了28 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/xxx_gt/article/details/88090391