Two Sum (两数之和)问题的思想
1、这个问题的最基本形式是这样:给你一个数组和一个整数target,可以保证数组中存在两个数的和为target,请你返回这两个数的索引。
比如输入nums = [3,1,3,6],target = 6,算法应该返回数组[0,2],因为 3 + 3 = 6。
暴力枚举
public static int[] towSum(int[] num,int target){//暴力枚举,时间复杂度O(n^2)
for (int i = 0; i < num.length; i++) {
for (int j =1+1; j < num.length; j++) {
if (num[i]+num[j]==target){
return new int[]{i,j};
}
}
}
return new int[]{-1,-1};
}
用哈希表实现一个映射关系
public static int[] towSum2(int[]num,int target){//用哈希表实现一个映射关系,时间复杂度O(n)
int n = num.length;
HashMap<Integer,Integer> index = new HashMap<>();
for (int i = 0; i < num.length; i++) {//实现映射
index.put(num[i],i);
}
for (int i = 0; i < n; i++) {
int a = target-num[i];
if (index.containsKey(a) && index.get(a)!=i){//存在符合的a,且a不是自身
return new int[]{i,index.get(a)};
}
}
return new int[]{-1,-1};
}
总结:这类问题主要是想让我们学会如何用哈希表处理问题。
2、稍微修改一下上面的问题,要求我们设计一个类,拥有两个 API:
class TwoSum {
// 向数据结构中添加一个数 number
public void add(int number);
// 寻找当前数据结构中是否存在两个数的和为 value
public boolean find(int value);
}
这里我们还是用到一个哈希表的方法:
public class Tow_SumII {
Map<Integer,Integer>freq = new HashMap();
public void add(int number){//时间复杂度O(1)
//记录number出现的次数
freq.put(number,freq.getOrDefault(number,0)+1);
}
public boolean find(int value){//时间复杂度O(n)
for(Integer key : freq.keySet()){
int a = value-key;
if (a == key && freq.get(key)>1){//存在两个a
return true;
}
if (a !=key && freq.containsKey(a)){//存在不等于a的值符合条件
return true;
}
}
return false;
}
}
但是,有时候我们害的考虑显示场景中的一个情况,
若是经常要使用find方法,我们需要对以上两个API进行优化,将时间复杂度转移到add的方法中,从而实现江都find方法的时间复杂度。
public class TowSumII{
Set<Integer> sum = new HashSet<>();//用于记录两个数之和
List<Integer> num = new ArrayList<>();//记录每一个元素
public void add(int number){//每次加入一个数,都记录所有两个数和的肯能值
for (int n : num){
sum.add(n + number);
num.add(number);
}
}
public boolean find(int value){//只需要判断是否存在符合的sum值就行,时间复杂度O(1)
return sum.contains(value);
}
}
总结:对于 TwoSum 问题,一个难点就是给的数组无序。对于一个无序的数组,我们似乎什么技巧也没有,只能暴力穷举所有可能。但是,我们对于不同的场景需求,还是需要灵活变化一点,这里也只是给大家开阔一个思维。