【LeetCode_01】Two Sum_Java&python_Dic&Hash解法

本文主要讲解了LeetCode_01,Two Sum的几种解法。博主是直接暴力求解的,在参考了比较好的解法之后,作了一定的讲解,分享于此。讲解部分是我自己的理解,方法一到方法四,如果你点开了,请耐心读读,欢迎交流。
知识尚浅可能会有遗漏或错误之处,欢迎指正。
20.10.27,10:00-16:00——初稿
20.10.28,9:00-11:30——完善算法理解,添加评价,修改标题
用的时间比较多,因为是刚开始刷leet,所以反复推敲了下,也方便自己总结套路(其实效率有点低了,不过我相信之后会慢慢快起来的)。
——梦开始的地方

题目描述

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:

给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

方法一、暴力求解

Java解法

算法思路:以第一个数为基准开始遍历,找到数列中剩余数中与其作和等于target的数,找到则存储下标到事先定义好的arr整型数组中,否则继续查找。最后返回数组。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] arr = new int[2];
        for(int i=0;i<nums.length;i++){
            for(int j = i+1;j<nums.length;j++){
                if(nums[j] == target - nums[i]){
                    arr[0] = i;
                    arr[1] = j;
                }
            }
        }
    return arr;
    }
}

在这里插入图片描述

评价

因为是先后以第一个、第二个…为基准向后查找,所以很轻松的覆盖的所有的情况。很常见的解题方案,时间复杂度为O(n^2),最坏情况下数组中任意两个数都要被匹配一次。从执行结果来看,执行用时88ms,空间消耗低,效果还不错。

方法二、用 Python 中 list 的相关函数求解

Python解法

代码写的有些繁琐,这里提前讲解代码中涉及到的知识。
Python count() 方法用于统计字符串里某个字符出现的次数。可选参数为在字符串搜索的开始与结束位置。
&,位运算,同真(1)为真(1)否则为假(0)
在这里插入图片描述
关于index的用法,发现网上很多参考都没有讲清楚,可以戳【Python】index()用法
Python continue 语句跳出本次循环,而break跳出整个循环。
continue 语句用来告诉Python跳过当前循环的剩余语句,然后继续进行下一轮循环。

算法思路:判断原数组nums中是否可以找到target与遍历数(初始从第一个数开始)的作差。如果找到了基准数本身(就比如[4,5,11,12,3] t = 8 ,nums.count(8-4) == 1&(8-4 == 4)说明只有本身能满足,后续的数都不能与其作和等于target),则从下一个数5开始循环。

否则遇到以下的情况则直接则从基准数后面的序列中查找
也就是位相与的相反的三种情况
①遇到nums.count(num2)等于二的情况,比如4,5,7,4 target = 8
②遇到num2 != nums[i],比如4,5,7,11 target = 9
③不存在两种都成立
所以就是以上这两种情况了。

class Solution(object):
    def twoSum(self,nums, target):
        lens = len(nums)
        j=-1
        for i in range(lens):
            num2 = target - nums[i]
            if (num2) in nums:#nums中找到target与遍历数的差
                if (nums.count(num2) == 1)&(num2 == nums[i]):#如果num2=num1,且nums中只出现了一次,说明找到了num1本身。就比如[4,5,11,12] t = 8 ,nums.count(8-4) == 1&(8-4 == 4),说明找到了本身4
                    continue
                else:
                    j = nums.index(num2,i+1) #index(x,i+1)是从num1后的序列后找num2                
                    break
        return [i,j]

在这里插入图片描述

评价

最坏情况是从num1(也就是num[i])后面的序列找num2时,4,5,7,4 target=8这种情况,也就是后面序列都要索引一遍,所以时间复杂度依然是O(n^2)。算法耗时较大。耗时较大的原因我觉得是每次找num2时是从整个序列nums里面找的,这里还包括了num1。所以考虑优化一下。

优化

仔细想想,num2 的查找并不需要每次从 nums 查找一遍,只需要从 num1 位置之前或之后查找即可。但为了方便 index 这里选择从 num1 位置之前查找。

class Solution(object):
    def twoSum(self,nums, target):
        lens = len(nums)
        j=-1
        for i in range(1,lens):
            temp = nums[:i]
            if (target - nums[i]) in temp:#num1之前中找target与遍历数的差
                j = temp.index(target - nums[i])
                break
        return [j,i]

在这里插入图片描述

评价

