本文正在参加「Java主题月 - Java 刷题打卡」,详情查看 活动链接
题目描述
两数之和
给定一个整数数组 nums 和一个整数目标值 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]
思路分析
首先能想到最简单的方式就是暴力破解,通过两个 for 循环来进行判断,复杂度是。但是仔细一想,似乎还有优化的空间,能否把复杂度降到呢,我们可以用一个 map 将数组中的值存储起来,key 对应 nums[i],value 对应 i,将 target - nums[i] 的值判断是否存在于 map 中,如果存在,就返回数组中对应的两个位置。该解法的时间复杂度是,空间复杂度也是。
还有一种解法是排序+双指针,先对该数组进行排序,排序前先拷贝一份数组,然后对数组进行排序,排序后利用双指针找到对应数的下标,因为已经排序过了,所以数组中的位置已经变化了,所以需要将拷贝的数组遍历寻找对应的下标。
代码展示
解法一:暴力破解,时间复杂度,空间复杂度。
public int[] twoSum(int[] nums, int target) {
int length = nums.length;
for(int i = 0;i < length;++i){
int outArrayValue = nums[i];
for(int j = 0;j < length;++j){
if(outArrayValue + nums[j] == target && i != j){
return new int[]{i,j};
}
}
}
return null;
}
解法二:通过 map 存储遍历过的值,进行判断。时间复杂度,空间复杂度。
public int[] twoSum(int[] nums, int target) {
int length = nums.length;
Map<Integer,Integer> map = new HashMap<>();
for(int i = 0;i < length;++i){
int value = target - nums[i];
if(map.containsKey(value)){
return new int[]{map.get(value),i};
}
map.put(nums[i],i);
}
return null;
}
解法三:排序+双指针,时间复杂度,空间复杂度。这种方式写法最复杂。
public int[] twoSum(int[] nums, int target) {
int[] tempArray = Arrays.copyOf(nums,nums.length);
int length = nums.length;
int head = 0;
int tail = length - 1;
quickSort(nums, length);
int[] targetArray = null;
while (head < tail) {
if (nums[head] + nums[tail] > target) {
tail--;
} else if (nums[head] + nums[tail] < target) {
head++;
} else {
targetArray = new int[]{head, tail};
break;
}
}
int count = 0;
boolean firstHasValue = false;
boolean secondHasValue = false;
for (int i = 0;i < length;i++){
if (nums[targetArray[0]] == tempArray[i] && !firstHasValue){
targetArray[0] = i;
count++;
firstHasValue = true;
if (count == 2){
return targetArray;
}
continue;
}
if (nums[targetArray[1]] == tempArray[i] && !secondHasValue){
targetArray[1] = i;
count++;
secondHasValue = true;
}
if (count == 2){
return targetArray;
}
}
return null;
}
private void quickSort(int[] nums,int n){
quickInternal(nums,0,n - 1);
}
private void quickInternal(int[] nums,int l,int r){
if(l >= r){
return;
}
int division = partition(nums,l,r);
quickInternal(nums,l,division - 1);
quickInternal(nums,division + 1,r);
}
private int partition(int[] a,int l,int r){
int base = a[l];
while(l < r){
while(l < r && a[r] >= base){
r--;
}
a[l] = a[r];
while(l < r && a[l] <= base){
l++;
}
a[r] = a[l];
}
a[l] = base;
return l;
}
先利用快排对数组进行排序,然后利用双指针寻找到合适下标,最后再遍历最原始的数组(也就是拷贝后的数组),找到原本的下标位置,最后一步找对应的下标位置时尤其要注意。
总结
两数之和的三种解法,暴力破解最容易但是时间复杂度最高,利用哈希表的解法时间复杂度最优但是需要耗费的空间,而双指针+快排这种的时间复杂度是,空间复杂度也不低,而且写法也比较复杂。