leetcode
leetcode 1 ~ 50
两数之和

两数之和

难度:

标签:

题目描述

给定一个整数数组 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) 的算法吗?

代码结果

运行时间: 28 ms, 内存: 16.1 MB


/*
 * 问题描述: 给定一个整数数组 nums 和一个目标值 target,找出数组中两个数的下标,使得它们的和等于 target。
 * 思路: 使用Java Stream API实现相同的逻辑,依然通过哈希表来快速查找。
 */
import java.util.HashMap;
import java.util.Map;
import java.util.stream.IntStream;
 
public class TwoSumStream {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>(); // 创建哈希表
        return IntStream.range(0, nums.length) // 使用IntStream来遍历数组下标
                .filter(i -> { // 过滤符合条件的下标
                    int complement = target - nums[i]; // 计算补数
                    if (map.containsKey(complement)) { // 检查补数是否在哈希表中
                        return true; // 如果找到,返回true
                    }
                    map.put(nums[i], i); // 将当前元素加入哈希表
                    return false; // 未找到,返回false
                })
                .mapToObj(i -> new int[]{map.get(target - nums[i]), i}) // 将结果映射为数组
                .findFirst() // 获取第一个符合条件的结果
                .orElseThrow(() -> new IllegalArgumentException("No two sum solution")); // 如果没有找到解,抛出异常
    }
}

解释

方法:

这个题解使用了哈希表来存储数组中每个元素的值和对应的下标。遍历数组的过程中,对于当前元素 num,先判断 target - num 是否在哈希表中,如果存在,则说明找到了两个数的和为 target 的情况,直接返回这两个数的下标。如果不存在,则将当前元素 num 和其下标存入哈希表中。这样可以在遍历的过程中快速判断是否存在与当前元素匹配的另一个数。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在遍历数组的同时更新哈希表,而不是先构建完整的哈希表再进行查找?
这种方法允许在单次遍历中同时进行查找和更新操作,从而将算法的时间复杂度控制在O(n)。如果我们先构建完整的哈希表,再进行查找,可能会导致对每个元素重复查找,增加不必要的操作和时间消耗。此外,同时更新哈希表可以保证我们在找到第一个符合条件的组合后立即停止遍历,提高效率。
🦆
在判断`target - num`是否在哈希表中时,为什么可以保证不会重复使用同一个元素?
在遍历的过程中,每遇到一个元素就立即检查其配对元素(target - num)是否已存在于哈希表中。由于我们是在将元素num添加到哈希表之前进行检查的,因此哈希表中存储的都是之前遍历过的元素。这样一来,我们找到的配对元素必然是不同的元素,避免了使用同一个元素两次。
🦆
如果数组中存在多对数字的和等于`target`,这个算法是否能找到所有这些组合?
这个算法只能找到第一对符合条件的两个数字。一旦找到一对符合条件的数字,算法就会结束并返回这对数字的下标。因此,它不会继续寻找其他可能的组合。如果需要找到所有符合条件的组合,需要对算法进行修改,使其能够继续遍历整个数组,收集所有有效的数字对。
🦆
这种方法对数组的顺序有没有特殊要求?
这种方法不对数组的顺序有特殊要求。无论数组中的元素如何排序,算法都能通过哈希表有效地查找到目标元素。数组的不同排列只影响找到的那对数字的顺序,但不影响算法的功能性。
🦆
在实际应用中,如何处理哈希表的冲突?
在哈希表中,冲突是指两个不同的键因为哈希函数的映射结果相同而被分配到同一个位置。处理方法通常包括链地址法(使用链表处理冲突)、开放地址法(寻找空白的哈希表位置)、双重哈希法(使用第二个哈希函数)等。在本算法中,由于键是数组中的元素值,若元素值相同,则更新其索引,这在处理具体问题时通常不会导致问题,因为我们只关心能否找到和为target的两个不同数字。

相关问题

三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

 

 

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

 

提示:

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

四数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abcd 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

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

 

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]

 

提示:

  • 1 <= nums.length <= 200
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109

两数之和 II - 输入有序数组

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列  ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1]numbers[index2] ,则 1 <= index1 < index2 <= numbers.length

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 index2

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

你所设计的解决方案必须只使用常量级的额外空间。

 

示例 1:

输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。

示例 2:

输入:numbers = [2,3,4], target = 6
输出:[1,3]
解释:2 与 4 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。

示例 3:

输入:numbers = [-1,0], target = -1
输出:[1,2]
解释:-1 与 0 之和等于目标数 -1 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。

 

提示:

  • 2 <= numbers.length <= 3 * 104
  • -1000 <= numbers[i] <= 1000
  • numbers非递减顺序 排列
  • -1000 <= target <= 1000
  • 仅存在一个有效答案

和为 K 的子数组

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 

子数组是数组中元素的连续非空序列。

 

示例 1:

输入:nums = [1,1,1], k = 2
输出:2

示例 2:

输入:nums = [1,2,3], k = 3
输出:2

 

提示:

  • 1 <= nums.length <= 2 * 104
  • -1000 <= nums[i] <= 1000
  • -107 <= k <= 107

两数之和 IV - 输入二叉搜索树

给定一个二叉搜索树 root 和一个目标结果 k,如果二叉搜索树中存在两个元素且它们的和等于给定的目标结果,则返回 true

 

示例 1:

输入: root = [5,3,6,2,4,null,7], k = 9
输出: true

示例 2:

输入: root = [5,3,6,2,4,null,7], k = 28
输出: false

 

提示:

  • 二叉树的节点个数的范围是  [1, 104].
  • -104 <= Node.val <= 104
  • 题目数据保证,输入的 root 是一棵 有效 的二叉搜索树
  • -105 <= k <= 105

小于 K 的两数之和

小于 K 的两数之和