How do you look through arrays more efficiently? Javascript [closed] - javascript

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I've been doing a lot of the practise tests on codility and though all my solutions work, they always have hidden stress tests which include massive arrays, the following code for example needs to take array A and find the lowest positive number missing from the sequence.
e.g: given A = [1, 3, 6, 4, 1, 2], the function should return 5. 5 is the value of i which increments in the loop. Function returns whenever index i is not found in array.
It works correctly but codility tested it with a 1-100,000 array and it timed out at the 6 second limit. most of my codes on the practise tests tend to fail for the same reason. Any help greatly appreciated.
console.log(solution([1, 3, 6, 4, 1, 2]));
function solution(A) {
let i = 0
for (i = 1; i <= A.length ; i++){
if(A.includes(i) == false){
return i
}
}
return i
}

There is no way to look through N items in less than O(n) which is what you're doing. The issue is you're looking through the array N times as well - this means you run N*N times and can be improved.
The most "typical" way to improve this approach is to use a Set or similar data structure with amortised (usually) constant-time access to elements. In your case this would look like:
console.log(solution([1, 3, 6, 4, 1, 2]))
function solution(A) {
// build existing elements, in O(N)
const values = new Set(A)
let i = 0
for (i = 1; i <= A.length; i++){
if(!values.has(i)){
return i
}
}
return i
}
This runs in O(N) (creating the set) + O(N) iterating the array and performing constant time work each time.

