First function:
concatenate string and integer into one string.
insert result string into an array.
join all array's strings into one string.
Second function does the same, but instead of concatenation, it inserts 2 strings in the array.
Question: How do you figure out what function will allocate less memory?
One more question: How many strings in memory (for each iteration) for first function we will have? For example, for 1st iteration we will have only "a0" or "a0", "a" and "0"?
function joinLetters() {
var arr = [];
for(var i = 0; i < 10000; i++) {
arr.push('a' + i);
}
return arr.join('');
}
function joinLetters2() {
var arr = [];
for(var i = 0; i < 10000; i++) {
arr.push('a');
arr.push(i.toString());
}
return arr.join('');
}
joinLetters in the inner loop makes a single push, joinLetters2 does two push instead.
So you will have in the first case arr.length = 10000, while in the second arr.length = 20000.
Definitely you can expect the second function to be more memory expensive then the first.
Related
I have a problem where, given an array of integers, I need to find sets of three numbers that add up to equal zero. The below solution works but isn't as optimal as I'd like and I am looking for ways to optimize it to avoid unnecessary processing.
What I am doing below is I am iterating through the all combinations of numbers while eliminating iterating through the same indices in each nested loop and I am checking if the three numbers in the inner most loop add up to zero. If yes, I am converting the array to a string and if the string isn't already in the results array I am adding it. Right before returning I am then converting the strings back to an array.
I appreciate any suggestions on how to further optimize this or if I missed out on some opportunity to implement better. I am not looking for a total refactor, just some adjustments that will improve performance.
var threeSum = function(nums) {
const sorted = nums.sort();
if(sorted.length && (sorted[0] > 0 || sorted[sorted.length-1] < 0)) {
return [];
}
let result = [];
for(let i=0; i < sorted.length; i++) {
for(let z=i+1; z < sorted.length; z++) {
for(let q=z+1; q < sorted.length; q++) {
if(sorted[i]+sorted[z]+sorted[q] === 0) {
const temp = [sorted[i], sorted[z], sorted[q]].join(',');
if(!result.includes(temp)) {
result.push(temp);
}
}
}
}
}
return result.map(str => str.split(','));
};
Sample Input: [-1,0,1,2,-1,-4]
Expected Output: [[-1,-1,2],[-1,0,1]]
One obvious optimisation is to precalculate the sum of the two first numbers just before the third nested loop. Then compare in the third loop if that number equals the opposite of the third iterated number.
Second optimisation is to take advantage of the fact that your items are sorted and use a binary search for the actual negative of the sum of the two first terms in the rest of the array instead of the third loop. This second optimisation brings complexity from O(N3) down to O(N2LogN)
Which leads to the third optimisation, for which you can store in a map the sum as key and as value, an array of the different pairs which sum to the sum so that each time you want to operate the binary search again, first you check if the sum already exists in that map and if it does you can simply output the combination of each pair found at that sum’s index in the map coupled with the negative sum.
The OP's solution runs in O(N³) time with no additional storage.
The classic "use a hash table" solution to find the missing element can bring that down to O(N²) time with O(N) storage.
The solution involves building a number map using an object. (You could use a Map object as well, but then you can't be as expressive with ++ and -- operators). Then just an ordinary loop and inner loop to evaluate all the pairs. For each pair, find if the negative sum of those pairs is in the map.
function threeSum(nums) {
var nummap = {}; // map a value to the number of ocurrances in nums
var solutions = new Set(); // map of solutions as strings
// map each value in nums into the number map
nums.forEach((val) => {
var k = nummap[val] ? nummap[val] : 0; // k is the number of times val appears in nummap
nummap[val] = k+1; // increment by 1 and update
});
// for each pair of numbers, see if we can find a solution the number map
for (let i = 0; i < nums.length; i++) {
var ival = nums[i];
nummap[ival]--;
for (let j = i+1; j < nums.length; j++) {
var jval = nums[j];
nummap[jval]--;
var target = -(ival + jval); // this could compute "-0", but it works itself out since 0==-0 and toString will strip the negative off
// if target is in the number map, we have a solution
if (nummap[target]) {
// sort this three sum solution and insert into map of available solutions
// we do this to filter out duplicate solutions
var tmp = [];
tmp[0] = ival;
tmp[1] = jval;
tmp[2] = target;
tmp.sort();
solutions.add(tmp.toString());
}
nummap[jval]++; // restore original instance count in nummap
}
nummap[ival]--;
}
for (s of solutions.keys()) {
console.log(s);
}
}
threeSum([9,8,7,-15, -9,0]);
var threeSum = function(unsortedNums) {
const nums = unsortedNums.sort();
if(nums.length && (nums[0] > 0 || nums[nums.length-1] < 0)) {
return [];
}
const result = new Map();
for(let i=0; i < nums.length; i++) {
for(let z=i+1; z < nums.length; z++) {
for(let q=z+1; q < nums.length; q++) {
if(nums[i]+nums[z]+nums[q] === 0) {
const toAdd = [nums[i], nums[z], nums[q]];
const toAddStr = toAdd.join(',');
if(!result.has(toAddStr)) {
result.set(toAddStr, toAdd);
}
}
}
}
}
return Array.from(result.values());
};
Write a function that takes in a non-empty array of distinct integers and a target integer.
Your function should find all triplets in the array that sum up to the target sum and return a two-dimensional array of all these triplets.
Each inner array containing a single triplet should have all three of its elements ordered in ascending order
ATTEMPT
function threeNumberSum(arr, target) {
let results = [];
for (let i = 0; i < arr.length; i++) {
let finalT = target - arr[i];
let map = {};
for (let j = i+1; j < arr.length; j++) {
if (map[arr[j]]) {
results.push([arr[j], arr[i], map[arr[j]]]);
} else {
map[finalT-arr[j]] = arr[j];
}
}
}
return results;
}
My code is formatted all funny, but right now im not getting any output. Am I missing a console log somewhere or something?
Your problem is that you read input wrong.
Pay attention to the last part of question: How to Read Input that is Used to Test Your Implementation
You wrote a function that takes the array as first arg and the target integer as the second one. But the input is entered one by one, so your program should read one value at a time from the console input.
The algorithm code from Grokking algorithm book:
const findSmallestIndex = (array) => {
let smallestElement = array[0]; // Stores the smallest value
let smallestIndex = 0; // Stores the index of the smallest value
for (let i = 1; i < array.length; i++) {
if (array[i] < smallestElement) {
smallestElement = array[i];
smallestIndex = i;
}
}
return smallestIndex;
};
// 2. Sorts the array
const selectionSort = (array) => {
const sortedArray = [];
const length = array.length;
for (let i = 0; i < length; i++) {
// Finds the smallest element in the given array
const smallestIndex = findSmallestIndex(array);
// Adds the smallest element to new array
sortedArray.push(array.splice(smallestIndex, 1)[0]);
}
return sortedArray;
};
console.log(selectionSort([5, 3, 6, 2, 10])); // [2, 3, 5, 6, 10]
The problem is in the function selectionSort, storing the array length in the variable wes necessary to make it work correctly and this one i couldn't understand, i tried to not store the length in a variable:
const selectionSort = (array) => {
const sortedArray = [];
for (let i = 0; i < array.length; i++) {
// Finds the smallest element in the given array
const smallestIndex = findSmallestIndex(array);
// Adds the smallest element to new array
sortedArray.push(array.splice(smallestIndex, 1)[0]);
}
return sortedArray;
};
console.log(selectionSort([5, 3, 6, 2, 10])); // [2, 3, 5]
I guessed that the problem may be the splice method because it reduces the length every time in the loop but i think the index is not important here, so it may not be be the problem!
Your code is removing the element from the original array, so on each iteration, i++ increases i and also splice decreases array.length. That means i and array.length get closer together by 2 each time instead of by 1, so the loop only iterates half as many times as you want it to. That means you only sort half of the elements into sortedArray.
By copying const length = array.length; first, the variable length is not changed inside the loop, so the i++ makes i closer to length on each iteration by 1, so the number of iterations is the original array length, and every element gets sorted.
As a side note, your algorithm sorts into a new array, but leaves the original array empty. That's probably never what you want; a sorting algorithm should either sort the array in-place (leaving the original array in sorted order), or return a new sorted array (leaving the original array unchanged). You could fix this by making a copy of array at the start of your function, so the algorithm destroys the copy instead of the original.
I'm putting this here for the sole reason that the posted implementation, apparently from an algorithms text, is needlessly overcomplicated and inefficient as well.
function selectionSort(array) {
function smallestIndex(start) {
let si = start;
for (let i = start + 1; i < array.length; ++i) {
if (array[i] < array[si])
si = i;
}
return si;
}
for (let i = 0; i < array.length; i++) {
let index = smallestIndex(i), t;
// swap value into current slot
t = array[index];
array[index] = array[i];
array[i] = t;
}
return array;
}
Here, the smallestIndex() function is enhanced to take a starting position as a parameter. Thus it finds the index of the smallest value in the remainder of the array. On the first iteration, that'll be the smallest value in the whole array. That value is swapped with whatever is at the current starting point, so after that first time through the main loop position 0 in the array is the smallest value in the whole array.
On the next iteration, the search for the index starts at 1, so that process will find the second smallest value from the original array, and swap that into position 1.
The process continues through the array. Note that no new arrays are constructed, and there are no calls to linear-time Array methods.
I wrote this function:
function randomProduct(num) {
var iter = num;
for (var i = 0; i < iter; i++) {
var rand = recommendedProducts[Math.floor(Math.random() * recommendedProducts.length)];
return rand
}
}
Which is supposed to pull from the recommendedProducts array however many are needed when the function is called. So basically randomProduct(1) would pull 1 and randomProduct(4) would pull 4, etc.
However no matter what number I enter in there when I test is through the console, I always only get 1 array item returned.
console.log(randomProduct(1));
console.log(randomProduct(2));
console.log(randomProduct(3));
console.log(randomProduct(4));
What am I doing wrong?
try this:
function randomProduct(num) {
var iter = num;
var rand ="";
for (var i = 0; i < iter; i++) {
rand += recommendedProducts[Math.floor(Math.random() * recommendedProducts.length)];
}
return rand
}
as #Steve Medley said the result expected to be string. so if recommendedProducts contains some string you should add this string in each iteration of loop to your result and return it after your loop has finished( also this is what i have understood from question)
Try this:
function randomProduct(num) {
var iter = num;
var randomArray = [];
for (var i = 0; i < iter; i++) {
randomArray.push(recommendedProducts[Math.floor(Math.random() * recommendedProducts.length)]);
}
return randomArray.join(); // returns it as a string
}
First you need to append the items to an array using push
Second you need to return outside of the loop, if you retrun from the inside of the loop you break the function after the first iteration.
The return rand take you off the function at the first result.
You can remove the loop, just return the rand, and call the function 4 times.
If you want the function to return array of random numbers, instead of return rand, push the results to new array and return the array when the for loop is done.
In your loop, variable rand will be given the value equal to the value it's last iterations output, you need to return array of objects instead of single object to get desired result.
Wondering why i needed to add 4 to the array length in order for it to print out the entire array in reverse?
before i added 4 it was just using the .length property and it was only printing out 6543.
thanks in advance!
function reverseArray(array) {
var newArray =[];
for(var i = 0; i <= array.length+4; i++) {
newArray += array.pop(i);
}
return newArray;
}
var numbers = [1,2,3,4,5,6];
console.log(reverseArray(numbers));
array.pop removes (and returns) the last element. This affects the length of the array. The length is checked on every iteration, so since the array is getting shorter every time, the loop is ended early.
You can create a loop and pop items until it is empty, but another thing to take into account, is that it is the original array you are altering. I think a function like reverseArray shouldn't alter the array numbers that was passed to it if it returns another one. So a better solution would be a simple loop that iterates over all items without modifying the array.
function reverseArray(array)
{
var newArray =[];
for (var i = array.length-1; i >= 0; i--) {
newArray.push(array[i]);
}
return newArray;
}
var numbers = [1,2,3,4,5,6];
console.log(reverseArray(numbers));
console.log(numbers); // Should be unaltered.
If you don't mind modifying the array, you can use the reverse() method of the array:
var numbers = [1,2,3,4,5,6];
numbers.reverse();
console.log(numbers);
In Javascript, pop always removes the last element of the array. This shortens length, meaning that i and array.length were converging.
You can do a few things to avoid this behavior:
Store the original length when you start the loop: for (var i = 0 , l = array.length; i < l; i++)
Copy over values without modifying the original array
When you pop the items from the array, the item is removed from the array. As you increase the counter and decrease the length, they will meet halfway, so you get only half of the items.
Use push to put the items in the result. If you use += it will produce a string instead of an array.
If you use pop, then you can just loop while there are any items left in the array:
function reverseArray(array) {
var newArray = [];
while (array.length > 0) {
newArray.push(array.pop());
}
return newArray;
}
You can leave the original array unchanged by looping through it backwards and add items to the new array:
function reverseArray(array) {
var newArray = [];
for (var i = array.length - 1; i >= 0; i--) {
newArray.push(array[i]);
}
return newArray;
}
use following method for same output
function reverseArray(array)
{
var newArray =[];
var j = array.length-1;
for(var i = 0; i < array.length; i++)
{
newArray[j]= array[i]; j--;
}
return newArray;
}
var numbers = [1,2,3,4,5,6];
console.log(reverseArray(numbers));