javascript: how to make function run faster - javascript

so i have to write a function which meets the following requirements:
Given a sequence of integers as an array, determine whether it is possible to obtain a strictly increasing sequence by removing no more than one element from the array.
Example:
For sequence = [1, 3, 2, 1], the output should be
almostIncreasingSequence(sequence) = false;
There is no one element in this array that can be removed in order to get a strictly increasing sequence.
For sequence = [1, 3, 2], the output should be
almostIncreasingSequence(sequence) = true.
You can remove 3 from the array to get the strictly increasing sequence [1, 2]. Alternately, you can remove 2 to get the strictly increasing sequence [1, 3].
Input/Output
[time limit] 4000ms (js)
[input] array.integer sequence
Guaranteed constraints:
2 ≤ sequence.length ≤ 105,
-105 ≤ sequence[i] ≤ 105.
so my code works except for one problem--there are 30 tests it has to pass with the time constraint of 4000ms, but it always times out on the 30th test, every time. i have tried modifying it so that it runs faster, but each time i do so it no longer works correctly. although i technically only have to write one function, i broke it up into three separate functions. here's my code:
var greater = function(a, b) {
if (a < b) {
return true
} else {
return false
}
}
function greaterThan(arr) {
for (var i = 0; i < arr.length-1; i++) {
var curr = arr[i]
var next = arr[i + 1]
if (greater(curr, next) === false) {
return false
}
}
return true
}
function almostIncreasingSequence(sequence) {
for(var i = 0; i < sequence.length; i++) {
var newArr = sequence.slice()
newArr.splice(i, 1)
if (greaterThan(newArr) === true) {
return true
}
}
return false
}
so how can i make it run faster without using two for-loops/iterations to do this?

Improving the algorithm may bring better results than improving the code. Here's the problem:
If the sequence is not strictly increasing at index i, such that a[i]>=a[i+1] is true, either a[i] or a[i+1] must be removed to possibly make the array strictly increasing - they can't both be left in place.
If the input array is to be fixable by removing only one element, and it decreases after the ith element it must become strictly increasing by removing the element with subscript i or (i+1).
Compare the efficiency of checking the original array and at most two sub arrays before returning true or false, with checking the same number of arrays as the original array's length. I'll leave re-writing the code to you - it's not my homework :-)

Related

firstDuplicate question on CodeSignal in Javascript

I couldn't figure out why I passed 22/23 on this challenge and not able to solve the last test case since it was hidden.. The feedback from the CodeSignal is
Tests passed: 22/23. Execution time limit exceeded: Program exceeded
the execution time limit. Make sure that it completes execution in a
few seconds for any possible input.
Challenge
Given an array a that contains only numbers in the range from 1 to a.length, find the first duplicate number for which the second occurrence has the minimal index. In other words, if there are more than 1 duplicated numbers, return the number for which the second occurrence has a smaller index than the second occurrence of the other number does. If there are no such elements, return -1.
Example
For a = [2, 1, 3, 5, 3, 2], the output should be solution(a) = 3.
There are 2 duplicates: numbers 2 and 3. The second occurrence of 3 has a smaller index than the second occurrence of 2 does, so the answer is 3.
For a = [2, 2], the output should be solution(a) = 2;
For a = [2, 4, 3, 5, 1], the output should be solution(a) = -1.
Input/Output
[execution time limit] 4 seconds (js)
[input] array.integer a
Guaranteed constraints:
1 ≤ a.length ≤ 105,
1 ≤ a[i] ≤ a.length.
[output] integer
The element in a that occurs in the array more than once and has the minimal index for its second occurrence. If there are no such elements, return -1.
My code
function solution(a) {
let first = Infinity
for ( let i = 0; i<a.length; i++ ) {
let pointer = i+1;
while (pointer <a.length) {
if (a[i] === a[pointer] && pointer<first) {
first = pointer;
}
pointer +=1
}
}
if (first === Infinity) {
return -1
}
return a[first]
}
Thank you.
In bad cases, you're iterating over the whole array for every element in it - O(n ^ 2). The inner while(pointer < a.length) results in the argument taking too much time.
Instead, make a Set of elements found so far, and return when the first duplicate element is found (which will be the minimal second index).
const solution = (a) => {
const set = new Set();
for (const item of arr) {
if (set.has(item)) return item;
set.add(item);
}
return -1;
};
Since this has no nested loop (.has and .add is O(1)), this is O(n) overall, which should be quick enough.