Your code loops through every item in the array for every item in the array. This gives a worst-case runtime of O(n^2). You can get a much better result if you sort the array, and then iterate through it looking for any missing numbers.
function compareNumeric(a,b) {
return a - b;
}
function solution(A) {
A.sort(compareNumeric);
let expect = 0;
for( let i=0; i<A.length; i++) {
if( A[i] > expect+1) return expect+1;
if( A[i] === expect+1) expect++;
}
return expect+1;
}
console.time('simple');
console.log(solution([1, 3, 6, 4, 1, 2]));
console.timeEnd('simple');
// worst-case: all numbers from 1-1M exist but are randomly shuffled in the array
const bigArray = Array.from({length:1000000},(_,i)=>i+1);
function shuffle(a) {
// credit: https://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
shuffle(bigArray);
console.time('worst-case');
console.log(solution(bigArray));
console.timeEnd('worst-case');
This will give you a runtime of O(n log n) and should be the fastest possible solution as far as I can tell.

Please check the two solutions, it will take the time complexity O(n * log n) or O(n) so it will be much faster than previous work.
const arrayGenerator = (len) => {
const array = [];
for(let i = 0; i < len; i ++) {
array.push(Math.floor(Math.random() * len) + 1);
}
return array;
}
function sort_array_solution(A) {
A.map(a=>a).sort((a, b)=>a - b)
let ans = 1;
for(let i = 0; i < A.length; i ++) {
if(A[i] < ans) continue;
if(A[i] > ans) return ans;
ans ++;
}
return ans;
}
function object_key_solution(A) {
const obj = {}
for(let i = 0; i < A.length; i ++) {
obj[A[i]] = 1;
}
for(let ans = 1; ; ans ++) {
if(!obj.hasOwnProperty(ans)) return ans;
ans ++;
}
return ans;
}
console.log(sort_array_solution([1,2]))
console.log(object_key_solution([1,2]))
const arr = arrayGenerator(10);
console.log(sort_array_solution(arr))
console.log(object_key_solution(arr))
const arr1 = arrayGenerator(100000);
console.log(sort_array_solution(arr1))
console.log(object_key_solution(arr1))

Related

How can I optimize for loop? [duplicate]

This question already has answers here:
2 Sum algorithm explantion?
(2 answers)
Closed 4 months ago.
Problem:
Return true if the sum of two different elements of the array equals the target, otherwise return false
I want to optimize the time complexity of this code.
Now, code has O(n^2) complexity. How can I reduce complexity?
input is unsorted array(number[]) and target(number), output is true or false.
Here`s my code.
function find(arr, target) {
for(let i = 0; i < arr.length; i++){
for(let j = i + 1; j < arr.length; j++){
if(target === (arr[i]+arr[j])){
return true;
}
}
}
return false;
}
I think hint is unsorted array. And I don`t know at all..
I don't think your particular implementation can be simplified, however if you first sort the array you can take a two-pointer approach to figure out if the target can be found, resulting in O(n log n) complexity.
function find(arr, target) {
arr.sort();
let start = 0;
let end = arr.length - 1;
while(start < end) {
if(arr[start] + arr[end] > target) {
end--;
} else if(arr[start] + arr[end] < target) {
start++;
} else {
return true;
}
}
return false;
}
Actually you don't need to sort the array which takes O(nlogn), but you can solve it in linear runtime O(n+n).
Idea:
First build a dictionary from array so later you can search for a key in O(1) time.
then iterate through your array and find out reminder is in dictionary or not, if the reminder is found that's the answer.
for example: arr = [1, 5, 6] and target = 6;
so when you are on 0th position then
let reminder = target - arr[0];
let reminder = 6 - 1 = 5
so you just need to look your array has 5 or not, that's why you need to build a dictionary for looking up in O(1).
const find = (arr, dict, target) => { // then iterating through array which takes O(n)
let found = false;
for (let i = 0; i < arr.length; ++i) {
const reminder = target - arr[i]; //just need to find out reminder is already in array or not
if (reminder in dict && dict[reminder] != i) {
found = true;
break;
}
}
return found;
}
const arr = [-3, 1, 10, 9, 15, 100];
const refDict = arr.reduce((currDict, val, i) => { //first create a dictionary which takes O(n)
return {
...currDict,
[val]: i
};
}, {});
const isFound = find(arr, refDict, 20);
console.log(isFound);
Hope it helps!

Leetcode problem: Remove Duplicates from Sorted Array

Example :
Input: nums = [0,0,1,1,1,2,2,3,3,4]
Output: 5, nums = [0,1,2,3,4,_,_,_,_,_]
Explanation: Your function should return k = 5, with the first five elements of nums
being 0, 1, 2, 3, and 4 respectively.
It does not matter what you leave beyond the returned k (hence they are underscores).
My code to solve this problem is
var removeDuplicates = function(nums) {
const non_duplicates = [];
for (let i=0;i<=nums.length-1;i++){
if(!(non_duplicates.includes(nums[i]))){
non_duplicates.push(nums[i]);
}
}
console.log(non_duplicates)
return non_duplicates.length;
};
That console.log(non_duplicates) displays correct output in stdOut. But when I return non_duplictes it prints empty array in output. And when I return non_duplictes.length it returns some array rather than the length of that array.
Please don't suggest any other method to solve this problem. Just tell what is wrong with this code
You can see that problem here
Your solution doesn't modify the array inplace. That's the first problem.
A bigger problem is that your algorithm is with O(N^2) time complexity, due to includes call and given that "1 <= nums.length <= 3 * 104" your solution would be incredibly slow if it ever passes the tests.
The goal is to find a linear solution of the problem. One possible solution would be:
var removeDuplicates = function(nums) {
let j = 0;
for (let i = 1, len = nums.length; i < len; i++) {
if (nums[i] !== nums[j]) {
nums[++j] = nums[i];
}
}
return j + 1;
};
You can achieve that by using Set() Object.
let nums = [0,0,1,1,1,2,2,3,3,4];
const uniqueNums = [...new Set(nums)];
console.log(uniqueNums.length, uniqueNums);
You're returning the correct value but you are not overwriting the nums array with the non-duplicate values.
After calling your function, the return value should be 5 and the first 5 values in the passed-in nums array should be 0,1,2,3,4 (the value of the others doesn't matter).
Simplest solution that's compatible with your attempted solution is:
copy the values from non_duplicates into nums
return non_duplicates.length.
Here's a simple example of how to do this:
var removeDuplicates = function (nums) {
const non_duplicates = [];
for (let i = 0; i < nums.length; i++) {
if (!non_duplicates.includes(nums[i])) {
non_duplicates.push(nums[i]);
}
}
for (let i = 0; i < non_duplicates.length; i++) {
nums[i] = non_duplicates[i];
}
return non_duplicates.length;
};
Note: this is not the most efficient solution, and efficiency becomes important with the medium to hard Leetcode challenges, but it's simple, close to your intended solution, and it works.

hasPairsWithSum Google Interview Question

I solved this problem by iterating through the array then find the item when the sum equals to array[i] + item returning true otherwise returning false.
My Question is => How I can return the indices of those numbers that add up to sum not just true? Using the same code below:
function hasPairsWithSum(array,sum) {
for (let i = 0; i < array.length; i++) {
if (array.find((item) => {return sum === array[i] + item}
));
return true;
};
return false;
};
console.log(hasPairsWithSum([1,2,4,4],8))
Note: Time complexity must be less than O(n ^ 2).
JavaScript O(n) Solution.
function hasPairsWithSum(array, sum) {
const map = new Map ();
for(let i = 0; i < array.length; i++) {
let currVal = array[i];
if (map.has(currVal)) {
return [map.get(currVal),i]
}
// difference value = sum - current value
let diff = sum - currVal
map.set(diff,i)
}
};
console.log(hasPairsWithSum([2,2,4,4], 8))
Please refer this code.
function hasPairsWithSum(array,sum) {
let result = [];
for (let i = 0; i < array.length; i++) {
if (array.some((item, index) => {return i === index ? false : sum === array[i] + item}))
result.push(i);
};
return result;
};
console.log(hasPairsWithSum([1,2,4,4],8))
console.log(hasPairsWithSum([3,2,4],6))
console.log(hasPairsWithSum([0,4,3,0],0))
O(n) Soln ... using math concept a+b = n then if a is present in our array then need to find b = n - a is present or not ..
def hasPairsWithSum(array,sum):
d = {}
for i in range(len(array)):
if(array[i] in d):
d[array[i]].append(i)
else:
d[array[i]] = [i]
ans = []
for i in range(len(array)):
val = sum - array[i]
if(val in d):
if(d[val][0] == i):
if(len(d[val]) > 1):
ans.append((i,d[val][1]))
break
else:
continue
else:
ans.append((i,d[val][0]))
break
return ans
print(hasPairsWithSum([4, 4, 4, 4], 8))
O(nlogn) soln ....just store the index with elements .. then sort it by their values .. next step run a loop with complexity of O(n) [concept : Two pointers]
def hasPairsWithSum(array,sum):
arr = []
for i in range(len(array)):
arr.append((array[i],i))
arr.sort()
i = 0
j = len(array)-1
ans = []
while(i<j):
tmp_sum = arr[i][0] + arr[j][0]
if(tmp_sum == sum):
ans.append((arr[i][1] , arr[j][1]))
#add your logic if you want to find all possible indexes instead of break
break
elif(tmp_sum < sum):
i = i + 1
elif(tmp_sum > sum):
j = j - 1
return ans
print(hasPairsWithSum([1,2,4,4],8))
note : if you want to find all possible soln then these approaches will not work either add you own logic in while loop or another approach is use binary search with traversal on every element and store the indexes in set (worst case this will be O(n^2) as we have to find all possible values) Eg: [4,4,4,4,4,4] , sum = 8 and you want to print all possible indexes then we end up running it upto n^2 (why? reason: total possible solns. are 5+4+3+2+1 = n*(n-1)/2 ≈ n^2)
You have to iterate over the array elements checking at every iteration for every element of the array (except the last one) all the elements at the right of it like below:
function findIndexes(array, sum) {
const result = [];
for (let i = 0; i < array.length -1; ++i) {
for (let j = i + 1; j < array.length; ++j) {
if ((array[i] + array[j]) === sum) {
result.push([i, j]);
}
}
}
return result;
}
console.log(findIndexes([1, 2, 4, 4], 8));
console.log(findIndexes([3, 2, 4], 6));
Update:
It is possible to obtain a linear O(n) complexity using an auxiliary Map structure associating an integer value as key with as a value the list containing all the indexes of the elements in the array equal to the integer key like below:
function findIndexes(array, sum) {
const map = new Map();
const result = [];
for (let i = 0; i < array.length; ++i) {
const a = array[i];
const b = sum - a;
if (map.has(b)) {
for (const index of map.get(b)) {
result.push([index, i]);
}
}
const l = map.has(a) ? map.get(a) : [];
l.push(i);
map.set(a, l);
}
return result;
}
console.log(findIndexes([1, 2, 4, 4], 8));
console.log(findIndexes([3, 2, 4], 6));
console.log(findIndexes([1, 1, 1], 2));

Pairwise sums challenge

I've got working code, but I'm looking to improve the way I understand and implement different coding techniques as a whole. I thought this problem presented a good chance to get feedback on what I'm doing.
The idea here is to take two arguments, an array and an integer. Identify all pairs in the array that sum to make the integer argument, and then return the sum of the indices. You cannot reuse an index, and you must always use the smallest index available to you. There is an explanation on the FCC code guide here: https://www.freecodecamp.org/learn/coding-interview-prep/algorithms/pairwise
So - here is the question. Is there any good way of doing this without using nested for loops? I am becoming increasingly aware of time/space complexities, and I know that O(n^2) won't land me the job.
I would imagine a hashmap of some sort would come into it, but I just don't have the experience and knowledge to know how to use them.
Here is the code:
function pairwise(arr, arg) {
let usedIndex = [];
let output = 0;
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (
arr[i] + arr[j] == arg
&& usedIndex.indexOf(i) == -1
&& usedIndex.indexOf(j) == -1
) {
usedIndex.push(i, j)
output += i + j
}
}
}
return output;
}
pairwise([0, 0, 0, 0, 1, 1], 1) // should return 10
This can be done in one loop with some clever use of an object and knowledge that indices can only be used once.
function pairwise(arr, arg) {
let map = {};
let output = 0;
let length = arr.length;
for (let i = 0; i < length; i++) {
let subArr = map[arr[i]];
if(subArr && subArr[0] !== undefined) {
//there is an index waiting to pair, remove it and add to output
output += subArr.pop() + i;
} else {
//add this index to its pair slot
let left = arg - arr[i];
if(!map[left]) map[left] = [];
map[left].unshift(i);
}
}
return output;
}
console.log(pairwise([0, 0, 0, 0, 1, 1], 1), "should be 10");
console.log(pairwise([1, 4, 2, 3, 0, 5], 7), "should be 11");
console.log(pairwise([], 100), "should be 0");
console.log(pairwise([1, 3, 2, 4], 4), "should be 1");
The keys of the map represent the other value needed to make a pair, and the values of the map are arrays of indices that have the value that would make a pair. The indices are inserted using unshift() so that pop() returns the first one that was inserted - the smallest one.
Iterating from the front guarantees that the smallest pairs are found first, and the map guarantees that any later index will know exactly what the earliest index that could make a pair is.
For a better performance you can save the arr.length into a variable, then for loop won't count every single loop.
function pairwise(arr, arg) {
let usedIndex = [];
let output = 0;
let length = arr.length;
for (let i = 0; i < length; i++) {
for (let j = i + 1; j < length; j++) {
if (
arr[i] + arr[j] == arg
&& usedIndex.indexOf(i) == -1
&& usedIndex.indexOf(j) == -1
) {
usedIndex.push(i, j)
output += i + j
}
}
}
return output;
}
Sort the list.
Have two counters walking from the ends. At each step check to see if the sum is what you want. If so, capture the desired metric.
Step 1 is O(n*logn).
Step 2 is O(n) -- each counter will go about halfway through the list, stopping when they meet.

