两数之和
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。
你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。
看到题目我首先想到的是将目标值(target)拆分,将目标值变成0+target,1+(target-1),...,(target/2)+(target-target/2).
由于第一次做算法题,我这种思想在我刚准备打代码时就直接被我抛之脑后,因为这种思路其实很'暴力',数组可能根本没有这么多数,如果给定的target很大,那我势必会在拆分上花费很大的开销。
那么现在,我们可以改进,得到的是,直接用target减去数组里的数,用得到的数去从数组里找,这样的话我们就节省很多。这是又有一个小问题,如果你从数组里找,你每减一个数,你势必需要遍历一遍数组。而我是将target减过的数放进List里面,从后面找前面已有的数。
代码如下:
class Solution {
public int[] twoSum(int[] nums, int target) {
List list =new ArrayList<Integer>();
int result[] = new int[2];
for (int i = 0 ;i<nums.length;i ++){
if (list.contains(target-nums[i]))
{
result[0]=list.indexOf(target-nums[i]);
result[1]=i;
break;
}else {
list.add(nums[i]);
}
}
return result;
}
}
时间复杂度为O(n),运行的时间大概为几十ms,下面则是几ms的代码;
class Solution {
public int[] twoSum(int[] nums, int target) {
// 先复制一份数组
int[] newNums = Arrays.copyOf(nums, nums.length);
// 排序复制的数组
Arrays.sort(newNums);
// 结果集
int[] res = new int[2];
// 循环遍历每一个
for (int i=0; i<newNums.length - 1; i++) {
// 如果剩余的数只有一个,那么判断这一个,并退出循环
if (newNums.length-i-1 == 1) {
if (newNums[i]+newNums[i+1] == target) {
res = new int[]{i,i+1};
} else {
res = null;
}
break;
}
// 如果剩余的不只一个,则用二分法
int num = newNums[i];
int rest = target-num;
int start = i+1;
int end = newNums.length-1;
int mid; // = (start+end) / 2;
int src = -1;
while(start < end) {
mid = (start+end+1) / 2;
if (rest < newNums[mid]) {
end = mid-1;
} else if (rest > newNums[mid]) {
start = mid+1;
} else {
src = mid;
break;
}
}
if (start == end && newNums[start] == rest) {
src = start;
}
// 如果二分法找到,则退出循环
if (src != -1) {
res[0] = i;
res[1] = src;
break;
}
}
boolean flag1=false;
boolean flag2=false;
// 在原数组中查找位置
for(int i=0; i<nums.length; i++) {
if (flag1 && flag2) break;
if (!flag1 && nums[i] == newNums[res[0]]) {
res[0] = i;
flag1 = true;
continue;
}
if (!flag2 && nums[i] == newNums[res[1]]) {
res[1] = i;
flag2 = true;
}
}
return res;
}
}