The problem statement from leetcode says:
Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Fin
Your solution may have exceeded the time limit and therefore may have not been able to pass the test cases. Below is the code having the runtime O(n^3)
. Initially, we sort the array which will help us to find the element either by moving forward or backward depending on the number we are looking for. We then take the first element from the array and perform a three sum on the remaining part leading to find the two sum. Only the ArrayList
is used. HashMap
is not required.
public static List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(nums==null|| nums.length<4)
return result;
Arrays.sort(nums);
for(int i=0; i<nums.length-3; i++)
{
if(i!=0 && nums[i]==nums[i-1]) continue; //Avoid duplicate elements
for(int j=i+1; j<nums.length-2; j++)
{
if(j!=i+1 && nums[j]==nums[j-1]) continue; //Avoid duplicate elements
int k=j+1;
int l=nums.length-1;
while(k<l)
{
if(nums[i]+nums[j]+nums[k]+nums[l]<target)
{
k++;
}
else if(nums[i]+nums[j]+nums[k]+nums[l]>target)
{
l--;
}
else
{
List<Integer> t = new ArrayList<Integer>();
t.add(nums[i]);
t.add(nums[j]);
t.add(nums[k]);
t.add(nums[l]);
result.add(t);
k++;
l--;
while( k<l &&nums[l]==nums[l-1] ) l--;
while( k<l &&nums[k]==nums[k+1] ) k++;
}
}
}
}
return result;
}
public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
Arrays.sort(num);
ArrayList<ArrayList<Integer>> res=new ArrayList<ArrayList<Integer>>();
int i=0;
while(i<num.length-3){
int j=i+1;
while(j<num.length-2){
int left=j+1, right=num.length-1;
while(left<right){
if(num[left]+num[right]==target-num[i]-num[j]){
ArrayList<Integer> t=new ArrayList<Integer>();
t.add(num[i]);
t.add(num[j]);
t.add(num[left]);
t.add(num[right]);
res.add(t);
left++;
right--;
while(left<right && num[left]==num[left-1])
left++;
while(left<right && num[right]==num[right+1])
right--;
}else if(num[left]+num[right]>target-num[i]-num[j])
right--;
else
left++;
}
j++;
while(j<num.length-2 && num[j]==num[j-1])
j++;
}
i++;
while(i<num.length-3 && num[i]==num[i-1])
i++;
}
return res;
}
Here is an O(n^3) solution
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
public class Solution {
public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
// Start typing your Java solution below
// DO NOT write main() function
Arrays.sort(num);
HashSet<ArrayList<Integer>> hSet = new HashSet<ArrayList<Integer>>();
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
for (int i = 0; i < num.length; i++) {
for (int j = i + 1; j < num.length; j++) {
for (int k = j + 1, l = num.length - 1; k < l;) {
int sum = num[i] + num[j] + num[k] + num[l];
if (sum > target) {
l--;
}
else if (sum < target) {
k++;
}
else if (sum == target) {
ArrayList<Integer> found = new ArrayList<Integer>();
found.add(num[i]);
found.add(num[j]);
found.add(num[k]);
found.add(num[l]);
if (!hSet.contains(found)) {
hSet.add(found);
result.add(found);
}
k++;
l--;
}
}
}
}
return result;
}
}
You solution is obviously calculating correctly. But if a value appears several times in the input array there will be given "same" results. Maybe that was tested.
The "twoSum" of num = { 1, 3, 1, 3, 4 } with target = 4 would be:
It this was the reason for failing in tests, the solution would probably be a set of an ordered list.
If also subsets must be given, { 4 } is missing.
So the question is, why did the tests fail, what are the requirements.
An optimisation probably involves special data structures: at least a (reverse) sorted num. Many links are already given. Recursion can help to find an provable and understandable optimal solution, with pre and post conditions. (As you are splitting the problem.)
One solution would be be creating pairs keeping a mapping of pair sum to all pairs of indices leading to that sum. And then find two pairs with disjunct indices.
As no one gave a satisfactory answer:
A simple recursive solution (not very nice or optimal). However it demonstrates that data structures are relevant.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.SortedSet;
import java.util.TreeSet;
public class FourSumSolution {
private static class SortedTuple implements Comparable<SortedTuple> {
int[] values;
public SortedTuple(int... values) {
this.values = Arrays.copyOf(values, values.length);
Arrays.sort(this.values);
}
public int size() {
return values.length;
}
public int get(int index) {
return values[index];
}
public ArrayList<Integer> asList() {
// Result type is ArrayList and not the better List,
// because of the external API of the outer class.
ArrayList<Integer> list = new ArrayList<Integer>(values.length);
for (int value: values)
list.add(value);
return list;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(values);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SortedTuple other = (SortedTuple) obj;
if (!Arrays.equals(values, other.values))
return false;
return true;
}
@Override
public int compareTo(SortedTuple other) {
int c = cmp(values.length, other.values.length);
if (c == 0) {
for (int i = 0; i < values.length; ++i) {
c = cmp(values[i], other.values[i]);
if (c != 0)
break;
}
}
return c;
}
@Override
public String toString() {
return Arrays.toString(values);
}
private static int cmp(int lhs, int rhs) {
return lhs < rhs ? -1 : lhs > rhs ? 1 : 0; // Cannot overflow like lhs - rhs.
}
}
public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
final int nTerms = 4;
SortedTuple values = new SortedTuple(num);
SortedSet<SortedTuple> results = new TreeSet<SortedTuple>();
int[] candidateTerms = new int[nTerms];
int valuesCount = values.size();
solveNSum(target, nTerms, values, results, candidateTerms, valuesCount);
ArrayList<ArrayList<Integer>> aList = new ArrayList<ArrayList<Integer>>();
for (SortedTuple solution: results) {
aList.add(solution.asList());
}
return aList;
}
public static void main(String[] args) {
final int[] num = { 1, 3, 1, 3, 4 };
final int requiredSum = 4;
final int nTerms = 2;
SortedTuple values = new SortedTuple(num);
SortedSet<SortedTuple> results = new TreeSet<SortedTuple>();
int[] candidateTerms = new int[nTerms];
int valuesCount = values.size();
solveNSum(requiredSum, nTerms, values, results, candidateTerms, valuesCount);
System.out.println("Solutions:");
for (SortedTuple solution: results) {
System.out.println("Solution: " + solution);
}
System.out.println("End of solutions.");
}
private static void solveNSum(int requiredSum, int nTerms, SortedTuple values, SortedSet<SortedTuple> results, int[] candidateTerms, int valuesCount) {
if (nTerms <= 0) {
if (requiredSum == 0)
results.add(new SortedTuple(candidateTerms));
return;
}
if (valuesCount <= 0) {
return;
}
--valuesCount;
int candidateTerm = values.get(valuesCount);
// Try with candidate term:
candidateTerms[nTerms - 1] = candidateTerm;
solveNSum(requiredSum - candidateTerm, nTerms - 1, values, results, candidateTerms, valuesCount);
// Try without candidate term:
solveNSum(requiredSum, nTerms, values, results, candidateTerms, valuesCount);
}
}
One can prune this further:
public class FourSum {
private static int countFourSum(int[] numbers) {
int count = 0;
for (int j = 0; j < numbers.length; j++) {
for (int i = j + 1; i < numbers.length; i++) {
int front = i + 1, rear = numbers.length - 1;
while (front < rear) {
if (numbers[front] + numbers[rear] + numbers[i] + numbers[j] == 0) {
System.out.printf(String.format("{%d} : {%d} : {%d} : {%d} \n", numbers[front], numbers[rear],
numbers[j], numbers[i]));
front++;
rear--;
count++;
} else {
if (Math.abs(numbers[front]) > Math.abs(numbers[rear])) {
front++;
} else {
rear--;
}
}
}
}
}
return count;
}
public static void main(String[] args) {
int[] numbers = { 1, 2, 3, 4, -4, -5, -6, 2, 4, -1 };
Arrays.sort(numbers);
System.out.println(countFourSum(numbers));
}
}
Here's a solution that does not use hash. It should be faster, but it still exceeds the time limit.
import java.util.ArrayList;
public class Solution {
public static void main(String[] args) {
int[] S = {1, 0, -1, 0, -2, 2};
int target = 0;
test(S, target);
}
public static void test(int[] num, int target) {
System.out.print("a:");
for (int i : num) {
System.out.print(" " + i);
}
System.out.println(": " + target);
ArrayList<ArrayList<Integer>> res = fourSum(num, target);
for (ArrayList<Integer> list : res) {
System.out.println(list);
}
System.out.println();
}
// a+b+c+d = target
public static ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
// Start typing your Java solution below
// DO NOT write main() function
// Sort
{
for (int i = 0; i < num.length; i++) {
for (int j = i + 1; j < num.length; j++) {
if (num[j] < num[i]) {
int tmp = num[i];
num[i] = num[j];
num[j] = tmp;
}
}
}
}
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
int i = 0;
while (i < num.length - 3) {
int j = i + 1;
while (j < num.length - 2) {
int k = j + 1, l = num.length - 1;
while (k < l) {
int sum = num[i] + num[j] + num[k] + num[l];
if (sum > target) {
l--;
while (k < l && num[l] == num[l+1]) l--;
} else if (sum < target) {
k++;
while (k < l && num[k] == num[k-1]) k++;
} else {
ArrayList<Integer> list =
new ArrayList<Integer>(4);
list.add(num[i]); list.add(num[j]);
list.add(num[k]); list.add(num[l]);
res.add(list);
k++;
while (k < l && num[k] == num[k-1]) k++;
l--;
while (k < l && num[l] == num[l+1]) l--;
}
}
j++;
while (j < num.length && num[j] == num[j-1]) j++;
}
i++;
while (i < num.length && num[i] == num[i-1]) {
i++;
}
}
return res;
}
}