How can I optimize for loop? [duplicate] - javascript

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!

Related

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));

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

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))

How to get unique values from a sorted array with time complexity of O(n) or better without altering the original array

I want to count the unique values in a given array without altering the original array but the solution has to be within the time complexity of O(n). so far all of the solutions I've seen, have a time complexity of O(n^2) like here. I can't find the error in my solution's logic. I'm new to Data Structure & Algorithms and would like a simple solution.
MY CODE -
const countUniqueValues = (arr) =>{
if(arr.length === 0){
return console.log(arr.length);
}else if(arr.length === 1){
return console.log(arr.length);
}
const unique = [];
let i = 0;
for( let j = 1; j < arr.length; j++){
if(arr[i] !== arr[j]){
i ++;
unique.push(arr[i]);
}
}
return console.log(unique);
}
//test cases
countUniqueValues([1,1,1,1,1,2]) // 2
countUniqueValues([1,2,3,4,4,4,7,7,12,12,13]) // 7
countUniqueValues([]) // 0
countUniqueValues([-2,-1,-1,0,1]) // 4
Wrong Output -
[ 1 ]
[
2, 3, 4, 4,
4, 7, 7, 12
]
0
[ -1, -1, 0 ]
Turn the array into a Set (O(n)) and count the set's size:
const countUniqueValues = arr => new Set(arr).size;
NB - very important - the arrays must be sorted for this to work:
This should do the trick:
var prevValue = "";
const countUniqueValues = (arr) =>{
if(arr.length === 0){
return console.log(arr.length);
}else if(arr.length === 1){
return console.log(arr.length);
}
prevValue = arr[0];
let i = 1;
for( let j = 1; j < arr.length; ++j){
if(arr[j] != prevValue){
++i;
prevValue = arr[j];
}
}
console.log(i);
return i;
}
const makeUniqueAndCount = arr => {
const uniqueKeysObject = {};
arr.forEach(number => {
uniqueKeysObject[number] = true;
});
return Object.keys(uniqueKeysObject).length;
};
This solution uses objects in javascript. The keys for a javascript object are always unique. You can then use the keys method of the javascript object prototype to turn it into an array to get its length. This solution will work for an unsorted array as well.

Push duplicate items into a separate array in Javascript with for loop?

