题目:
分析:
解法一:暴力O(n^2)时间复杂度求解。
/**
* 解法1:时间复杂度O(n^2),空间复杂度O(1)
* 遍历求两数之和等于target,返回两数下标(从1开始)
* http://www.lintcode.com/zh-cn/problem/two-sum/
* @author yzwall
*/
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] results = new int[2];
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
results[0] = i + 1;
results[1] = j + 1;
return results;
}
}
}
return results;
}
}
解法二:双指针O(nlog(n))时间复杂度求解
思路:首先保存数组排序前的元素位置,可以使用hashmap保存(空间复杂度为O(n)),然后将数组排序(时间复杂度为O(nlogn),然后通过双指针分别指向排序后的数组头和尾,两端同时遍历,如果两指针数组元素之和小于target,则头指针往后移,若之和大于target,则尾指针往前移,若之和正好等于target,则输出下标位置即可。注意,可能存在数组中有相同元素的情况,即对应于hashmap中同key值下有多value的情况,用list保存,故在取出时,应同时删除对应list中的记录。同时要保证第一个下标小于第二个下标。
复杂度分析:时间复杂度O(nlog(n)),空间复杂度O(n).
//解法一:双指针O(nlog(n))时间复杂度求解
public int[] twoSum(int[] numbers, int target) {
// write your code here
if(numbers.length==0) return null;
int[] num=new int[2];
//通过hashmap记录数组排序前的下标
HashMap<Integer,ArrayList<Integer>> hashMap=new HashMap<>();
for(int i=0;i<numbers.length;i++){
if(hashMap.containsKey(numbers[i])){
hashMap.get(numbers[i]).add(i);
}else{
hashMap.put(numbers[i],new ArrayList<>());
hashMap.get(numbers[i]).add(i);
}
}
Arrays.sort(numbers); //排序
int low=0;
int high=numbers.length-1;
while(low<high){
int sum=numbers[low]+numbers[high];
if(sum==target){
int index1=hashMap.get(numbers[low]).get(0);
//把上一步取出的值从hashmap中去掉,防止数组中存在相同元素的情况,这种情况下不去除掉就会取得同样的下标值
hashMap.get(numbers[low]).remove(0);
int index2=hashMap.get(numbers[high]).get(0);
//保证index1<index2;
num[0]=Math.min(index1,index2);
num[1]=Math.max(index1,index2);
return num;
}else if(sum>target){
high--;
}else{
low++;
}
}
return num;
}
解法三:HashMap的O(n)时间复杂度求解
思路:耗费O(n)空间构造哈希表,遍历数组每个元素nums[i],哈希表对应存储<target-nums[i],i>,存储nums[i]期望的另一半,一旦哈希表中包含nums[i],代表“另一半”已经存储在哈希表中,直接返回即可。
复杂度分析:时间复杂度O(n),空间复杂度O(n).
//解法二:HashMap O(n)时间复杂度求解
public int[] twoSum1(int[] numbers, int target) {
if(numbers.length==0) return null;
int[] num=new int[2];
HashMap<Integer,Integer> hashMap=new HashMap<>();
for(int i=0;i<numbers.length;i++){
if(hashMap.containsKey(numbers[i])){
int index1=hashMap.get(numbers[i]);
int index2=i;
//保证index1<index2;
num[0]=Math.min(index1,index2);
num[1]=Math.max(index1,index2);
return num;
}else{
hashMap.put(target-numbers[i],i);
}
}
return num;
}
题目:
分析:这题只需要返回三个整数即可,不需要返回下标,所以直接排序即可。整个题目的解题思路同二数之和差不多。通过控制一个指针i不变,其余两个指针分别指向j和r分别指向i+1和数组最后一个位置,也就是双指针的方法,target为0-指针i指向的值。算法如下:
public class threeSum {
/**
* @param numbers: Give an array numbers of n integer
* @return: Find all unique triplets in the array which gives the sum of zero.
*/
//三数之和
//给出一个有n个整数的数组S,在S中找到三个整数a, b, c,找到所有使得a + b + c = 0的三元组。
public List<List<Integer>> threeSum(int[] numbers) {
// write your code here
List<List<Integer>> list=new ArrayList<>();
if(numbers==null || numbers.length<3) return list;
Arrays.sort(numbers);
for(int i=0;i<numbers.length;i++){
//跳过与当前i元素相同的元素
if(i>0 && numbers[i]==numbers[i-1])
continue;
int l=i+1;int r=numbers.length-1; //类似于双指针
int target=-numbers[i];
twosum(numbers,l,r,target,list);
}
return list;
}
//求两数之和
public void twosum(int[] numbers,int left,int right,int target,List<List<Integer>> lists){
while(left<right){
int sum=numbers[left]+numbers[right];
if(sum==target){
List<Integer> list=new ArrayList<>();
list.add(-target);
list.add(numbers[left]);
list.add(numbers[right]);
lists.add(list);
left++;
right--;
//跳过与当前left元素相同的元素
while(left<right && numbers[left]==numbers[left-1]){
left++;
}
while(left<right && numbers[right]==numbers[right+1]){
right--;
}
}else if(sum<target){
left++;
}else{
right--;
}
}
}
题目:
分析:和三数之和思想类似,只不过这次先固定两个指针,两个for循环确定首尾两个指针后,通过调整剩下两个指针来与target比较。
public class Solution {
/**
* @param numbers: Give an array
* @param target: An integer
* @return: Find all unique quadruplets in the array which gives the sum of zero
*/
public List<List<Integer>> fourSum(int[] numbers, int target) {
// write your code here
List<List<Integer>> list=new ArrayList<>();
if(numbers==null || numbers.length<3) return list;
Arrays.sort(numbers);
for(int i=0;i<numbers.length;i++){
//跳过与当前i元素相同的元素
if(i>0 && numbers[i]==numbers[i-1])
continue;
for(int j=numbers.length-1;j>i;j--) {
if(j<numbers.length-1 && numbers[j]==numbers[j+1])
continue;
int l = i + 1;
int r = j - 1; //类似于双指针
int subtarget = target-numbers[i]-numbers[j];
twosum(numbers, l, r,i,j,subtarget, list);
}
}
return list;
}
//求两数之和
public void twosum(int[] numbers,int left,int right,int fixI,int fixJ,int target,List<List<Integer>> lists){
while(left<right){
int sum=numbers[left]+numbers[right];
if(sum==target){
List<Integer> list=new ArrayList<>();
list.add(numbers[fixI]);
list.add(numbers[left]);
list.add(numbers[right]);
list.add(numbers[fixJ]);
lists.add(list);
left++;
right--;
//跳过与当前left元素相同的元素
while(left<right && numbers[left]==numbers[left-1]){
left++;
}
while(left<right && numbers[right]==numbers[right+1]){
right--;
}
}else if(sum<target){
left++;
}else{
right--;
}
}
}
}
题目:
分析:和三数之和思想类似,将数组排序后,通过固定一个元素,调整另外两个来得到三数之和。用一个变量存储当前找到的最接近target的三数之和。
public class Solution {
/**
* @param numbers: Give an array numbers of n integer
* @param target: An integer
* @return: return the sum of the three integers, the sum closest target.
*/
public int threeSumClosest(int[] numbers, int target) {
// write your code here
if(numbers==null || numbers.length<3) return -1;
Arrays.sort(numbers);
int min=Integer.MAX_VALUE;
for(int i=0;i<numbers.length;i++){
//跳过与当前元素相同的元素
if(i>0 && numbers[i]==numbers[i-1]) continue;
int l=i+1; int r=numbers.length-1; //类似于双指针
while(l<r){
int sum=numbers[l]+numbers[r]+numbers[i];
if(Math.abs(sum-target)<Math.abs(min-target)) min=sum;
if(sum<target) l++;
else r--;
}
}
return min;
}
}
分析:和求三数之和一样,首先确定x、y、z的取值范围,可以肯定小于sqrt(n)。固定x后,分别变动y和z指针。
package Array;
public class threeSum2 {
/**
* @param n: an integer
* @return: the number of solutions
*/
//输入n,求所有符合x^2+y^2+z^2 = n的 x, y, z 的方案数。(x, y, z为非负整数)
public int threeSum2(int n) {
// Write your code here
if(n<0) return 0;
int m=(int)Math.sqrt(n);
if(m*m>n){
m--;
}
int ans=0;
for(int i=0;i<=m;i++){
int res=n-i*i;
int left=i,right=m;
while(left<=right){
int temp=left*left+right*right;
if(temp<res){
left++;
}else if(temp>res){
right--;
}else{
ans++;
left++;
}
}
}
return ans;
}
}