Reputation: 642
I want to loop through an array and then add each value to each other (except itself + itself) and if the sum of the two values that were looped through equals the second argument in my function, and the pair of values hasn't been encountered before, then remember their indices and, at the end, return the full sum of all remembered indices.
In other words, the problem statement is: given an array A of integers and a second value s that is a desired sum, find all pairs of values from array A at indexes i, j such that i < j and A[i] + A[j] = s, and return the sum of all indexes of these pairs, with the following restriction:
Example
For example, functionName([1, 4, 2, 3, 0, 5], 7)
should return 11 because values 4, 2, 3 and 5 can be paired with each other to equal 7 and the 11 comes from adding the indices of them to get to 11 where:
4 + 3 = 7
5 + 2 = 7
4 [index: 1]
2 [index: 2]
3 [index: 3]
5 [index: 5]
1 + 2 + 3 + 5 = 11
Example #2
functionName([1, 3, 2, 4], 4)
would only equal 1, because only the first two elements can be paired to equal 4, and the first element has an index of 0 and the second 1
1 + 3 = 4
1 [index: 0]
3 [index: 1]
0 + 1 = 1
This is what I have so far:
function functionName(arr, arg) {
var newArr = [];
for(var i = 0; i < arr.length; i++){
for(var j = i + 1; j < arr.length; j++) {
if((arr[i] + arr[j]) === arg ) {
newArr.push(i , j);
}
}
}
if(newArr.length === 0) {
return console.log(0);
}
return console.log(newArr.reduce(function(a,b){return a + b}));
}
functionName([1, 4, 2, 3, 0, 5], 7);
The problem I have is that it all works but I have the issue that once it finds a pair that equals the second argument, then it's not supposed to use the same value pairs again but mine does, e.g.:
if the array is [1,1,1]
and the second argument is 2
, the loop will go through and find the answer but it continues to search after it finds the sum and I only want it to use the pair [1, 1]
once, so if it finds a pair like this at indexes [0, 1]
then it should not include any other pair that contains the value 1
.
I was thinking that i could remove the rest of the values that are the same if more than 2 are found using filter leaving me with only 2 of the same value if there is in an array thus not having to worry about the loop finding a 1 + 1 twice but is this the best way to go about doing it?
I'm still new to this but looking forward to your comments
PS I'm planning on doing this using pure JavaScript and no libraries
Link to a JS fiddle that might make things easier to see what I have. https://jsfiddle.net/ToreanJoel/xmumv3qt/
Upvotes: 1
Views: 1049
Reputation: 683
This solution should have a time complexity of 0(n) or linear Much faster than two nested for-loops. This function will give you the two indices that add up to the target number. It can easily be modified to solve any other configuration of this problem.
var twoSum = function(nums, target) {
const hash = {}
for(let i = 0; i < nums.length; i++) {
hash[nums[i]] = i
}
for(let j = 0; j < nums.length; j++) {
let numToFind = target - nums[j]
if(numToFind in hash && hash[numToFind] !== j) {
return [hash[numToFind], j]
}
}
return false
};
console.log(twoSum([1,2,3,5,7], 5))
In Python:
def twoSum(self, nums: List[int], target: int) -> List[int]:
myMap = {}
for i in range(len(nums)):
myMap[nums[i]] = i
for j in range(len(nums)):
numToFind = target - nums[j]
if numToFind in myMap and myMap[numToFind] != j:
return [myMap[numToFind], j]
print(twoSum([1,2,3,5,7], 5))
In Java:
import java.util.*;
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for(Integer i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for(Integer j = 0; j < nums.length; j++) {
Integer numToFind = target - nums[j];
Integer myInt = map.get(numToFind);
if(map.containsKey(numToFind) && myInt != j) {
return new int[] {myInt , j};
}
}
return new int[] {0, 0};
}
}
System.out.println(twoSum([1,2,3,5,7], 5))
Upvotes: 0
Reputation: 7468
The solution below is very compact. It avoids unnecessary checks and loops only through the relevant elements. You can check the working codepen here: http://codepen.io/PiotrBerebecki/pen/RRGaBZ.
function pairwise(arr, arg) {
var sum = 0;
for (var i=0; i<arr.length-1; i++) {
for (var j=i+1; j<arr.length; j++) {
if (arr[i] <= arg && arr[j] <= arg && arr[i] + arr[j] == arg) {
sum += i+j;
arr[i] = arr[j] = NaN;
}
}
}
return sum;
}
console.log( pairwise([1, 1, 0, 2], 2) ) // should return 6
Under the hood:
i
) = 0.j
is always higher than i
as we are adding 1 to i
.arg
, check if their sum equals to the arg
. This avoids checking the sum if either of the numbers are greater than the arg
.NaN
to avoid further checks and duplication.Upvotes: 0
Reputation: 386540
Solution with loops with restart, if a sum is found. the found summands are stored in usedNumbers
and later sorted and used to get the index for summing the index.
The sorting and the last index
provides the correct start position for the Array.prototype.indexOf
.
Edit:
what about [1,1,1,1], 2 ... should that be 6 or 1? – Jaromanda X 21
@JaromandaX that should be 1, after the pair is found with the values then it shouldn't look for a pair with the same values again – Torean
This version takes care of the requirement.
function f(array, sum) {
var arrayCopy = array.slice(0),
usedNumbers = [],
index = 0,
indexA = 0,
indexB,
a, b;
while (indexA < arrayCopy.length) {
indexB = indexA + 1;
while (indexB < arrayCopy.length) {
a = arrayCopy[indexA];
b = arrayCopy[indexB];
if (a + b === sum) {
usedNumbers.push(a, b);
arrayCopy = arrayCopy.filter(function (i) { return a !== i && b !== i; });
indexA--; // correction to keep the index
break;
}
indexB++;
}
indexA++;
}
return usedNumbers.sort().reduce(function (r, a, i) {
index = array.indexOf(a, i === 0 || a !== usedNumbers[i - 1] ? 0 : index + 1);
return r + index;
}, 0);
}
document.write(f([1, 4, 2, 3, 0, 5], 7) + '<br>'); // 11
document.write(f([1, 1, 1], 2) + '<br>'); // 1
document.write(f([5, 5, 1, 1, 1], 6) + '<br>'); // 2
document.write(f([0, 5, 0, 5, 1, 1, 1], 6) + '<br>'); // 5
document.write(f([1, 1, 1, 1], 2) + '<br>'); // 1
Upvotes: 0
Reputation: 57774
This is more complicated than it initially looks. In fact, making a loop inside a loop causes the algorithm to have quadratic time complexity with regard to the size of the array. In other words, for large arrays of numbers, it will take a very long time to complete.
Another way to handle this problem is to notice that you actually have to use each unique value in the array only once (or twice, if s is even and you have two s/2 values somewhere in the array). Otherwise, you would have non-unique pairs. This works because if you need pairs of numbers x and y such that x + y = s, if you know x, then y is determined -- it must be equal s - x.
So you can actually solve the problem in linear time complexity (to be fair, it's sometimes n*log(n)
if all values in A are unique, because we have to sort them once).
The steps of the algorithm are as follows:
s - lower
)Here's the full code:
function findSumOfUniquePairs(numbers, sum) {
// First, make a map from values to lists of indexes with this value:
var indexesByValue = {},
values = [];
numbers.forEach(function (value, index) {
var indexes = indexesByValue[value];
if (!indexes) {
indexes = indexesByValue[value] = [];
values.push(value);
}
indexes.push(index);
});
values.sort();
var result = 0;
for (var i = 0, maxI = values.length; i < maxI; ++i) {
var lowerValue = values[i],
higherValue = sum - lowerValue;
if (lowerValue > higherValue) {
// We don't have to check symmetrical situations, so let's quit early:
break;
}
var lowerValueIndexes = indexesByValue[lowerValue];
if (lowerValue === higherValue) {
if (lowerValueIndexes.length >= 2) {
result += lowerValueIndexes[0] + lowerValueIndexes[1];
}
} else {
var higherValueIndexes = indexesByValue[higherValue];
if (higherValueIndexes) {
result += lowerValueIndexes[0] + higherValueIndexes[0];
}
}
}
return result;
}
document.write(findSumOfUniquePairs([1, 4, 2, 3, 0, 5], 7) + '<br>'); // 11;
document.write(findSumOfUniquePairs([1, 3, 2, 4], 4) + '<br>'); // 1
document.write(findSumOfUniquePairs([1, 1, 1], 2) + '<br>'); // 1
document.write(findSumOfUniquePairs([1, 1, 1, 1], 2) + '<br>'); // 1
document.write(findSumOfUniquePairs([1, 2, 3, 1, 2, 3, 1], 4) + '<br>'); // 7
document.write(findSumOfUniquePairs([5, 5, 1, 1, 1], 6) + '<br>'); // 2
document.write(findSumOfUniquePairs([0, 5, 0, 5, 1, 1, 1], 6) + '<br>'); // 5
Upvotes: 1
Reputation: 472
This works, but it mucks up the initial array.
function functionName(arr, arg) {
var newArr = [];
for(var i = 0; i < arr.length; i++){
for(var j = i + 1; j < arr.length; j++) {
if((arr[i] + arr[j]) === arg ) {
newArr.push(i , j);
arr[i] = null;
arr[j] = null;
}
}
}
if(newArr.length === 0) {
return console.log(0);
}
return console.log(newArr.reduce(function(a,b){return a + b}));
}
Upvotes: 0