可以看到执行用时降低了近一半。我们优化的同时一定要考虑重复数的情况,考虑这三个例子
①[3,3] t = 6
②[3,5,7,3] t = 6
③[5,3,7,3] t = 6
都可以完美通过,这种优化方案的优点在于,使用temp[:i]记录了i之前的数,没有打乱数的序号。如果是向后查找,对于nums = [3,3]的情况,使用temp[i:],temp=[3],本身nums中序号为1的3到了temp中序号变成了0,这也是下面的疑问的来源。关于向后查找有没有比较好的思路,欢迎大佬评论~

疑问

这个优化方案是从 num1 位置之前查找。那么为什么不能从num1位置之后查找呢?

class Solution(object):
    def twoSum(self,nums, target):
        lens = len(nums)
        j=-1
        for i in range(0,lens):
            temp = nums[i+1:lens]
            if (target - nums[i]) in temp:
                j = nums.index(target - nums[i])                
                break
            else:
                continue
        return [i,j]

但是对于这种[3,3] target= 6,并不好使,因为从temp中索引到的3在sums中的索引为1,我们是希望它的索引是2的。所以这里不得不考虑重复的情况。思去想来,好像又回到了解放前。
在这里插入图片描述

另外,我们发现前面的方法在找num2的时候都没有把序号和数值绑定在一起,所以我们考虑用到python enumerate()这种方法在查找的时候将序号和数值绑定在一起。

方法三、用字典模拟哈希求解

算法思路:通过哈希来求解,这里通过字典来模拟哈希查询的过程。类似方法二,字典记录了 num1 和 num2 的值和位置,而省了再查找 num2 索引的步骤。

Python enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。

Python 字典(Dictionary) get() 函数返回指定键的值。如果键不在字典中返回默认值 None 或者设置的默认值。这里是返回None。

Python解法

注:为了简化代码,num2在本代码中为cp,num1就是遍历的数

class Solution(object):
    def twoSum(self,nums, target):
        dct={}
        for i,n in enumerate(nums):
            dct[n] = i
        for i,n in enumerate(nums):
        	cp = target - n
            j = dct.get(cp)
            if j is not None and i!=j:
                return [i,j]

在这里插入图片描述
比如2,5,7,11 t=9这个例子在这里插入图片描述

评价

耗时28ms,还可以的。因为是通过字典来仿哈希查询,查字典用的复杂度较大,所以可以优化为直接用哈希表。

方法四、哈希表

算法思路:方法一二的时间复杂度较高的原因是寻找num2 (target-num1)的时间复杂度过高。因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。同时这个方法也是方法三的优化。

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

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

Python解法

注:为了简化代码,num2在本代码中为cp,num1就是遍历的数

class Solution(object):
     def twoSum(self,nums, target):
        dct = {}
        for i, n in enumerate(nums):
            cp = target - n
            if cp in dct:
                return [dct[cp], i]
            else:
                dct[n] = i

在这里插入图片描述
比如2,5,7,11 t=9这个例子
在这里插入图片描述
也可以参考官方给的代码
python

class Solution:
    def twoSum(self, nums,target):
        hashtable = dict()
        for i, num in enumerate(nums):
            if target - num in hashtable:
                return [hashtable[target - num], i]
            hashtable[nums[i]] = i
        return []

java

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> hashtable = new HashMap<Integer,Integer>();
        for(int i = 0;i<nums.length;i++){
            if(hashtable.containsKey(target - nums[i])){
                return new int[]{hashtable.get(target - nums[i]),i};
            }
            hashtable.put(nums[i],i);
        }
        return new int[0];
    }
}

评价

使用哈希表,可以将寻找 num2 的时间复杂度降低到从 O(N)降低到 O(1)。耗时16ms,推荐此方法。

总结

本文主要讲解了LeetCode_01,Two Sum的几种解法。
比较推荐第三种优化解法,比较快一些,算法也比较好理解。

刚开始刷题,通常只能想到暴力求解。因为做的太少了。刷题也是为了有更好的解法,节省时间和空间。见的多自然就会用更好的方法。

参考

Python enumerate() 函数
python object类
读懂python中的self
Python 字典(Dictionary) get()方法

博主比较小白,但是热爱分享。一直感觉自己写代码的能力,算法能力都不太行,所以最近开始刷LeetCode,一方面记录方便自己学习,另一方面给需要的同伴参考。虽然失败并不可怕,但是也希望同伴们少踩一些坑。分析算法挺费劲的,留个赞或评论支持一下博主吧!同时我也非常希望写出更通俗易懂的文章,知识尚浅,如有遗漏或错误,欢迎指正~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

疯狂java杰尼龟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值