For some reason, the manipulated doubleArray below is not shown in the console. Any variables that I declare after the for loop won't show to the console on both cases. Consider that in the first algorithm, there is only one for loop with x being incremented everytime. Whereas, in the second algorithm, it's a nested for loop. Can someone help me fix my error in both algorithms?
First Algorithm:
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3];
var doubleValue = [];
var x = 0;
for (i = 0; i < helloWorld.length; i++) {
x = x + 1;
if (helloWorld[i] === helloWorld[x] && i !== x) {
doubleValue.push(helloWorld[i])
console.log(helloWorld[i]);
} else {
continue;
}
}
console.log(doubleValue);
};
The second Algorithm:
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3];
var doubleValue = [];
for (i = 0; i < helloWorld.length; i++) {
for (x = 1; x < helloWorld.length; i++) {
if (helloWorld[i] === helloWorld[x] && i !== x) {
doubleValue.push(helloWorld[x]);
}
}
}
console.log(doubleValue);
};
In first algorithm, you are only checking if the number at current index is equal to the number at the next index, meaning you are only comparing numbers at consecutive indexes. First algorithm will work only if you have duplicate numbers on consecutive indexes.
In second algorithm, you are incrementing i in both loops, increment x in nested loop, change x = 1 to x = i + 1 and your error will be fixed.
Here's the fixed second code snippet
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3, 1, 2];
var doubleValue = [];
for (let i = 0; i < helloWorld.length; i++) {
for (let x = i + 1; x < helloWorld.length; x++) {
if (helloWorld[i] === helloWorld[x] && i !== x) {
doubleValue.push(helloWorld[x]);
}
}
}
console.log(doubleValue);
};
isDuplicate();
Heres's another way to find the duplicates in an array, using an object. Loop over the array, if current number is present as key in the object, push the current number in the doubleValue array otherwise add the current number as key-value pair in the object.
const isDuplicate = function() {
const helloWorld = [1,2,3,4,3, 1, 2];
const doubleValue = [];
const obj = {};
helloWorld.forEach(n => obj[n] ? doubleValue.push(n): obj[n] = n);
console.log(doubleValue);
};
isDuplicate();
Not entirely sure what you are trying to do. If you are only looking for a method to remove duplicates you can do the following:
const hello_world = [1, 2, 2, 3, 4, 5, 5];
const duplicates_removed = Array.from(new Set(hello_world));
A set is a data object that only allows you to store unique values so, when converting an array to a set it will automatically remove all duplicate values. In the example above we are creating a set from hello_world and converting it back to an array.
If you are looking for a function that can identify all the duplicates in an array you can try the following:
const hello_world = [1, 2, 2, 3, 4, 5, 5];
const duplicates_found = hello_world.filter((item, index) => hello_world.indexOf(item) != index);
The main problem by finding duplicates is to have nested loop to compare each element of the array with any other element exept the element at the same position.
By using the second algorithm, you can iterate from the known position to reduce the iteration count.
var isDuplicate = function(array) {
var doubleValue = [];
outer: for (var i = 0; i < array.length - 1; i++) { // add label,
// declare variable i
// no need to check last element
for (var j = i + 1; j < array.length; j++) { // start from i + 1,
// increment j
if (array[i] === array[j]) { // compare values, not indices
doubleValue.push(array[i]);
continue outer; // prevent looping
}
}
}
return doubleValue;
};
console.log(isDuplicate([1, 2, 3, 4, 3])); // [3]
You could take an object for storing seen values and use a single loop for getting duplicate values.
const
getDuplicates = array => {
const
seen = {}
duplicates = [];
for (let value of array) {
if (seen[value]) duplicates.push(value);
else seen[value] = true;
}
return duplicates;
};
console.log(getDuplicates([1, 2, 3, 4, 3])); // [3]
Your first algorithm doesn't work because it only looks for duplicates next to each other. You can fix it by first sorting the array, then finding the duplicates. You can also remove the x and replace it by ++i in the loop.
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3,6];
var doubleValue = [];
helloWorld = helloWorld.sort((a, b) => { return a - b });
for (i = 0; i < helloWorld.length; i++) {
if (helloWorld[i] === helloWorld[++i]) {
doubleValue.push(helloWorld[i])
console.log(helloWorld[i]);
} else {
continue;
}
}
console.log(doubleValue);
};
isDuplicate();
For the second algorithm loop, you probably meant x++ instead of i++ in the second loop. This would fix the problem.
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3,4];
var doubleValue = [];
for (i = 0; i < helloWorld.length; i++) {
for (x = i + 1; x < helloWorld.length; x++) {
if (helloWorld[i] === helloWorld[x]) {
doubleValue.push(helloWorld[x]);
}
}
}
console.log(doubleValue);
};
isDuplicate()
The first algorithm can't be fixed, it can only detect consecutive duplicates,
in the second algorithm you increment i in both loops.
To avoid the duplicates beeing listed too often, you should start the second loop with i + 1

How to return the first occurrence of repeated item in an array using for loop?

