题目描述
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
题目大意
给定一个整数数组,两个数之和等于目标值(target)则返回这两个数的下标。
可以确定的是,每个样例有且仅有一个解,同一元素不能使用两次。
Example:
Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].
Subscribe to see which companies asked this question.
题解:
1. 暴力求解,这是最为简单易行的解法,使用双重循环分别枚举两个数A和B,直到找到A + B = target为止,时间复杂度O(n^2);
(实际时间使用情况,由于测试样例的缘故,正向枚举需要几百ms时间,而逆向则只需要29ms[Java])
2. 借助Hash求解,这是较为标准的解法,首先将数组中每个数存入Hash表,然后枚举数组内数字(假设当前枚举数字为A,则在Hash表中查询target- A是否存在,找到则返回解),枚举一趟中一定因为有解的存在而返回,时间复杂度O(n);
(实际时间使用情况,10ms[Java]以内)
3. 排序后搜索,将数组排序使之成为有序数组,一个指针[start]指向数组第一个元素,另一个指针[end]指向数组最后一个元素。如果两数之和恰好等于target,则返回;如果两数之和比target小,说明当前和较小,将start指向下一个元素(end已经不能后移);如果两数之和比target大,则将end指向前一个元素。由此,每次问题都缩小规模,直至最终的答案。时间复杂度O(nlogn)
(实际时间使用情况,5ms[Java]左右,情况甚至好于Hash,原因在于数据规模不大的情况下,内存分配等占用的时间超过实际算法计算时间)
解法2代码:
Java:
public class Solution {
public int[] twoSum(int[] nums, int target) {
// 创建一个map以储存数字
Map map = new HashMap();
for (int i = 0; i < nums.length; ++i) {
if (map.containsKey(target - nums[i])) {
return new int[]{map.get(target - nums[i]), i};
}
map.put(nums[i], i);
}
return null;
}
}
class Solution {
public:
vector twoSum(vector& nums, int target) {
// map存储数组信息 vector存储结果
map m;
vector ans;
// 遍历数组查询结果
for (int i = 0; i < nums.size(); ++i) {
// 查找到配对元素
if (m.find(target - nums[i]) != m.end()) {
ans.push_back(m[target - nums[i]]);
ans.push_back(i);
return ans;
}
// 否则将现在元素插入map
m.insert(pair(nums[i], i));
}
return ans;
}
};
极致速度探索1(双指针 5ms):
public class Solution {
private int findIndexHead(int[] nums, int n) {
for (int i = 0; i < nums.length; ++i) {
if (nums[i] == n) {
return i;
}
}
return -1;
}
private int findIndexTail(int[] nums, int n) {
for (int i = nums.length - 1; i > -1; --i) {
if (nums[i] == n) {
return i;
}
}
return -1;
}
public int[] twoSum(int[] nums, int target) {
// 拷贝数组
int[] cNums = Arrays.copyOf(nums, nums.length);
Arrays.sort(cNums);
// 从前面和后面分别开始遍历
for (int start = 0, end = cNums.length - 1; start < end;) {
if (cNums[start] + cNums[end] == target) {
return new int[]{findIndexHead(nums, cNums[start]),
findIndexTail(nums, cNums[end])};
}
else if (cNums[start] + cNums[end] < target) {
start++;
}
else {
end--;
}
}
return null;
}
}
探索1的速度已经比较快了,但是与0ms差距较大,考虑到系统HashMap涉及ReHash过程,手写一个OpenHash,固定Hash表长度,实际慢了1ms,为6ms。。。
public class Solution {
// 手写Hash结构
class Hash{
// 保存元素 以OpenHash的方式处理Hash冲突
private class Elem {
public int key;
public int value;
public Elem next;
public Elem(int key, int value) {
this.key = key;
this.value = value;
this.next = null;
}
}
// Hash数组
private Elem[] arr;
// 构造函数
public Hash(int len) {
arr = new Elem[len];
}
// 插入元素方法
public void put(int key, int value) {
int index = key % arr.length;
if (arr[index] == null) {
arr[index] = new Elem(key, value);
return;
}
Elem last = arr[index];
while (last.next != null) {
last = last.next;
}
last.next = new Elem(key, value);
}
// 获取元素方法
public int get(int key) {
int index = key % arr.length;
if (arr[index] == null) {
return Integer.MIN_VALUE;
}
for (Elem last = arr[index]; last != null; last = last.next) {
if (last.key == key) {
return last.value;
}
}
return Integer.MIN_VALUE;
}
}
public int[] twoSum(int[] nums, int target) {
// 查找最小值
int minVal = nums[0];
for (int i = 1; i < nums.length; ++i) {
minVal = Math.min(minVal, nums[i]);
}
minVal = -minVal;
// 处理target以及nums数组
target += 2 * minVal;
for (int i = 0; i < nums.length; ++i) {
nums[i] += minVal;
}
// 使用定义的Hash结构
Hash hash = new Hash(1991);
for (int i = 0; i < nums.length; ++i) {
if (target - nums[i] < 0) {
continue;
}
if (hash.get(target - nums[i]) != Integer.MIN_VALUE) {
return new int[]{hash.get(target - nums[i]), i};
}
hash.put(nums[i], i);
}
return null;
}
}
考虑使用BitSet一次创建足够多的空间,将Hash过程变成数组找寻下标的方式,
结果是,仍然为6ms。。。
public class Solution {
private int find(int[] nums, int target) {
for (int i = 0; i < nums.length; ++i) {
if (nums[i] == target) {
return i;
}
}
return -1;
}
public int[] twoSum(int[] nums, int target) {
// 寻找最小和最大值
int minVal = nums[0];
int maxVal = nums[0];
for (int i = 1; i < nums.length; ++i) {
minVal = Math.min(minVal, nums[i]);
maxVal = Math.max(maxVal, nums[i]);
}
// 开一个BitSet
BitSet set = new BitSet(maxVal - minVal + 1);
for (int i = 0; i < nums.length; ++i) {
int nToFind = target - minVal - nums[i];
if (nToFind < 0) {
continue;
}
if (set.get(nToFind)) {
return new int[]{find(nums, target - nums[i]), i};
}
set.set(nums[i] - minVal);
}
return null;
}
}
极致探索版4:先探到这把,洗洗睡了。。。