引用
在算法刷题中,"两数之和"是入门级经典题型,核心考察对数组遍历、元素查找的基础逻辑设计能力。LeetCode 1 题"两数之和"要求在数组中找到和为目标值的两个元素并返回下标,既需要满足"不重复使用元素"的约束,也考验对数据存储与查找效率的平衡,而使用 ArrayList 解答则能直观体现"遍历匹配+结果存储"的基础算法思维,尤其适合入门者理解数组操作与集合类的结合使用。
一、问题概述
1.1 问题描述
给定一个整数数组 nums 和一个整数目标值 target,需在数组中找出和为 target 的两个整数,返回它们的数组下标。题目保证每种输入只会对应一个答案,且不能使用两次相同的元素,答案返回顺序不限。
1.2 示例解析
- 示例 1:
输入 nums = [2, 7, 11, 15],target = 9。数组中 2 + 7 = 9,对应下标为 0 和 1,输出 [0, 1]。
- 示例 2:
输入 nums = [3, 2, 4],target = 6。数组中 2 + 4 = 6,对应下标为 1 和 2,输出 [1, 2]。
- 示例 3:
输入 nums = [3, 3],target = 6。数组中两个 3 之和为 6,对应下标为 0 和 1,输出 [0, 1]。
二、解题思路
2.1 核心规律分析
本题的核心是"暴力枚举匹配"------通过遍历数组中所有可能的元素对,判断其和是否等于目标值。关键逻辑如下:
-
元素对需满足"不重复使用":通过控制遍历下标,让 j 始终大于 i(j = i + 1 开始),避免重复遍历同一对元素(如 (i=0,j=1) 和 (i=1,j=0)),同时保证不使用同一元素两次。
-
结果唯一性:题目保证仅有一组有效答案,找到后可直接终止遍历,无需继续查找。
-
ArrayList 的核心作用:临时存储找到的两个下标,最后转换为题目要求的 int[] 类型返回,利用集合类的动态存储特性简化结果管理。
2.2 基于 ArrayList 的解决方案
结合 ArrayList 的动态存储优势,解题步骤如下:
-
初始化 ArrayList,用于存储找到的两个目标下标。
-
双重循环枚举所有元素对:外层循环控制第一个元素下标 i(从 0 到 n-2),内层循环控制第二个元素下标 j(从 i+1 到 n-1)。
-
判断元素对和是否等于 target:若满足条件,将 i 和 j 加入 ArrayList,同时终止所有循环(避免多余计算)。
-
转换结果格式:将 ArrayList 中存储的两个下标,转换为 int[] 数组返回(满足题目返回值要求)。
三、代码实现与逐句解析
3.1 完整代码
```java
java
class Solution {
public int[] twoSum(int[] nums, int target) {
ArrayList<Integer> resultList = new ArrayList<>();
int n = nums.length;
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
if (nums[i] + nums[j] == target) {
resultList.add(i);
resultList.add(j);
// 找到唯一答案,直接跳出所有循环(避免多余遍历)
i = n; // 让外层循环也退出
break;
}
}
}
// 将 ArrayList 转换为 int[] 数组返回(题目要求返回 int[])
int[] result = new int[2];
result[0] = resultList.get(0);
result[1] = resultList.get(1);
return result;
}
}
```
3.2 代码逐句解析
-
import java.util.ArrayList;:导入 ArrayList 类,用于动态存储目标下标。
-
ArrayList resultList = new ArrayList<>();:创建 Integer 类型的 ArrayList 实例,用于临时存储找到的两个下标(ArrayList 支持动态添加元素,无需提前指定长度)。
-
int n = nums.length;:获取原数组长度,简化后续循环条件编写。
-
外层 for 循环 for (int i = 0; i < n - 1; i++):遍历第一个元素,i 最大为 n-2(确保内层循环 j = i+1 不越界)。
-
内层 for 循环 for (int j = i + 1; j < n; j++):遍历第二个元素,j 从 i+1 开始,保证每个元素对仅被遍历一次,且不重复使用同一元素。
-
if (nums[i] + nums[j] == target):判断当前元素对的和是否等于目标值,满足条件则执行后续逻辑。
-
resultList.add(i); resultList.add(j);:将两个目标下标的添加到 ArrayList 中,完成结果存储。
-
i = n; break;:通过修改外层循环变量 i 为 n,使外层循环条件 i < n-1 不成立,同时用 break 退出内层循环,实现"找到答案后立即终止所有遍历"的优化。
-
int[] result = new int[2];:创建长度为 2 的 int 数组(题目要求返回 int[] 类型)。
-
result[0] = resultList.get(0); result[1] = resultList.get(1);:从 ArrayList 中取出两个下标,赋值给 int 数组(ArrayList 的 get 方法按索引获取元素)。
-
return result;:返回最终结果数组。
四、复杂度分析
4.1 时间复杂度
时间复杂度为 O(n²)。外层循环需执行 n-1 次(n 为数组长度),内层循环最坏情况下需执行 n-i-1 次,总遍历次数为 (n-1) + (n-2) + ... + 1 = n(n-1)/2,属于 O(n²) 级别。由于题目保证仅有一组答案,实际执行次数可能小于最坏情况,但时间复杂度的量级不变。
4.2 空间复杂度
空间复杂度为 O(1)。虽然使用了 ArrayList,但 ArrayList 仅存储两个固定的下标值,占用空间不随数组长度 n 变化,属于常数级空间开销。额外创建的 int[] 结果数组长度固定为 2,同样不影响空间复杂度量级。
五、边界情况与测试用例分析
5.1 边界情况
-
数组长度为 2:此时 i=0,j=1,直接判断两元素和是否等于 target,如输入 [5,7]、target=12,输出 [0,1],逻辑正常。
-
数组中有重复元素:如示例 3 中 [3,3]、target=6,i=0 时 j=1,两元素和满足条件,存入下标 0 和 1,避免重复遍历。
-
目标值为负数或零:如输入 [-1,-2,-3]、target=-5,i=0(-1)、j=2(-3),和为 -4 不满足;i=1(-2)、j=2(-3),和为 -5 满足,返回 [1,2],算法逻辑通用。
-
答案在数组末尾:如输入 [1,4,5,8]、target=13,i=2(5)、j=3(8),和为 13,正常返回 [2,3]。
5.2 测试用例验证
- 示例 1 验证:
输入 nums = [2,7,11,15],target=9。
外层 i=0,内层 j 从 1 开始:
j=1 时,2+7=9,满足条件,将 0、1 加入 resultList,i 设为 4(n=4),退出内层循环,外层循环条件 i
转换为 int[] 数组返回 [0,1],与示例输出一致。
- 示例 2 验证:
输入 nums = [3,2,4],target=6。
外层 i=0:j=1(3+2=5≠6)、j=2(3+4=7≠6),无匹配;
外层 i=1:j=2(2+4=6),满足条件,将 1、2 加入 resultList,i 设为 3(n=3),终止遍历。
返回 [1,2],与示例输出一致。
- 示例 3 验证:
输入 nums = [3,3],target=6。
外层 i=0:j=1(3+3=6),满足条件,将 0、1 加入 resultList,终止遍历。
返回 [0,1],与示例输出一致。
六、算法思想拓展与应用场景
6.1 算法思想拓展
本题使用的"暴力枚举+ArrayList 结果存储"思想,可拓展至以下场景:
-
多元素组合查找:如"三数之和""四数之和"的入门解法(暴力枚举所有组合),可通过 ArrayList 存储多组下标或元素值,最后统一处理。
-
结果集动态管理:当需要存储不确定数量的中间结果(如多组满足条件的元素对)时,ArrayList 的动态添加特性比数组更灵活(本题虽仅一组结果,但可直接迁移至多结果场景)。
-
类型转换适配:当题目返回类型与中间存储需求不一致时(如本题需返回 int[],但中间存储需动态添加),可利用 ArrayList 作为过渡,最后统一转换。
6.2 应用场景
该算法的基础逻辑可应用于实际开发:
-
简易数据匹配:如在学生成绩数组中,查找两门课程成绩之和达到特定分数线的学生下标(如总分≥180)。
-
购物场景:在商品价格数组中,查找两件商品总价等于优惠券面额的组合,返回商品索引用于展示。
-
日志分析:在用户行为时长数组中,查找两次行为时长之和等于特定值的记录下标(如两次访问总时长=30分钟)。
七、总结
"两数之和"的 ArrayList 解法,核心是通过"暴力枚举元素对"找到有效答案,利用 ArrayList 的动态存储特性简化结果管理,最后完成与题目要求返回类型(int[])的适配。该解法无需复杂数据结构基础,逻辑直观易懂,是入门者理解"数组遍历+集合类应用"的典型案例。
虽然时间复杂度为 O(n²),在大规模数组(如 n>10⁴)场景下效率较低,但对于入门刷题、理解题目核心约束(不重复使用元素、唯一答案)具有重要意义。此外,该解法中"通过修改外层循环变量终止所有循环"的优化技巧、"ArrayList 与数组类型转换"的实践,也为后续复杂算法的实现积累了基础操作经验。
若需优化时间复杂度,可将 ArrayList 替换为 HashMap(键存元素值,值存下标),将查找补数的时间复杂度降为 O(1),整体时间复杂度优化至 O(n),适合大规模数据场景。