Understanding Hashmaps with Javascript

I need help understanding what and how to use hash maps in javascript. I have an example where
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.
Can someone breakdown what this hashmap solution is doing and why it's better? Also if someone would be kind of enough to give me a similar problem to practice with that would extremely helpful.
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
My BruteForce Solution
for (var i = 0; i < nums.length; i++) {
for (var j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] === target) {
result.push(i);
result.push(j);
}
}
}
return result;
}
console.log(twoSum([2, 7, 11, 15], 9));
HashMapSolution
function twoSumBest(array, target) {
const numsMap = new Map();
for (let i = 0; i < array.length; i++) {
if(numsMap.has(target - array[i])) {
return [numsMap.get(target - array[i], i)];
// get() returns a specified element associated with the specified key from the Map object.
} else {
numsMap.set(array[i], i);
// set() adds or updates an element with a specified key and value to a Map object.
}
}
}
If you want to reach 10 and you have 7, you already know that the other number that is needed is 3. So for every number in the array, you only have to check wether the complementary number is in the array, you don't necessarily have to search for it.
If we go over all array entries once (let's call them a) and add them to a hashmap with their index, we can go over the array again and for each entry (b), we can check if the hashmap contains an a (where a + b = target). If that entry is found, the index of b is known, and the index of a can be retrieved from the hashmap¹. Now using that method we only iterate the array twice (or just once, doesn't matter that much), so if you have 1000 numbers, it'll iterate 1000 times. Your solution will iterate 1000 * 1000 times (worst case). So the hashtable approach is way faster (for larger arrays).
¹ Hashmaps are very special, as the time it takes to look up a key takes a constant amount of time, so it does not matter wether the array has 10 or 10 million entries (the time complexity is constant = O(1)). Thus, looking up in an hashtable is way better than searching inside of an array (which takes more time with more entries = O(n)).

JS: Finding unpaired elements in an array

I have the following question (this is not school -- just code site practice questions) and I can't see what my solution is missing.
A non-empty array A consisting of N integers is given. The array contains an odd number of elements, and each element of the array can be paired with another element that has the same value, except for one element that is left unpaired.
Assume that:
*N is an odd integer within the range [1..1,000,000];
*each element of array A is an integer within the range [1..1,000,000,000];
*all but one of the values in A occur an even number of times.
EX: A = [9,3,9,3,9,7,9]
Result: 7
The official solution is using the bitwise XOR operator :
function solution(A) {
var agg = 0;
for(var i=0; i<A.length; i++) {
agg ^= A[i];
}
return agg;
}
My first instinct was to keep track of the occurrences of each value in a Map lookup table and returning the key whose only value appeared once.
function solution(A) {
if (A.length < 1) {return 0}
let map = new Map();
let res = A[0]
for (var x = 0; x < A.length; x++) {
if (map.has(A[x])) {
map.set(A[x], map.get(A[x]) + 1)
} else {
map.set(A[x], 1)
}
}
for ([key,value] of map.entries()) {
if (value===1) {
res = key
}
}
return res;
}
I feel like I handled any edge cases but I'm still failing some tests and it's coming back with a 66% correct by the automated scorer.
You could use a Set and check if deletion deletes an item or not. If not, then add the value to the set.
function check(array) {
var s = new Set;
array.forEach(v => s.delete(v) || s.add(v));
return s.values().next().value;
}
console.log(check([9, 3, 9, 7, 3, 9, 9])); // 7
You're not accounting for cases like this:
[ 1, 1, 2, 2, 2 ] => the last 2 is left unpaired
So your condition should be if ( value % 2 ) instead of if ( value === 1 ).
I think also there is not much benefit to using a Map rather than just a plain object.
The official solution works due to the properties of the bitwise XOR (^), namely the fact that a ^ a == 0, a ^ 0 == a, and that the operation is commutative and associative. This means that any two equal elements in the array will cancel each other out to become zero, so all numbers appearing an even amount of times will be removed and only the number with an odd frequency will remain. The solution can be simplified using Array#reduce.
function findOdd(arr) {
return arr.reduce((a,c)=>a ^ c, 0);
}
You need not to make a count of each and traverse again, if you are sure that there will be exactly one number which will occur odd number of times. you can sum the array and do + when odd entry and - when even entry (to dismiss it back) and in the hash (map or object) you can just toggle for subsequent entry of each number.
Here is an example:
let inputArray1 = [10,20,30,10,50,20,20,70,20,70,50, 30,50], //50 -> 3 times
inputArray2 = [10,20,30,20,10], //30 -> 1 time
inputArray3 = [7,7,7,7,3,2,7,2,3,5,7]; //5 -> 1 time
let getOddOccuence = arr => {
let hash = {};
return arr.reduce((sum, n) => sum + ((hash[n] = !hash[n]) ? n : -n), 0);
}
console.log('Input Array 1: ', getOddOccuence(inputArray1));
console.log('Input Array 2: ', getOddOccuence(inputArray2));
console.log('Input Array 3: ', getOddOccuence(inputArray3));
In case the input contains multiple or no numbers having odd number of occurance (if you are not sure there) then you have already the hash (and you can ignore performing sum) and return the keys of hash (where value is true (and not checking with %2 and then consider as truthy or false in case of you have count))
function solution(A) {
let result = 0;
for (let element of A) {
// Apply Bitwise XOR to the current and next element
result ^= element;
}
return result;
}
const unpaired = solution([9, 3, 9, 3, 9, 7, 9]);
console.log(unpaired);
Source: https://gist.github.com/k0ff33/3eb60cfb976dee0a0a969fc9f84ae145

