橘子刷题第二题之两数之和

题目:本题出自力扣第一题,两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案
进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?

解法1、暴力遍历

算法解题第一步,暴力穷举就完事了。我们没技术人都是这么做的。但是考虑到万一存在比我还没技术的人,所以我这里还是写一写暴力的一个过程。
首先一个数组(又尼玛要画图,淦)
在这里插入图片描述
首先这是个数组,总共八个数字,我们假定目标数字是target = 10 。暴力穷举的方式就是遍历循环,一层不够来两层,总有够的时候,我丢。
我们从下标0开始,然后开始循环和后面的做求和然后比较。这样比一遍没找到,那就再从下标1开始,再循环一遍,这样往下走就完了,你也应该看出来这是个两层循环了吧。既然战策已定,那就开整吧。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        // 初始化返回结果
        int [] result = new int[2];
        // 外层循环,把控第一个固定数字
        for(int i = 0;i<nums.length;i++){
            // 内层循环不断往后匹配
            for(int j = i + 1;j<nums.length;j++){
                // 循环的中间计算结果
                int temp = nums[i] + nums[j];
                // 结果匹配判断
                if(temp == target){
                    // 返回数组设置
                    result[0] = i;
                    result[1] = j;
                    return result;
                }
            }
        }
        // 不成功匹配返回null
        return null;
    }
}

在这里插入图片描述
计算通过了,但是你看到了击败了四分之一的用户,这就是拉了吧。这个计算复杂度是n的平方了,人家题目里面也有要求,让你想想弄一个小于n^2的复杂度,我滴妈,影响仕途了。所以我们需要考虑一个优化手段。既然是优化,那我们就得想想目前哪里拉了,然后再看在哪优化。

解法2、优化一波

2.1、拉胯点

我们先来分析一下这个我们上面到底拉胯在哪里。我们想想,我们的目标是啥,我们是在数组中找两个数等于目标,我们上面找的时候每次都是一波循环,这个我瞬间想到了那个冒泡排序,实在太尼玛像了,冒泡就是多次遍历,很多值都被遍历了很多遍,冒泡能用map存历史值(类似字典),那我们要是我们每次遍历一个值能存起来,以后用的时候就比较一下,这不能省不少事吗。那还说啥map走起。

这里我打个比例,不想看的可以跳过。
我们这个题其实像是在一堆男的女的里面匹配对象。前面暴力配对就是先指定一个妹子,然后下面一个个找,看看
合适就结婚。不行就下一个再来一轮,这样下去,每个男的女的,每一轮都比较。这尼玛就累,什么年代了。
于是介绍人想了一个方法,我每次处理完一个人,就把他记下来,记一个二元组<当前人条件,身份证>,匹配到第
一个先看看,前面记下合适的没,因为第一个,所以前面没有,那你就记下来二元组,你先回家吧,后面我有了合适
的我就通知你,反正我记下你身份证号了。
后面一个个的匹配,每次匹配到一个就看看小本本,小本本上有合适的直接联系身份证找到结婚。
不合适就打发回家,这样最后匹配到了就行了,最后也没匹配到那就说明这一屋子男男女女都尼玛不合适,玩完。

2.2、继续优化结构

既然我们想着用map了,那map有啥呢,k-v。
k存啥呢?因为我们是要保存遍历的过程的历史值,使用到map的O(1)获取的特性,那就存数组的值为k。
v存啥呢?因为我们最后返回的结果是数组值遍历的下标,我们要是通过k比较出值了,直接拿v就行,这个v很自然就是下标了。
类型都是整形数。
Map<Integer,Integer> map = new HashMap<>();

2.3、show me your code

public int[] twoSum(int[] nums, int target) {
        // map的声明,一个字典的存在
        Map<Integer,Integer> map = new HashMap<>();
        // 初始化返回结果
        int [] result = new int[2];
        // 循环遍历数组,终归还是要遍历一次的
        for(int i = 0;i<nums.length;i++){
            // 计算当前数组值所需要达成目标值的另一半
            int temp = target - nums[i];
            // 先看map中有没有过你的另一半,要是有
            if(map.containsKey(temp)){
                // 直接赋值返回
                result[0] = i;
                result[1] = map.get(temp);
                return result;
            }else{
                // 要是没有,就把当前这个放进去,等待下一次遍历来匹配
                map.put(nums[i],i);
            }
        }
        // 最终没找到,寄了
        return null;
    }
}

在这里插入图片描述
看到运行结果优化了好多,没问题。芜湖,起飞。此时你的时间复杂度就是线性的O(n)了。

3、总结

我们看到其实像这种多次遍历的操作我们一般优化就是存一个字典记录,后面用的时候直接拿。这个直接拿就体现出一个数据结构就是map,因为他O(1)映射。所以直接用map就行,但是也引入了一个新的map结构,这就是一个空间换时间的体现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值