问题描述
给定一个整数数组和一个目标值,需要找出数组中两个数,使它们的和等于给定的目标值,并返回这两个数。
方法一:暴力枚举法(最简单但效率最低)
通过两层循环遍历数组,外层循环取一个元素x,内层循环从x的下一个位置开始遍历,检查x与当前内层元素的和是否等于目标值。
代码实现
public static int[] getTargetSumIndices(int[] arr, int target){
for(int i = 0; i < arr.length; i++){
int x = arr[i]; // 元素 x
for(int j=i+1; j < arr.length; j++){
int y = arr[j]; // 元素 y
int sum = x + y;
if(target == sum){
return new int[] {i, j};
}
}
}
return new int[] {-1, -1}; // 未找到
}
复杂度分析
- 时间复杂度:最坏情况为O(n²),因为使用了嵌套循环
- 空间复杂度:O(1),只使用了常数级别的额外空间
方法二:排序 + 二分查找法
首先对数组进行排序,然后遍历每个元素x,计算目标值与x的差值(补数),在x之后的元素中使用二分查找寻找这个补数。
代码实现
public static int[] getTargetSumPair(int[] arr, int target){
// 对数组进行排序
Arrays.sort(arr);
for(int i = 0; i < arr.length; i++) {
int x = arr[i]; // 元素 x
// 计算x与目标值的补数
int complement = target - x;
// 二分查找 - 寻找补数的索引
int y = binarySearch(complement, arr, i+1, arr.length - 1);
if( y != -1){
return new int[]{x, y};
}
}
return new int[] {-1, -1}; // 未找到
}
public static int binarySearch(int num, int[] arr, int l, int h){
while( l <= h ){
int mid = (l + h) / 2;
if( arr[mid] > num ){
h = mid - 1;
}else if( arr[mid] < num ){
l = mid + 1;
}else{
return arr[mid];
}
}
return -1;
}
复杂度分析
- 时间复杂度:O(n*log(n)),主要来自排序和二分查找
- 空间复杂度:O(1)
方法三:排序 + 双指针法
先将数组排序,然后用两个指针分别指向数组的起始位置和结束位置。如果两指针指向的元素之和等于目标值,则返回这两个元素;如果和小干目标值,左指针右移增大和;如果和大于目标值,右指针左移减小和。
代码实现
public static int[] getTargetSumPair(int[] arr, int target){
// 对数组进行排序
Arrays.sort(arr);
// 设置高低指针
int l = 0;
int h = arr.length - 1;
// 当左指针小于右指针时循环
while(l < h ){
if (arr[l] + arr[h] > target){
h--; // 和大于目标值,减小较大数
}else if(arr[l] + arr[h] < target){
l++; // 和小于目标值,增大较小数
}else{
return new int[] {arr[l], arr[h]}; // 和等于目标值,返回结果
}
}
return new int[] {-1, -1}; // 未找到
}
复杂度分析
- 时间复杂度:O(n*log(n)),主要来自排序操作
- 空间复杂度:O(1)
方法四:哈希集合法(最优解)
创建一个空的哈希集合,遍历数组元素。对于每个元素,先计算其补数(目标值减当前元素),然后检查这个补数是否已存在于哈希集合中。如果不存在,将当前元素加入集合;如果存在,则说明找到了答案,返回当前元素和补数。
代码实现
public static int[] getTargetSumPair(int[] arr, int target){
HashSet<Integer> seen = new HashSet<>();
for( int i = 0; i < arr.length; i++){
int complement = target - arr[i];
if(!seen.contains(complement)) {
seen.add(arr[i]);
}else{
return new int[] {arr[i], complement};
}
}
return new int[] {-1, -1};
}
复杂度分析
- 时间复杂度:O(n),只需一次遍历,哈希表的查找和插入操作都是O(1)
- 空间复杂度:O(n),需要额外的哈希集合存储访问过的元素
总结
四种方法各有优缺点,暴力枚举法最简单直观但效率最低;排序+二分查找和排序+双指针法通过排序优化了时间复杂度;哈希集合法通过空间换时间的方式达到了线性时间复杂度,是解决两数之和问题的最优方案。在实际应用中,应根据具体场景(如是否需要返回索引、是否允许修改原数组等)选择合适的方法。FINISHED