how to find an almost increasing sequence Array

I have had an algorithm problem below:
"Given a sequence of integers as an array, determine whether it is possible to obtain a strictly increasing sequence by removing no more than one element from the array.
Example
For sequence = [1, 3, 2, 1], the output should be
almostIncreasingSequence(sequence) = false;
There is no one element in this array that can be removed in order to get a strictly increasing sequence.
For sequence = [1, 3, 2], the output should be
almostIncreasingSequence(sequence) = true.
You can remove 3 from the array to get the strictly increasing sequence [1, 2]. Alternately, you can remove 2 to get the strictly increasing sequence [1, 3]."
My code (for Javascript) was:
function almostIncreasingSequence(sequence) {
var count =0;
for (i =0 ; i<sequence.length-1 ; i++){
if (sequence[i+1]<=sequence[i]){
count++;
}
}
return count <2;
}
However if in the case of sequence =[1,2,3,4,3,4,5]
My code will be wrong;
Please explain to me an algorithm that can be used to solve this problem.
And also please explain in step by step so that I can understand.
Sorry for not being clear as it is my first question here.
This may help.
function almostIncreasingSequence(sequence) {
var exists = false; // Assume the sequence returns false by default
var mainSequence = sequence.slice(); // Clone sequence so that we can use it again
m_seq: // The name of the first loop, so that we can exit from it
for (var i = 0; i < mainSequence.length; i++) { // Loop through the array
sequence.splice(i,1); // Remove an item from the array
s_seq: // The name of the loop, so that we can exit from it
for (var j = 0; j < mainSequence.length-1; j++) { // Loop through sliced array, to find an increasing sequence
if (sequence[j+1] <= sequence[j]) { // Check if next is smaller
exists = false; // if so define false
break s_seq; // exit from the loop
}
exists = true; // if the loop has not been interrupted, return true
}
sequence = mainSequence.slice(); // Reassign the main sequence to be used again.
//console.log('s',mainSequence);
if (exists) {break m_seq;} // İf condition is met, why bother to stay in the loop???
}
return exists;
}
var testing = [1,2,3,4,3,4,5];
var testingAgain = [1,2,3,4,3,7,9];
var testingFinal = [1,1];
// almostIncreasingSequence(testing);
console.log(almostIncreasingSequence(testing));
console.log(almostIncreasingSequence(testingAgain));
console.log(almostIncreasingSequence(testingFinal));

