Given an array of integers, find two numbers such that they add up to a specific target number.
The function twoSum should return result of the two numbers such that they add up to the target . Please note that your returned answers (both result1 and result2) are not zero-based.
You may assume that each input would have exactly one solution.
Input: numbers={2, 7, 11, 15}, target=9
Output: result1=2, result2=7
一:若数组无序
方法一,使用双重循环,遍历数组的方法,确定一个数之后查找数组以确定另外一个数是否也存在数组中
/*2:
* 寻找和为定值的两个数
* 问题描述:给定一个数组和一个数,要求在数组中找出两个数,使其和为该数,并输出这两个数
*
* 方法一:该算法的时间复杂度为o(n*n),双重循环
* 分析:
* 1,给定无序数组T,整数k
* 2,i=0->T.length int temp=k-T[i]
* 3,j=i+1->T.length if(temp==T[j]) return T[I],T[j]
* @param Targent目标无序数组
* @param k和值
* @param 返回值为一个数组,存放结果的两个数
*/
public int[] twoSum1(int[] Targent,int k){
int[] result=new int[2];
int temp=0;
for(int i=0;i<Targent.length;i++){
temp=k-Targent[i]; //第一个数确定的情况下,求出第二个数
for(int j=1+i;j<Targent.length;j++){
if(temp==Targent[j]){ //在数组中寻找第二个数是否存在,若存在,则返回两个数
result[0]=Targent[i];
result[1]=Targent[j];
return result;
}
}
}
return result;
}
方法二,使用hashmap映射的方法
/*
* 方法四:利用hashMap存放互补数组,将原数组和互补数组作映射,查找hashMap的时间复杂度为O(1),与处理hashMap的时间
* 复杂度为O(n),故该方法的时间复杂度为O(n),但是需要O(N)的hashMap空间复杂度,在时间复杂度要求比较高的情况下,可以利用
* 空间换时间!
* 伪代码:
* 1,int[] T,int k int[] R=k-T[i] i=0->T.length
* 2,hashMap.put(k-T[i],T[i]) i=0->T.length-1 //构造hashMap,key=k-T[i],value=T[i] ,时间复杂度为O(n)
* 3,if(hashMap.containsKey(T[i])) return T[i],k-T[i]; //containsKey(key)根据hashMap中是否存在键值,来返回
* @param Targent为无序或者有序数组
* @param k为输入和值
* @param 返回值为二维数组
*/
public int[] twoSum4(int[] Targent,int k){
int[] result=new int[2];
//构造hashMap
Map<Integer, Integer> reflectMap=new HashMap();
for(int i=0;i<Targent.length;i++){
//将数组R作为key值,T作为value,时间复杂度为O(n)
reflectMap.put(k-Targent[i], Targent[i]);
}
//根据T[i]查询hashMap,hashMap查询时间复杂度为O(1)
for(int i=0;i<Targent.length;i++){
if(reflectMap.containsKey(Targent[i])){
result[0]=Targent[i];
result[1]=k-Targent[i];
return result;
}
}
return result;
}
二:若数组按照升序排列
方法一,在循环遍历的基础上使用二分查找,确定一个数之后,使用二分查找另外一个数是否存在于数组之中
/*
* 算法优化:二分查找的时间复杂度为O(lgn),最坏情况下需要查找n次,故该算法的时间复杂度为O(nlgn)
* 若数组无序,则用快速排序方法进行排序,时间复杂度为O(nlgn),最后总共的时间复杂度为O(nlgn)+O(nlgn)=O(nlgn)
* 分析:
* 1,int i=0->T.length int temp=k-T[i]
* 2,使用二分查找的方法来寻找temp数值是否在T数组中
* @param Targent为有序数组
* @param k为输入和值
* @param 返回值为二维数组
*/
public int[] twoSum2(int[] Targent,int k){
int[] result=new int[2];
int temp=0;
for(int i=0;i<Targent.length;i++){ //时间复杂度O(n)
temp=k-Targent[i];
if(divideQuery(Targent, temp)){ //时间复杂度O(lgn)
result[0]=Targent[i];
result[1]=temp;
return result;
}
}
return result;
}
/*
* 二分查找:递归实现,时间复杂度为O(lgn)
* 分析:
* 1,对于一个有序数组T,所需要查找的数为k
* 2,int index = (T.length-1)/2 //找到数组中位数的下标指针
* 3,boolean divideQuery'(int[] Targent, int k, int left, int right)
* if(k<T[index]) return divideQuery(T, k , 0 ,index-1)
* else if(k==T[index]) return true
* else return divide Query(T, k, index+1, T.length-1)
* 4,if(left==index&&Targent[index]!=k) return false; //递归结束判断
* @param Targent目标有序数组
* @param k需要查找的数
* @param 返回值为boolean ,存在k则返回true ,否则返回false
*/
public boolean divideQuery(int[] Targent,int k) {
int firstIndex=Targent.length-1;
if(k<Targent[0]||k>Targent[firstIndex])
return false;
return divideQuery(Targent, k, 0 , firstIndex);
}
private boolean divideQuery(int[] Targent, int k ,int left ,int right) {
int index=(right+left)>>1;
//该判断为递归结束的判断条件,否则递归不能结束
if(left==index&&Targent[index]!=k)
return false;
if(k<Targent[index])
return divideQuery(Targent, k, left, index-1);
else if(k==Targent[index])
return true;
else
return divideQuery(Targent, k, index+1, Targent.length-1);
}
/*
* 二分查找:while循环实现,对于有序数组时间复杂度为O(lgn)
* 伪代码:
* 1,int left=0,right=T.length-1
* 2,int index=(left+right)>>1
* 3,while(left!=right){
* if(T[index]>k){ //若中位数比k大,则说明k在中卫数之前,需要将right指针左移一位
* right=index-1;
* index=(left+right)>>1;}
* else if(T[index]==k) //若中位数与k相等,则返回找到
* return true
* else{ //若中位数比k小,则说明k在中位数后面,需要将left指针加一
* left=index+1
* index=(left+right)>>1}
* }
* @param Targent目标有序数组
*/
public boolean divideQuery2(int[] Targent,int k){
int left=0;
int right=Targent.length-1;
int index=(right+left)>>1;
//此处找到循环条件需要注意left<=right为正确,不能选择left!=right否则
//当查找到left==right时,循环结束,该条件下没有判断
while(left<=right){
if(Targent[index]>k){
//注意找到中位数之后,right指针需要变动
right=index-1;
index=(left+right)>>1;
}else if(Targent[index]==k)
return true;
else{
//注意找到中位数之后,left指针需要变动
left=index+1;
index=(left+right)>>1;
}
}
return false;
}
方法二,可以使用求互补数组的方法,对于升序排列的数组,求出其互补数组,若存在两个数相加等于目标数,则在这两个数组之中一定存在相等的两个数。
/*
* 求互补数组的方法
* 算法时间复杂度为O(n),但是需要求出互补数组,故空间复杂度为O(n)
* 1,若有序数组T中存在两个数a,b 使得a+b=k成立
* 2,作数组D=k-T[i],则a,b两数一定在数组D中存在
* 3,定义两个整形变量i,j,使i从T数组左边开始遍历,j从D数组右边开始遍历,若找到相等的数,则返回找到,一直到i=j
* 例如,若数组T={1,2,4,7,11,15},若k=15,则数组D={14,13,11,8,4,0},4+11=15成立,故若4在数组T中,则在D中也一定存在
* 伪代码:
* 1,int T,
* 2,int D=k-T[i] i=0->T.length;
* 3,while(i!=j)
* if(T[i]>D[J])
* J++;
* if(T[i]==D[j])
* return T[i],k-T[i]
* if(T[i]<D[j])
* i++
* return -1
* @param Targent为无序或者有序数组
* @param k为输入和值
* @param 返回值为二维数组
*/
public int[] twoSum3(int[] Targent,int k){
int[] result=new int[2];
//计算数组T的差分数组,时间复杂度为O(n)
int[] minus=new int[Targent.length];
for(int i=0;i<Targent.length;i++){
minus[i]=k-Targent[i];
}
//定义两个指针
int i=0;
int j=Targent.length-1;
while(i!=j){ //时间复杂度为O(n),遍历指针i+j<=n
if(Targent[i]==minus[j]){
result[0]=Targent[i];
result[1]=k-Targent[i];
return result;
}
if(Targent[i]>minus[j])
j--;
if(Targent[i]<minus[j])
i++;
}
return result;
}
方法三,该算法在升序排列的基础上,为最好的算法。
/*
* 最好的算法,
* 要求数组为有序数组,此时时间复杂度为O(n),否则先进行排序,时间复杂度为O(NLGN)+O(N)
* 寻找时间复杂度为O(N),空间复杂度为O(1)的算法
* 分析:
* 1,对于数组T,若数组无序,则首先对数组进行排序,时间复杂度为O(nlgn)
* 2,定义两个指针,left=0,right=T.length-1分别从数组的两端遍历数组
* 3,若T[left]+T[right]>K,则使right指针左移一位,减小两个数的和值
* 4,若T[left]+T[right]<K,则使left指针右移一位,增加两个数的和值
* 5,当指针left=right时,结束循环判断
* @param Targent为有序数组
* @param k为输入的数
* @param 返回值为一个二维数组
*/
public int[] twoSum5(int[] Targent,int k){
//定义存放结果的数组
int[] result=new int[2];
//定义两个指针
int left=0;
int right=Targent.length-1;
//循环控制,判断T[left]+T[right]与K值的大小
while(left<right){
if(Targent[left]+Targent[right]>k)
right--;
else if(Targent[left]+Targent[right]<k)
left++;
else {
result[0]=Targent[left];
result[1]=Targent[right];
return result;
}
}
return result;
}