两数之和-java

文章讲述了如何解决LeetCode上的两数之和问题,首先介绍了暴力枚举法的时间复杂度为O(n^2),然后引入哈希表优化算法,将时间复杂度降低至O(n),并详细解释了哈希表长度设置为nums.length-1的原因以提高效率。
摘要由CSDN通过智能技术生成

题目描述:(力扣题库1)

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

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

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

class Solution {
    public int[] twoSum(int[] nums, int 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
  • 只会存在一个有效答案
法一: 暴力枚举法

思路分析: 将nums数组内的每个数,两两相加,直到两个两个元素的和等于target

具体步骤如下:

  1. 外层循环遍历数组中的每一个元素,假设当前元素的索引为i。
  2. 内层循环从当前元素的下一个位置开始遍历数组,假设内层循环的索引为j。
  3. 在内层循环中,检查当前元素nums[i]与内层循环中的元素nums[j]的和是否等于目标值target。
  4. 如果找到两个元素的和等于目标值,则返回它们的索引[i, j]。
  5. 如果内层循环结束后仍未找到符合条件的元素对,继续外层循环,直到找到答案或遍历完整个数组。

这种方法的时间复杂度为O(n^2),因为需要遍历所有可能的组合。虽然这种方法简单直观,但对于较大规模的数组来说,效率较低。

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

进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?

法二: 哈希表查数:

思路分析: 假设,存在一个表格key,用来记录target 与 数组nums内的元素的差值 target  -  nums[i](并记录nums数组元素的下标); 遍历数组nums, 当nums[j] 与 key表中的某个值(target - nums[i])相等,即nums[j] + nums[i] = target,返回i, j. 时间复杂度为o(n).其中, 关键点为: 判断nums数组内的元素是否与key表内的值相等,以及确定下标i, 如果把nums[i]与key表中的每个值比较,不就又变成了暴力枚举?所以,我们可以利用哈希表,利用其特性快速判断nums数组内的元素是否与key表内的值相等,以及确定下标i.

那么,接下来是代码演示:

第一步,创建一个哈希表,存放nums数组内的元素以及下标i

Map<Integer, Integer> hasMap = new HashMap<>(nums.length - 1);
       

第二步, 将nums第一个元素,及下标导入哈希表,遍历nums数组,如果nums[i]与哈希表中的一个值相等,返回两个下标,如果不是,则将nums[i] 与 i导入哈希表

hasMap.put(nums[0], 0);
        for (int i = 1; i < nums.length; i++) {
            if(hasMap.containsKey(target - nums[i])){
                return new int[] {i, hasMap.get(target - nums[i])};
            }
            hasMap.put(nums[i], i);
        }

        return null; // 如果一直没有匹配的,返回null

这里,我并没有一开始直接把哈希表创建出来,而是在遍历nums数组的过程中创建的

代码到这,已经结束了,以下是该方法的完整代码(已在leetcode通过)

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> hasMap = new HashMap<>(nums.length - 1);
        hasMap.put(nums[0], 0);

        for (int i = 1; i < nums.length; i++) {
            if(hasMap.containsKey(target - nums[i])){
                return new int[] {i, hasMap.get(target - nums[i])};
            }
            hasMap.put(nums[i], i);
        }

        return null;
    
    }
}

 最后,回到当初创建哈希表时提出的疑问

在这段代码中,哈希表的长度被初始化为`nums.length - 1`的原因是为了避免哈希表在存储元素时发生扩容,从而提高代码执行的效率。在Java中,`HashMap`的默认负载因子(load factor)为0.75,这意味着当哈希表中的元素数量达到容量的75%时,哈希表会自动扩容,将容量翻倍。

在这段代码中,我们知道`hasMap`哈希表的目的是为了存储数组`nums`中的元素,以便快速查找与当前元素配对的另一个元素。由于在for循环中,我们从`nums`数组的第一个元素开始遍历,所以在初始化哈希表时,我们只需要考虑从第二个元素开始的元素。

如果我们将哈希表的长度设置为`nums.length`,那么哈希表的初始容量会是`nums.length`,这会导致在向哈希表中添加元素时,可能会触发多次的哈希表扩容操作,从而降低代码的执行效率。

因此,为了避免不必要的哈希表扩容操作,我们选择将哈希表的长度初始化为`nums.length - 1`,这样可以在一定程度上减少哈希表的扩容次数,提高代码的执行效率。

以上就是本篇文章的全部内容,博主也是最近才接触哈希表这一类型,我将自己当时遇到的疑惑(例如: 为什么哈希表长度为 nums.length - 1, 而不是nums.length),以及我想到的问题,以讲解的方式,在文章内回答了. 所以,讲解部分的内容可能有点繁琐,如诺有不妥之处,敬请谅解.

  • 34
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值