How to understand this recursion

Hello all I am trying to understand this solution to combination sum.
function combinationSum(candidates, target) {
var result = [];
if ((candidates == null) || (candidates.length == 0)) {
return result;
}
var cur = [];
candidates = candidates.sort((a, b) => a - b)
csh(candidates, target, cur, result, 0);
return result;
};
function csh(cand, target, cur, result, j) {
//console.log(cur);
if (target == 0) {
var temp = cur.slice();
result.push(temp);
return;
}
for (var i = j; i < cand.length; i++) {
if (target < cand[i]) {
return;
}
//console.log(cur);
cur.push(cand[i]);
console.log(cur);
csh(cand, target - cand[i], cur, result, i);
cur.pop();
}
}
https://leetcode.com/problems/combination-sum/description/
While I understand the basic principles of recursion this problem is a little bit lost on me. So for example for the input:
candidates = [2,3,6,7]
target = 7
When you first enter the function cur is empty so our first iteration is:
[],
[2],
[2,2]
And then we keep adding cand[i] which is currently 2
[2,2,2]
However at this point, target = 1 which is less than cand[i] which is 2 so we return. And since we're returning we pop off the stack which pops the last 2 off the stack. Since we've returned we increment i and then we add 3 to cur
[2,2,3]
Since our target array is equal to 0 we now return again and my question is, at this point do we keep returning until cur is empty and continue the function like the following?
[2,2]
[2]
[]
[6]
[]
[7]
I'm just trying to understand what is being done in this function.
target is local to each invocation of the function. target is 0 only for some invocations of the function. Notice that the recursive invocation is:
csh(cand, target - cand[i], cur, result, i);
The target in that scope has not change, but the invocation of csh currently being entered will have a lower value for its target. When that function returns and the program flow reenters that other level, we resume using the higher value of target, insted of the reduce value target - cand[i] that was supplied to the subcall.
The algorithm will try all other possibilities on the [2,2,...] path as well, before changing the second element to the next alternative. Then it will explore the [2,3,...] space, and the [2,6,...] space, and ultimately all the [3,...], [6,...] and [7,...] possibilities as well.
The algorithm always goes as deep as a possible (i.e., as long of an array as possible) when it can do so without going over the original limit.
Note that it does not produce [2,3,2], because an earlier candidate cannot come after a later one (so a 2 can never be subsequent to a 3 in a result). It enforces this by making the for look start at i = j, where j is the array-depth of the last candidate used, so when the result-in-progress ends with the nth candidate, it only considers nth and higher candidates. this is of practical value because the algorithm only returns one permutation of each value result set: [2,2,3] and [2,3,2] contain the same set of values.
I completely understand that recursion can be very difficult to understand and to explain as well, but here is my take on it.
When csh is called for the 1st time, this is what is being passed
csh(cand, 7, [], [], 0)
Now from the for loop, i = 0, function called is
csh(cand, 5, [2], [], 0)
from the loop, i = 0, function called is
csh(cand, 3, [2,2], [], 0)
from the loop, i = 0, function called is
csh(cand, 1, [2,2,2],[],0)
from the for loop, target(1) < cand[0](2), so return to step Step 4 and pop the last 2 from [2,2,2] resulting in [2,2]
from the loop i = 1, function called is
csh(cand, 0, [2,2,3], [], 1)
here, target == 0 condition is met. so, [2,2,3] is pushed in the result. and then return to step 4. again, 3 is popped from [2,2,3].
from the loop i = 2, target(3) < cand[2](6), so return to step 3. and pop 2 from [2,2] resulting in [2].
from the loop i = 1, function called is
csh(cand, 2, [2,3], [[2,2,3]], 1)
from the loop i = 1, target(2) < cand[1](1) so return to step 9.
and so no...
Basically, each and every combination will be checked.
[2,2,2]
[2,2,3] --> added to result
[2,3,3]
[2,6]
[2,7]
[3,3,3]
[3,6]
[3,7]
[6,6]
[6,7]
[7] --> added to res

Categories

Resources