I'm stuck on how to find the max value of an array recursively in javascript. I tried it first iteratively and of course it works. recursively, I wanted to try it with a subroutine first, and then without a subroutine. What's the best way to call the subroutine inside itself? Where I am getting tripped up is that I want to look inside the indices, but currently my subroutine accepts an array as a parameter.
function maxValue(arr) {
var max = 0;
for (var i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
console.log(maxValue([2, 7, 8, 3, 1, 4])); //returns 8
function maxValueRecursive(arr) {
var length = arr.length; //6
var max = 0;
function doSearch(arr) {
if (arr.length === 1) {
max = arr[0];
} else { // true
max = Math.max(arr[length - 1], arr[length - 2]);
//max = Math.max(4, doSearch()) = 4.
}
return max;
}
return doSearch(arr);
}
console.log(maxValueRecursive([2, 7, 8, 3, 1, 4])) //returns 4
You can use Math.max and solve for a smaller bit of the array at each step. The trick is to remove some (any) item from the array and use Math.max to compare the item with findmax on the smaller array:
function findmax(arr){
if (arr.length == 1){
// base case - single item in array is always max
return arr[0];
}
// recursive call on smaller problem (shorter array)
return Math.max(arr[0], findmax(arr.slice(1)))
}
I used slice but you can use pop or whatever method to remove an item and compare it using Math.max as I did.
For the array [1, 4, 2, 3] the recursion unfolds as follows:
1. findmax([1, 4, 2, 3]
2. Math.max(1, findmax([4, 2, 3]))
3. Math.max(1, Math.max(4, findmax([2, 3])))
4. Math.max(1, Math.max(4, Math.max(2, findmax([3]))))
5. Math.max(1, Math.max(4, Math.max(2, 3))) // 4
For doing it without a subroutine, consider that the maximum of a one-array element is the element itself.
You want to break down the problem such that you can get the input array down to one element, and then start working backwards and checking if there are any larger elements.
Some pseudocode to get you started:
max (A[1, 2, ..., n])
if n = 1
return A[1]
else
oldMax <- max([2, ..., n])
if A[1] > oldMax
return A[1]
else
return oldMax
This may be a bit intimidating at first, but here's an explanation. We know how to get the maximum of an array with one element, so we keep doing the recursive call until we have an array with one element, But here's the trick: the array we pass into the recursive call is the same array, but with the first element removed. This way, we're making the array smaller and smaller until there's only one element left. Then, we know that the maximum is the element itself.
So we get the maximum of the one-element array, and we return that value. Now we're peeling back each layer of recursion as we try to find the biggest value.
Say the array is [1, 2, 3, 4]. Then the function will keep making recursive calls like this:
max([1, 2, 3, 4])
max([2, 3, 4])
max([3, 4])
max([4]) -> 4
At the last stage, we can return 4 since that's the maximum. But now we're back at the stage where the array is [3, 4]. At this stage, we will check to see if the first element, 3, is bigger than the maximum we computed from the recursive call, 4. In this case, it isn't, so we keep returning 4.
We repeat this until we're back at the first layer, where we finally return 4 since 1 is not greater than the maximum so far, 4. Since none of the elements in the array are greater than 4, the maximum never changed.
I know it feels weird at first, but calling a function from within itself is as simple as... well, calling it from within itself!
The below function takes the array as the first parameter, the next two parameters are the indices of the max value at that point in the search and the next value to compare the max value to respectively. You start off by using 0 and 1 and then let the function take over from there.
var findMax = function (array, curMaxIndex, nextIndex) {
if (array[nextIndex] > array[curMaxIndex]) {
curMaxIndex = nextIndex;
}
if (nextIndex > array.length) {
return array[curMaxIndex];
}
return findMax(array, curMaxIndex, nextIndex + 1);
}
var max = findMax([2, 7, 8, 3, 1, 4, 9], 0, 1);
console.log(max); //9
You could also set up default values, so the original call is more simple:
var findMax = function (array, curMaxIndex, nextIndex) {
curMaxIndex = typeof curMaxIndex !== 'undefined' ? curMaxIndex : 0;
nextIndex = typeof nextIndex !== 'undefined' ? nextIndex : 1;
if (array[nextIndex] > array[curMaxIndex]) {
curMaxIndex = nextIndex;
}
if (nextIndex > array.length) {
return curMaxIndex;
}
return findMax(array, curMaxIndex, nextIndex + 1);
}
var max = findMaxIndex(array);
The best way though is to use the built in Math.max(), which it appears you've already heard of, so I'm assuming this is just a learning exercise.
Related
I need to create a function that take as parameter an array and a target. It should return an array of arrays where the sum of these numbers equals to the target
sumPairs(array, target) {
}
For example:
sumPairs([1, 2, 3, 4, 5], 7) // output : [[2, 5], [3, 4]]
I know I have to use map(), and probably reduce(), set(), or filter() maybe (I read their documentation in MDN but still cant find out). I tried some ways but I can't get it.
If you guys could help me to find out how to dynamically create arrays and push them into a new array..
I read there some solutions (Split array into arrays of matching values) but I hate to just use created functions without knowing what they really do or how they work.
Some very basic code for achieving it, Just run all over combinations and conditionally add the items you want.
function sumPairs(array, target) {
var res = [];
for(var i = 0; i < array.length; i++){
for(var j = 0; j < array.length; j++){
if(i!=j && array[i]+array[j]==target &&
res.filter((x)=> x[0] == array[j] && x[1] == array[i]).length == 0 )
res.push([array[i], array[j]]);
}
}
return res;
}
var result = sumPairs([1, 2, 3, 4, 5], 7);
console.log(result);
Option 2 - see this answer for more options (like using reduce)
function sumPairs(array, target) {
return array.flatMap(
(v, i) => array.slice(i+1).filter(w => (v!=w && v+w==target)).map(w=> [w,v])
);
}
var result = sumPairs([1, 2, 3, 4, 5], 7);
console.log(result);
"The exercise says that it sould be arrays of pairs that sum the
target value so I think only 2 items"
If you need a pair that matches a sum and you pick any number from the list, you are left with
the following equation to solve num + x = sum where we want to find x. E.g. if you picked 7 and the target sum is 10 then you know you are looking for a 3.
Therefore, we can first construct a counting map of the numbers available in our list linear (O(n)) time and then search for matches in linear time as well rather than brute forcing with a quadratic algorithm.
const nums = [1, 2, 3, 4, 5];
console.log(findSumPairs(nums, 7));
function findSumPairs(nums, sum) {
const countByNum = countGroupByNum(nums);
return nums.reduce((pairs, num) => {
countByNum[num]--;
const target = sum - num;
if (countByNum[target] > 0) {
countByNum[target]--;
pairs.push([num, target]);
} else {
countByNum[num]++;
}
return pairs;
}, []);
}
function countGroupByNum(nums) {
return nums.reduce((acc, n) => (acc[n] = (acc[n] || 0) + 1, acc), {});
}
Here's another implementation with more standard paradigms (e.g. no reduce):
const nums = [1, 2, 3, 4, 5];
console.log(findSumPairs(nums, 7));
function findSumPairs(nums, sum) {
const countByNum = countGroupByNum(nums);
const pairs = [];
for (const num of nums) {
const target = sum - num; //Calculate the target to make the sum
countByNum[num]--; //Make sure we dont pick the same num instance
if (countByNum[target] > 0) { //If we found the target
countByNum[target]--;
pairs.push([num, target]);
} else {
countByNum[target]++; //Didin't find a match, return the deducted num
}
}
return pairs;
}
function countGroupByNum(nums) {
const countByNum = {};
for (const num of nums) {
countByNum[num] = (countByNum[num] || 0) + 1;
}
return countByNum;
}
You can also sort your array and find all the pairs with given sum by using two pointer method. Place the first pointer to the start of the array and the second pointer to the end.
if the sum of the values at the two places is :
More than target: Decrement your second pointer by 1
Less than target: Increment your first pointer by 1
Equal to target: This is one possible answer, push them to your answer array and increment your first pointer by 1 and decrement your second pointer by 1.
This is more performant solution with complexity O(n*log(n))
Question
You are going to be given an array of integers. Your job is to take that array and find an index N where the sum of the integers to the left of N is equal to the sum of the integers to the right of N. If there is no index that would make this happen, return -1.
For example:
Let's say you are given the array {1,2,3,4,3,2,1}: Your function will return the index 3, because at the 3rd position of the array, the sum of left side of the index ({1,2,3}) and the sum of the right side of the index ({3,2,1}) both equal 6.
Answer
function findEvenIndex(arr){
for(let i = 0; i <arr.length; i++){
let arr1 = arr.slice(0, (arr[i] - 1));
let arr2 = arr.slice((arr[i] + 1),);
let arr11 = arr1.reduce((total, item)=>{
return total + item;
}, 0);
let arr22 = arr2.reduce((total, item)=>{
return total + item;
}, 0);
if(arr11 === arr22){
return arr[i];
}
}
return -1;
}
console.log(findEvenIndex([1, 2, 3, 4, 3, 2, 1]))
console.log(findEvenIndex([1, 100, 50, -51, 1, 1]))
console.log(findEvenIndex([1, 2, 3,4,5,6]))
I can't see an error here, but it yields incorrect results. Any ideas?
You have this part:
let arr1 = arr.slice(0, (arr[i] - 1));
let arr2 = arr.slice((arr[i] + 1),);
This is incorrect: arr[i]. That is a value, eg in [2,4,6,8,10] arr[3]==8. You want to slice on the index itself:
let arr1 = arr.slice(0, i - 1);
let arr2 = arr.slice(i + 1,);
Please note: There is another error in the two lines :) I leave that to you. Hint: you're now slicing two values out of the array instead of one. Perform the following code in your head first, then somewhere where you verify your results.
let arr = [0,1,2,3,4]
let x = 2;
console.log(arr.slice(0, x - 1));
console.log(arr.slice(x + 1,));
You could also use the array method findIndex, which, we shouldn't be surprised to learn, finds an index in an array subject to a certain condition.
const sum = (ns) =>
ns .reduce ((total, n) => total + n, 0)
const findBalancedIndex = (ns) =>
ns .findIndex ((_, i) => sum (ns.slice (0, i)) === sum (ns.slice (i + 1)))
console .log (findBalancedIndex ([1, 2, 3, 4, 3, 2, 1]))
console .log (findBalancedIndex ([1, 100, 50, -51, 1, 1]))
console .log (findBalancedIndex ([1, 2, 3, 4, 5, 6]))
Here we include a simple helper function to find the sum of an array, and then we pass a function to findIndex which uses it twice on the elements before the index and those after it. We use the second parameter of the callback function, the index to do this. This means we are skipping the first parameter altogether, and rather than naming it with something like n, we use the somewhat common convention of calling it _, signalling a placeholder we won't use. Note that you don't need to subtract one from the right-hand boundary of slice, since that boundary value is already excluded. And of course, others have pointed out that you need to slice to the index and not the array value at that index.
This finds the first correct index. You would have to use a different technique if you wanted to find all such indices. (That it's possible to have more than one should be clear from arrays like [1, 2, 3, 0, 0, 0, 0, 3, 2, 1] -- the indices for all those 0s would work.)
you return arr[i] when you need to return just i
Instructions
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].
How can I refactor this to eliminate the nested for-loop? I'd like to get the time complexity down.
Code
const twoSum = function(nums, target) {
for(let i in nums){
for(let j in nums) {
if(nums[i] + nums[j] === target && nums[i] != nums[j]) {
return [i, j];
}
}
}
};
console.log(twoSum([2, 7, 11, 15], 9));
You can save the difference of each element with the target inside an object with the result as keys and the index as values. This will make checking for the existence of an element inside an object without looping though the whole content. In a different loop check if the array elements exist in the object, if they do then you have got the pair. The additional condition is to prevent comparing an element with itself.
const twoSum = function(nums, target) {
const temp = {};
for(let i=0; i<nums.length; i++) {
temp[target - nums[i]] = i;
}
for(let i=0; i<nums.length-1; i++) {
if(temp[nums[i]] && temp[nums[i]] !== i) {
return [i, temp[nums[i]]]
}
}
};
console.log(twoSum([2, 11, 7, 17], 9));
console.log(twoSum([1, 3, 4, 2], 6));
Since this appears to be homework, I'll make a couple suggestions without giving away a complete solution:
Your current code is repeating index checks. For example, you're looping over indices [0,1] and [1,0], which will always have the same sum since a+b = b+a. Instead, I would suggest your loop for i go from 0 to len-1, and your loop for j go from i+1 to len-1. That way you will never duplicate checks.
Part of your current check includes the condition that nums[i] != nums[j], but your problem doesn't state that two values in the array can't be the same. Is it possible to call this function with values like toSum([1, 4, 4], 8) such that 4+4=8? If so, then you can remove the nums[i] != nums[j] check to save time.
It's not clear if the array provided is sorted. If it's not, then you could create a tracking variable to account for values you've already checked, and prevent checking them on future iterations. For example, if you already compared the value 4 against all other values in the array and found no solution, then if you encounter 4 later in the array, there is no reason to check it.
You can solve this problem with O(n) time. The condition is to solve by this approach is that the array must be sorted.
let twosum = (arr, x) => {
let s = 0,
e = arr.length - 1;
let loc = [];
while (s < e) {
if (arr[s] + arr[e] === x) {
loc.push([s,e]);
s++;
e--;
} else if (arr[s] + arr[e] < x) {
s++;
} else {
e--;
}
}
return loc;
};
console.log(twosum([1, 2, 3, 4, 5, 7, 8], 9));
console.log(twosum([2, 7, 11, 15], 9));
The algorithm behind this if anyone interested:
1. Set s value as 0
2. Set e value as last index say (arr.length - 1)
3. While s is less than e i.e not pass one another
4. Check if arr[s] + arr[e] === x then we find it.
4.1. increment s value by 1 as there is no possibility to find any combination before the current s value
4.2. decrement e value by 1 as there is no possibility to find any combination after the current e value
4.3. collect the indexes where the match found.
5. If arr[s] + arr[e] < x
5.1 increment s as there is no possibility to find any combination before the current s value. But there still has the possibility for the e value to get a match.
6. If arr[s] + arr[e] > x
6.1 decrement e as there is no possibility to find any combination after the current e value. But there still has the possibility for the s value to get a match.
Giving an array, say [4,2,1,3,5], based on this array, we have a new array, which each number shows the count of elements on its left that are bigger than itself, which is [0,1,2,1,0]. Now write a function with given input of [0,1,2,1,0], return the original array. The range of array is 1 ~ n (n is the size of the array, you can assume all numbers in the original array are consecutive if sorted)
Now to recover the original array, I have tried a way to solve the problem by iterating through the range of array from the end to the front like this:
My approach:
say the range is 1 ~ 5, the original array would be [1, 2, 3, 4, 5] if sorted. Iterate from the end to the beg,
so first 5, there is no element can be bigger than 5, so its maximum count of bigger elements would be 0, then 4 would have 1 as its maximum count of bigger elements, 3 to 2, etc. Store the key-value pairs into an object.
Now iterating through the input from back to front,
0 -> 5
1 -> can be 4, 3, or 2
2 -> can be either 3, 2, or 1
1 -> any number bigger than the first one.
0 -> (can be anything, since 5 is taken, so it can be either 1, 2, 3, or 4)
Simply to map each element of the input as value to its key from the map is not enough. What would be an intuitive way to approach this with optimal performance? (avoiding O(n ^2) if possible.)
Initially make an AVL Tree from numbers 1 to n.
Start from rear i.e. at nth index (considering 1 based index).
Now the high level outline level of the algorithm should look like this:
1. At any ith index, say the number in array(not the originial array) is j
2. Search the number which is at (i-j)th position in your AVL tree(This can be done in O(logn) time. Comment if you need more explanation on this)
3. The element in the AVL tree is your required element. Delete that element from AVL tree.(O(logn))
So the total complexity would be O(nlogn).
Walkthrough
Initially the tree will contain all 5 elements.
You start at index 5(1-based indexing). Element is 0, i.e. i=5, j=0. So 5th largest element which is 5.
Now the tree contains four elements 1,2, 3, and 4. i=4, j=1. So 4-1 i..e 3rd largest element which is 3 in this case.
i=3, j=2. (3-2)rd largest element is 1 since the tree contains (1, 2, 4).
And so on.
Using Tree to find the ith largest number
We can do this by, storing the count of number of nodes in left subtree at the root node. So consider a tree, having elements 1, 2, 3,4 and 5 and tree structure as following:
4(3)
/ \
3(1) 5(0)
/ \
1(0) 2(0)
At root, number 4 is the value and the number in round bracket has the number of nodes in left subtree.
While constructing(insertion and deletion too) the tree, we can maintain the count.
Now, to find the ith node, say we want suppose 3rd nodes in the given tree. We start with the root, it says it has 3 elements smaller than it to the left so we move to left. Now the root i.e. 3 has 1 smaller left element which is less than 3(ith element) so we move to right of it. Subtract 1(the left count)+1(the root itself) out of 3. Now the root is 2 we want 1st element, the left count is 0. Hence the 1st element of the subtree rooted at 2 is 2.
Basic pseudocode is below:
while(true){
count = root->leftCount;
if((count+1)<i){
//move to right
i-=(count+1);
root = root->right;
}
else if(i==(count+1)){
//root is the ith node
break;
} else{
//move to the levft
root=root->left
}
}
You could use Array#reduceRight and use the value as negative index for generating the original array.
var array = [1, 2, 3, 4, 5],
countLeft = [0, 1, 2, 1, 0],
result = countLeft.reduceRight(function (r, a) {
return array.splice(array.length - 1 - a, 1).concat(r);
}, []);
console.log(result);
Shorter version with ES6 and reverse base array.
var array = [5, 4, 3, 2, 1],
countLeft = [0, 1, 2, 1, 0],
indices = array.map((_, i) => i),
result = [];
countLeft.forEach(a => {
result.unshift(array[indices[a]]);
indices = indices.filter((_, i) => i !== a);
});
console.log(result);
At last a proposal with complexity between O(n*(n-1)/2) and O(n).
This version uses a lazy array with progressive reduction of the length for every iteration. At the end, the offset array has zero elements.
var array = [5, 4, 3, 2, 1],
countLeft = [0, 1, 2, 1, 0],
result = [],
length = array.length;
countLeft.forEach((offset => (offset.length = countLeft.length, a => {
var i = offset[a] || 0;
result.unshift(array[i + a]);
offset.length--;
while (i < offset.length) {
offset[i] = (offset[i] || 0) + 1;
i++;
}
}))([]));
console.log(result);
A linear version, heavily inspired by the proposal of Oriol
var array = [1, 2, 3, 4, 5],
countLeft = [0, 1, 2, 1, 0],
swap = [],
i = 0,
l,
temp;
while (i < countLeft.length) {
l = countLeft[i];
while (l) {
swap.push(i + l - countLeft[i]);
l--;
}
i++;
}
i = swap.length;
while (i--) {
temp = array[swap[i]];
array[swap[i]] = array[swap[i] - 1];
array[swap[i] - 1] = temp;
}
console.log(array);
Here is a possible solution. See inline comments for a brief description of this method.
var a = [0,1,2,1,0],
n, b = [], res = [];
// build b = [5,4,3,2,1]
// we use this array to keep track of values to be pushed in res[],
// sorted in descending order
for(n = a.length; n > 0; n--) {
b.push(n);
}
// for each element of a, starting from the end:
// find correct value in b and remove it from b
while(a.length) {
res.push(b.splice(a.pop(), 1)[0]);
}
res = res.reverse();
console.log(res);
Output:
[4, 2, 1, 3, 5]
I propose an approach based on a custom sort, based on mergesort:
Split the array of inversions into two halves
Sort each part recursively, from greatest to lowest, maintaining stability
Merge the two parts
The difference with mergesort is the merge part. If we choose the j-th element of right part instead of the i-th of the left one, it will advance some elements, and therefore its number of inversions must be reduced by that amount.
Like mergesort, the complexity is O(n log n)
function undoInversions(inversions) {
function reorder(arr, from=0, to=arr.length) {
// Based on a stable decreasing mergesort
if(from >= to) return []; // Unusual base case
if(to === from + 1) return [arr[from]]; // Base case
var m = Math.floor((from + to)/2);
var l = reorder(arr, from, m), // Left recursive call
r = reorder(arr, m, to), // Right recursive call
ret = [], i=0, j=0;
while(i < l.length && j < r.length) { // Merge
if(r[j].value - l.length + i >= l[i].value) {
r[j].value -= l.length - i; // Reduce number of inversions
ret.push(r[j++]);
} else {
ret.push(l[i++]);
}
}
while(i < l.length) ret.push(l[i++]); // Merge remaining, if any
while(j < r.length) ret.push(r[j++]); // Merge remaining, if any
return ret;
}
var array = new Array(inversions.length);
reorder(inversions.map(function(inv, idx) {
return {value: inv, originalIndex: idx}; // Keep track of indices
})).forEach(function(obj, idx) {
if(obj.value !== 0) throw 'Invalid input';
array[obj.originalIndex] = idx + 1; // Invert the permutation
});
return array;
}
console.log(JSON.stringify(undoInversions([0,1,2,1,0])));
Here is an example to understand how it works:
[0,1,2,1,0] ~ [4,2,1,3,5]
⤩ ⤧
[0,0,2,1,0] ~ [2,4,1,3,5]
⤩ ⤧
[0,1,0,1,0] ~ [2,1,4,3,5]
⤩ ⤧
[0,0,0,1,0] ~ [1,2,4,3,5]
⤩ ⤧
[0,0,0,0,0] ——→ [1,2,3,4,5]
That is, each array of inversions corresponds to a permutation. We apply a permutation σ which transforms the input to the array of inversions [0,0,0,0,0], which corresponds to the permutation [1,2,3,4,5]. Since we kept track of the original indices, now we only need the to apply σ⁻¹ to [1,2,3,4,5] in order to get the permutation corresponding to the input.
Is there a way to compare an integer against an array of integers? For instance, to determine if an int is larger than any of the array ints?
var array = [1, 2, 3, 4];
if(5 > array){
// do something
}
Update: I guess I meant, is 5 larger than the largest number in the array. Thanks!
You can use Math.max and apply
if (5 > Math.max.apply(Math, array)) {
// do something
}
Update: To explain as it works. It's described in the docs I linked but I will try to be more clear here:
Math.max returns the largest of zero or more numbers, so:
Math.max(1, 2, 3, 4) // returns 4
apply calls a function with a given 'this' value (the first argument) and arguments provided as an array (the second). so:
function sum(a, b) {
return a + b;
}
console.log(sum.apply(window, [2, 3])); // 5
Therefore, if you have an array of integers and you want to get the max, you can combine them to have:
console.log(Math.max.apply(Math, [1, 2, 3, 4])); // 4
Because it's exactly like have:
console.log(Math.max(1, 2, 3, 4));
The difference is you pass an array instead.
Hope it's more clear now!
There is no good and readable built in way of doing it, but it can be done simply with:
var bigger = true;
for (var i =0; i < array.length; i++) {
if (5 <= array[i]) {
bigger = false;
// you can add here : break;
}
}
Sure, you could sort the array, take the last element, and see if your integer is greater than that. Or, you could loop through and check each one. Up to you. The loop is a more performant way.
//maybe check the bounds on this if you know it can be a blank array ever
var max = myArray[0];
for(var x = 0; x < myArray.length; x++) {
max = Math.max(max, myArray[x]);
}
if(500 > max) {
//do something
}