您的位置:首页 > 编程语言 > Java开发

Two Sum

2016-07-27 19:32 381 查看


题目描述

Given an array of integers, find two numbers such that they add up to a specific target number.

The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.

You may assume that each input would have exactly one solution.

Input: numbers={2, 7, 11, 15}, target=9

Output: index1=1, index2=2

题目大意:
给一个数组,找出其中是否有两个数之和等于给定的值。类似的还有3 sum ,4 sum ..等 K sum
问题。其实原理是差不多的,这样想:先取出一个数,那么我只要在剩下的数字里面找到两个数字使得他们的和等于(target – 那个取出的数)。

分析:

1、暴力法,复杂度是 O(n^2),会 TLE。

2、先排序,然后从开头和结尾同时向中间查找,原理也比较简单。复杂度O(nlogn)

3、使用HashMap。把每个数都存入map中,任何再逐个遍历,查找是否有
target – nubmers[i]。 时间复杂度 O(n)

方法1:暴力法

typedef struct Node{
int id,val;
}Node;
bool compare(const Node & a,const Node & b){
return a.val < b.val;
}

class Solution {
public:
vector<int> twoSum(vector<int> &numbers, int target) {
Node nodes[numbers.size()];
for(unsigned int i=0; i<numbers.size(); i++){
nodes[i].id = i+1;
nodes[i].val = numbers[i];
}
sort(nodes, nodes+numbers.size(), compare);
int start=0,end=numbers.size()-1;
vector<int> ans;
while(start < end){
if(nodes[start].val + nodes[end].val == target){
if(nodes[start].id > nodes[end].id)
swap(nodes[start].id , nodes[end].id);
ans.push_back(nodes[start].id);
ans.push_back(nodes[end].id);
return ans;
}else if( nodes[start].val + nodes[end].val < target ){
start++;
}else
end--;
}
}
};
方法2:先排序
ublic class Solution {

static class Pair implements Comparable<Pair> {
int value, index;
public Pair(int v, int id) {
value = v;
index = id;
}

@Override
public int compareTo(Pair b) {
return this.value - b.value;
}
}

public static int[] twoSum(int[] numbers, int target) {
int[] res = new int[2];
Pair[] pairs = new Pair[numbers.length];

// get pairs and sort
for (int i = 0; i < numbers.length; ++i) {
pairs[i] = new Pair(numbers[i], i + 1);
}
Arrays.sort(pairs);

// two points
int left = 0, right = numbers.length - 1, sum = 0;
while (left < right) {
sum = pairs[left].value + pairs[right].value;
if (sum == target) {
res[0] = pairs[left].index;
res[1] = pairs[right].index;
if (res[0] > res[1]) {
// swap them
res[0] ^= res[1];
res[1] ^= res[0];
res[0] ^= res[1];
}
break;
} else if (sum > target) {
--right;
} else {
++left;
}
}

return res;
}
}
方法3:HashMap
public class Solution {
public static int[] twoSum(int[] numbers, int target) {
int[] res = new int[2];
HashMap<Integer, Integer> nums = new HashMap<Integer, Integer>();

for (int i = 0; i < numbers.length; ++i) {
// add i-th number
Integer a = nums.get(numbers[i]);
if (a == null)
nums.put(numbers[i], i);

// find (target - numbers[i])
a = nums.get(target - numbers[i]);
if (a != null && a < i) {
res[0] = a + 1;
res[1] = i + 1;
break;
}
}
return res;
}
}
思路是循环一次,每次都判断当前数组索引位置的值在不在hashtable里,不在的话,加入进去,key为数值,value为它的索引值;在的话,取得他的key,记为n(此时n一定小于循环变量i),接下来再在hashtable中查找(target-当前数值)这个数,利用了hashtable中查找元素时间为常数的优势,如果找到了就结束,此处需要注意的是,如果数组中有重复的值出现,那么第二次出现时就不会加入到hashtable里了,比如3,4,3,6;target=6时,当循环到第二个3时,也可以得到正确结果。

总结:读完题首先想到的就是两层遍历法,但是显然时间复杂度太高,是O(N^2),不符合要求,于是就应该想如何降低复杂度,首先应该想将逐个比较转变为直接查找,即首先计算出 target与当前元素的差,然后在序列中寻找这个差值,这样首先就把问题简化了,而寻找的过程可以先对序列进行快排,然后二分查找,这样整体的复杂度就降低为 O(N*logN) 了;查找最快的方法是利用一个 map容器存储每个元素的索引,这样取得某个特定元素的索引只需要常数时间即可完成,这样就更快了,最多只需遍历一次序列,将元素及其索引加入map中,在遍历的过程中进行对应差值的查找,如果找到了就结束遍历,这样时间复杂度最多为
O(N),It's amazing!

        本来想用C++的hash_map,后来发现STL并没有这个容器,貌似需要自己实现代码才能用,遂用map了,map底层使用红黑树实现的,所以它的查找时间是O(logN),略逊于hash_map,但是这不是绝对的,hash_map虽然理论上是O(N),其实他还有计算hash值的时间消耗,也存在时间比O(logN)还大的时候。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java leetcode hash