I am trying to build logic currently with arrays and data structure. I am trying to implement the logic using for loop
function getRepeatingNumber(arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
return arr[i];
}
}
}
return undefined;
}
getRepeatingNumber([2, 3, 6, 5, 2]);
the above function takes in array and returns a repeated item in the array so in the above case it will return 2. But what if I have an array something like this arr[2,3,3,6,5,2] in this case it should return 3 but as the outer loop has index [0] which is 2 as the reference it will return 2 as the answer.
How to implement a function that returns the first occurrence of the repeated item.
Instead of iterating with j in the part after i, iterate the part before i:
function getRepeatingNumber(arr){
for (var i = 1; i < arr.length; i++) {
for (var j = 0; j < i; j++) {
if (arr[i] === arr[j]) {
return arr[i];
}
}
}
}
console.log(getRepeatingNumber([2,3,3,6,5,2]));
Note that an explicit return undefined is not needed, that is the default behaviour already.
You could also use indexOf to shorten the code a bit:
function getRepeatingNumber(arr){
for (var i = 1; i < arr.length; i++) {
if (arr.indexOf(arr[i]) < i) {
return arr[i];
}
}
}
console.log(getRepeatingNumber([2,3,3,6,5,2]));
You could even decide to make use of find -- which will return undefined in case of no match (i.e. no duplicates in our case):
function getRepeatingNumber(arr){
return arr.find((a, i) => {
if (arr.indexOf(a) < i) {
return true;
}
});
}
console.log(getRepeatingNumber([2,3,3,6,5,2]));
If you do this for huge arrays, then it would become important to have a solution that runs with linear time complexity. In that case, a Set will be useful:
function getRepeatingNumber(arr){
var set = new Set;
return arr.find(a => {
if (set.has(a)) return true;
set.add(a);
});
}
console.log(getRepeatingNumber([2,3,3,6,5,2]));
And if you are into functions of functions, and one-liners, then:
const getRepeatingNumber = r=>(t=>r.find(a=>[t.has(a),t.add(a)][0]))(new Set);
console.log(getRepeatingNumber([2,3,3,6,5,2]));
You need a data structure to keep track of first occurring index.
My recommendation is to use an array to store all the index of repeating numbers. Sort the array in ascending order and return the item at first index from the array.
function getRepeatingNumber(arr){
var resultIndexArr = [];
var count = 0;
var flag = 0;
for(var i=0;i<arr.length;i++)
{
for(var j=i+1;j<arr.length;j++)
{
if(arr[i] === arr[j])
{
flag = 1;
resultIndexArr[count++] = j;
}
}
}
resultIndexArr.sort((a, b) => a - b);
var resultIndex = resultIndexArr[0];
if(flag === 1)
return arr[resultIndex];
else
return;
}
console.log(getRepeatingNumber([2,3,6,5,2])); // test case 1
console.log(getRepeatingNumber([2,3,3,6,5,2])); // test case 2
console.log(getRepeatingNumber([2,5,3,6,5,2])); // test case 3
This will return correct result, but this is not the best solution. The best solution is to store your items in an array, check for each iteration if the item already exists in your array, if it exists then just return that item.
as a javascript dev you should be comfortable wit functional programming & higher-order functions so check the doc to get more understanding of some useful functions: like filter - find - reduce - findIndex map ...
Documentation
Now to answer your question:
at first you should think by step :
Get the occurrence of an item in an array as function:
const arr = [2, 5, 6, 2, 4, 5, 6, 8, 2, 5, 2]
const res = arr.reduce((numberofOcc, item) => {
if (item === 2)
numberofOcc++
return numberofOcc
}, 0);
console.log(`result without function ${res}`);
/* so my function will be */
const occurenceFun = (num, arr) => {
return arr.reduce((numberofOcc, item) => {
if (item === num)
numberofOcc++
return numberofOcc
}, 0);
}
console.log(`result using my function ${occurenceFun(2, arr)}`);
Now i have this function so i can use it inside another function to get the higher occurrence i have in an array
const arr = [1, 2, 5, 6, 8, 7, 2, 2, 2, 10, 10, 2]
const occurenceFun = (num, arr) => {
return arr.reduce((numberofOcc, item) => {
if (item === num)
numberofOcc++
return numberofOcc
}, 0);
}
/*let's create our function*/
const maxOccurenceFun = arr => {
let max = 0;
arr.forEach(el => {
if (max < occurenceFun(el, arr)) {
max = el
}
})
return max;
}
console.log(`the max occurence in this array is : ${maxOccurenceFun(arr)}`);

Categories

Resources