Find the largest subarray length with delimiter

I'm trying to solve this problem:
Given two parameters: an array a and integer i, find the largest array length where the sum of all elements is <= i.
For example, having the following array: [3, 1, 2, 1], and i = 4, all the combinations that are <= i are: [3], [1], [2], [3, 1], [1, 2], [1, 2, 1]. The largest subarray is [1, 2, 1], so the return of the function should be 3 (the array length).
What would be a an efficient approach to solve this problem?
This is my algorithm until now, but I know that I'm missing something:
function sumArray(a) {
return a.reduce((a, b) => a + b, 0)
}
function maxLength(a, i) {
let max = 0
let array = [a[0]]
a.splice(1, a.length).forEach(number => {
array.push(number)
if (sumArray(array) <= k) max = array.length
else array.splice(array.indexOf(Math.max.apply(null, array)), 1)
})
return max
}
Here's how I would do it.
First, we'll get the longest sub-array by taking only the smallest elements out of the original, because that way the sum will be smallest possible as we go along. So:
const original = [3, 1, 2, 1];
const maxsum = 4;
// To make sure we take only the smallest, let's just
// sort the array straight away. That way the smallest
// element will always be the first.
// The .slice call is to make a copy, so we don't change
// the original:
const sorted = original.slice().sort();
// Here's your sum function. Looks legit.
function sumArray(a) {
return a.reduce((a, b) => a + b, 0)
}
// Now take items from the front of the sorted array and
// put them in the new array until either the original is
// empty or the max is reached.
let subarray = [];
while (sorted.length) {
// Get the next smallest element. It's always the
// first one because of the sort.
const smallest = sorted.shift();
// If the sum of what we have plus this new element
// is bigger than the max, our work is done:
if (sumArray(subarray) + smallest > maxsum) break;
// Otherwise, add it to our sub array and continue.
subarray.push(smallest)
}
// Once that loop has run, either we ran out of elements,
// or we hit the max. Either way, our job is done.
console.log("Original array:", original);
console.log("Maximal subset:", subarray);
console.log("Maximal subset length:", subarray.length);
Finally, if you want to get fancy, you can even do this with a single .reduce call:
const original = [3, 1, 2, 1];
const maxsum = 4;
const maximalSubset = original.slice().sort().reduce((subset, current) => {
if (subset.reduce((s, c) => s + c, 0) + current <= maxsum) subset.push(current);
return subset;
}, []);
console.log("Orignal:", original);
console.log("Maximal subset:", maximalSubset);
console.log("Maximal subset length:", maximalSubset.length);
Although while shorter, that second snippet has the downside that we have to iterate the entire array before we get the result, whereas the first one will stop once the maximum is reached.
EDIT:
It turns out that the subarray needs to be a continuous piece of the original, so changing the order of the original won't work since we need to make sure the result is a continuous slice of the original.
To do that, instead, just check each subslice of the array, and keep the best one:
let original = [74,659,931,273,545,879,924,710,441,166,493,43,988,504,328,730,841,613,304,170,710,158,561,934,100,279,817,336,98,827,513,268,811,634,980,150,580,822,968,673,394,337,486,746,229,92,195,358,2,154,709,945,669,491,125,197,531,904,723,667,550];
const maxsum = 22337;
function arraySum(arr) {
return arr.reduce((p, c) => p + c, 0);
}
// Double for loop will do the trick.
let bestSoFar = [];
for (let i = 0; i < original.length; i++) {
for (let j = i+1; j < original.length; j++) {
if (j-i > bestSoFar.length && arraySum(original.slice(i, j)) < maxsum) {
bestSoFar = original.slice(i, j);
}
}
}
console.log("Longest continuous subarray is:", bestSoFar.length);
A brute force approach is likely the best solution to this problem. Start at each entry and see how far you can go before arriving at a sum > i and if it's better than the best you've seen so far save it. I provided a sample Java solution below, I haven't actually run it so one or two of my indices may be off but I think you can get the gist. Runtime is O(n^2), memory is O(n) (both occurring with, for example, #getMaxSubArray(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 1000000))
private int[] getMaxSubArray(int[] a, int i) {
int minIndex = 0;
int maxIndex = -1;
for (int j = 0; j < a.length; j+=1) {
if (a[j] > i) {
continue;
}
int sum = a[j];
for (int k = j + 1; k < a.length; k++) {
if (sum + a[k] > i) {
if ((k - 1) - j > maxIndex - minIndex) {
maxIndex = k - 1;
minIndex = j;
}
break;
}
sum += a[k];
}
}
if (maxIndex - minIndex < 0) {
return null;
}
int[] result = new int[maxIndex - minIndex + 1];
for (int p = minIndex; p <= maxIndex; p += 1) {
result[p - minIndex] = a[p];
}
return result;
}
This is my solution. It will return the max length of the subarray. Can you please take a look, guys?
function maxLength(a, k) {
const sortedArray = a.sort((i,j) => i - j);
let sum = 0;
let length = 0;
const subArray = [];
for (let i=0; i < sortedArray.length; i++) {
sum = sum + sortedArray[i];
if (sum <= k) {
length++;
subArray.push(sortedArray[i]);
} else {
return length;
}
}
return length;
}

Categories

Resources