I was trying out the Codility MaxCounter question:
You are given N counters, initially set to 0, and you have two possible operations on them:
increase(X) − counter X is increased by 1,
max_counter − all counters are set to the maximum value of any counter.
A non-empty zero-indexed array A of M integers is given. This array represents consecutive operations:
if A[K] = X, such that 1 ≤ X ≤ N, then operation K is increase(X),
if A[K] = N + 1 then operation K is max_counter.
For example, given integer N = 5 and array A such that:
A[0] = 3
A[1] = 4
A[2] = 4
A[3] = 6
A[4] = 1
A[5] = 4
A[6] = 4
the values of the counters after each consecutive operation will be:
(0, 0, 1, 0, 0)
(0, 0, 1, 1, 0)
(0, 0, 1, 2, 0)
(2, 2, 2, 2, 2)
(3, 2, 2, 2, 2)
(3, 2, 2, 3, 2)
(3, 2, 2, 4, 2)
The goal is to calculate the value of every counter after all operations.
For example, given:
A[0] = 3
A[1] = 4
A[2] = 4
A[3] = 6
A[4] = 1
A[5] = 4
A[6] = 4
the function should return [3, 2, 2, 4, 2].
Assume that:
N and M are integers within the range [1..100,000];
each element of array A is an integer within the range [1..N + 1].
Complexity:
expected worst-case time complexity is O(N+M);
expected worst-case space complexity is O(N), beyond input storage (not counting the storage required for input arguments).
Elements of input arrays can be modified.
Here is my solution, for which i used the reduce method. It scores 40% on performance.
Can anyone see where the performance issue is here?
I'm assuming perhaps it is the reduce speed itself thats the problem and that in order to increase this score i would need to convert this to for loops, but this just feels like a really ugly way to use modern javascript in this context.
Hopefully one of you will point out something non JS related about this solution that will not indicate that reduce is the issue and instead indicate that im an idiot (I will deal with the ramifications of this over a cold beer)
function maxCounter(N, A) {
let maxCounter = 0
const NArray = new Array(N).fill(0)
const results = A.reduce((acc, current) => {
if (current === N + 1) return new Array(N).fill(maxCounter)
const out = acc.map((element, index) => {
if (index + 1 === current){
const newValue = element + 1
if (newValue > maxCounter) maxCounter = newValue
return newValue
} else {
return element
}
})
return out
}
, NArray)
return results
}
const results = maxCounter(5, [1,4,2,5,2,6,2])
console.log({results})
You could introduce a min value, which is set if all values have to be set to the max value, but this does happen only if the value is incremented, then the min value is used for update or at the end to give all items at least the min value.
function maxCounter(n, a) {
var min = 0,
max = 0,
result = [],
i;
for (i of a) {
if (--i === n) {
min = max;
continue;
}
if (!result[i] || result[i] < min) result[i] = min;
if (++result[i] > max) max = result[i];
}
for (i = 0; i < n; i++) {
if (!result[i] || result[i] < min) result[i] = min;
}
return result;
}
console.log(...maxCounter(5, [3, 4, 4, 6, 1, 4, 4]));
console.log(...maxCounter(5, [1, 4, 2, 5, 2, 6, 2]));
The time complexity of your solution is O(NM), which exceeds the required complexity of O(N + M). The reason is that your solution builds a new array of length N for each of the M queries.
This isn't because you used reduce, but rather because you used reduce on an array of length M with a reduction operation which takes O(N) time. If your reduction operation took O(1) time instead, then you would be fine.
It is possible to achieve O(1) time per query by updating the counters array in-place; the hard part is answering the "set-all" query in O(1) time, i.e. without updating all N elements of the counter array. One solution is to record a kind of "timestamp" for the last time a counter was modified, and another "timestamp" for the last time "set-all" was done; this allows you to test if a counter's value is older than the most-recent "set-all" query, and get the correct counter value either way. Here's a class which answers both queries in O(1) using this technique:
class Counters {
constructor(n) {
this.setallVal = 0;
this.setallTimestamp = 0;
this.maxCounter = 0;
this.counts = new Array(n).fill(0);
this.timestamps = new Array(n).fill(0);
}
getCounter(i) {
if (this.timestamps[i] >= this.setallTimestamp) {
return this.counts[i];
} else {
return this.setallVal;
}
}
incCounter(i) {
let c = this.getCounter(i) + 1;
this.counts[i] = c;
this.timestamps[i] = this.setallTimestamp;
if (c > this.maxCounter) {
this.maxCounter = c;
}
}
setAllToMax() {
this.setallVal = this.maxCounter;
this.setallTimestamp++;
}
}
#kaya3 Thanks!
I tried this too which gets 60% but i assume falls over with the same issue of setting the max across all elements
function solution(N, A) {
let maxCounter = 0
const NArray = new Array(N).fill(0)
for (i=0; i<A.length; i++){
if (A[i]>N){
NArray.fill(maxCounter)
} else {
const currentVal = NArray[A[i] -1] + 1
if (currentVal > maxCounter) maxCounter = currentVal
NArray[A[i] -1] = currentVal
}
}
return NArray
}
Related
I am trying to solve a leetcode type problem that is a practice problem that came with an upcoming code test I need to do for a job and I am having trouble with it. Can anyone help me understand whats going wrong?
I am essentially looking for the brute force option as I dont know algos/DS.
PROBLEM:
Write a function:
function solution(A);
that, given an array A of N integers, returns the smallest positive integer (greater than 0) that does not occur in A.
For example, given A = [1, 3, 6, 4, 1, 2], the function should return 5.
Given A = [1, 2, 3], the function should return 4.
Given A = [−1, −3], the function should return 1.
Write an efficient algorithm for the following assumptions:
N is an integer within the range [1..100,000];
each element of array A is an integer within the range [−1,000,000..1,000,000].
HERE IS MY SOLUTION:
function solution(A) {
let newArray = A.sort(function(a, b){return a-b})
let lowestNumber = 1
for(i=0; i < newArray.length; i++) {
if(lowestNumber > newArray[0]) {
return lowestNumber
}
if(lowestNumber == newArray[i]) {
lowestNumber = lowestNumber + 1
}
if(i = newArray.length - 1) {
return lowestNumber
}
}
}
The below snippet isnt working like I expect it to. lowestNumber isnt being increased and also the loop is exiting here I believe.
if(lowestNumber == newArray[i]) {
lowestNumber = lowestNumber + 1
Thanks for your help!
You can do this in O(N) using a Map():
First set every number in the array.
Then starting from 1 look for and return the missing number in the sequence.
function solution(arr) {
const seen = new Map();
for (let i = 0; i < arr.length; i++) {
seen.set(arr[i]);
}
for (let i = 1; i <= arr.length + 1; i++) {
if (!seen.has(i)) return i;
}
return 1;
}
console.log(solution([1, 3, 6, 4, 1, 2])); //-> 5
console.log(solution([1, 2, 3])); //-> 4
console.log(solution([-1, -3])); //-> 1
I think your > should be <, and the = in if(i = newArray.length - 1) should be ===.
And lowestNumber > newArray[0] will always be true if the array contains a negative number, so 1 will be returned.
Your effort seems careless, so you are going to have to up your game for the interview.
const integers = [5, -345, 562456, 95345, 4, 232, 1, 2, 3, 7, -457];
function solution(A) {
let newArray = A.sort((a, b) => a - b);
let lowestNumber = 1;
for (let i = 0; i < newArray.length; i++) {
const n = newArray[i];
if (n > 0) {
if (lowestNumber < n) {
return lowestNumber;
} else {
lowestNumber = n + 1;
}
}
}
return lowestNumber;
}
console.log(solution(integers));
The fastest solution
function solution(A) {
// write your code in JavaScript (Node.js 8.9.4)
if (!A) return 1;
A.sort();
if (A[A.length - 1] < 1) return 1;
const setA = new Set(A);
let length = setA.size;
for (let i = 1; i <= length; i++) {
if (!setA.has(i)) {
return i;
}
}
return length + 1;
}
I have worked same problem for nowadays, and regardless the original answer, here is my version of finding least positive number which is missing the in the array.
function findLeastPositive(array) {
const numbersSet = new Set(array);
let leastPositiveNumber = 1;
while(numbersSet.has(leastPositiveNumber)) {
leastPositiveNumber++;
}
return leastPositiveNumber;
}
let result = findLeastPositive([1,2,3,4,5,6,7,8,9,0]);
console.log(result);
result = findLeastPositive([10,11,12,13,14,15,16,17,18,19]);
console.log(result);
There are sure similar answers floating on the internet but using given array length disturbing me of which I can't explain properly why we have to create second loop starts with 1 (known the least positive number) and to N.
Using hash table (I am using Set here) for lookup table is fine idea, as in it effect to overall performance O(N) (probably initialize the Set with array) and O(1) for checking if the value in the Set or not.
Then we need to set second loop for obvious reason that checking the the smallest positive number existence, starting from 1..N range. This is the part bugged me, so I decided to go for while loop. It's obvious rather why there's a for..loop starts from 1..N on which N is the length of the array.
Here is 100% code
function solution(A) {
/**
* create new array of positive numbers in given array,
* if there sis no specific number in given array, in result array
* that index will be undefine
*/
const c = A.reduce((arr, cur) => {
if(cur > 0) arr[cur] = 1;
return arr;
} , [1] )
/**
* return first undefined index
*/
for(let i = 0; i < c.length; i++)
if(!c[i]) return i;
// otherwise return the maximum index in array
return c.length;
}
function solution(arr) {
for (let i = 1; i <= arr.length + 1; i++) {
if (!arr.includes(i)) return i;
}
return 1;
}
console.log(solution([1, 3, 6, 4, 1, 2])); //-> 5
console.log(solution([1, 2, 3])); //-> 4
console.log(solution([-1, -3])); //-> 1
I am working through some problems on CodeSignal. I came across this one, arrayMaxConsecutiveSum. I got it to pass almost all tests, but it is timing out on the last one. If I move the test into custom tests, it passes there, so I'm not sure what to do. How do I code it better so that it doesn't time out?
Problem:
Given array of integers, find the maximal possible sum of some of its k consecutive elements.
Example: For inputArray = [2, 3, 5, 1, 6] and k = 2, the output should be
arrayMaxConsecutiveSum(inputArray, k) = 8. All possible sums of 2 consecutive elements are:
2 + 3 = 5;
3 + 5 = 8;
5 + 1 = 6;
1 + 6 = 7.
Thus, the answer is 8.
function arrayMaxConsecutiveSum(inputArray, k) {
let max = 0;
for(let i = 0; i < inputArray.length; i++){
let sum = 0;
let newArr = inputArray.slice(i, i + k);
sum = newArr.reduce((accumulator, currentVal) => accumulator + currentVal);
if(sum > max){
max = sum;
}
}
return max;
}
Error: Tests passed: 19/20. Execution time limit exceeded on test 20: Program exceeded the execution time limit. Make sure that it completes execution in a few seconds for any possible input.
Your current algorithm is O(n ^ 2) because it requires a nested loop.
You can make it O(n) by using a rolling sum instead. Start with the sum of elements 0 to k, then on each iteration, subtract the earliest element that makes up the sum and add the next element not included in the sum yet.
For example, with a k of 2:
start out with the sum of elements [0] and [1]
subtract [0], add [2], compare the new sum
subtract [1], add [3], compare the new sum
and so on.
function arrayMaxConsecutiveSum(inputArray, k) {
let rollingSum = inputArray.slice(0, k).reduce((a, b) => a + b);
let max = rollingSum;
for(let i = 0; i < inputArray.length - k; i++){
rollingSum += inputArray[i + k] - inputArray[i];
max = Math.max(max, rollingSum);
}
return max;
}
console.log(arrayMaxConsecutiveSum([2, 3, 5, 1, 6], 2));
function arrayMaxConsecutiveSum(inputArray, k) {
var max = inputArray.slice(0,k).reduce((a,b)=>a+b);
var cur = max;
for(var i = k; i < inputArray.length; i++) {
cur = cur + inputArray[i] - inputArray[i-k];
if(cur>max)
max = cur
}
return max
}
console.log(arrayMaxConsecutiveSum([2, 3, 5, 1, 6], 2));
I'm not sure if I describe the question right, So I'll give a simple example.
Let's say I have an array:
var array = [0, 1.1, 2, 2.4, 4, 4.6, 5];
Given a number, 2.1
I want to find what index interval does it fall in.
For this question, the answer will be 2,3.
Currently I have two ideas for this, first one is simple but definitely very slow, which is loop through the whole array and find where array[i-1] is less than 2.1 and array[i] is greater than 2.1.
Another way is, add 2.1 to the array, sort the array in ascending order, the answer will be the index of 2.1, and this index - 1.
Any other better suggestions?
You would do a binary search:
function binarySearch(array, el) {
var m = 0;
var n = array.length - 1;
while (m <= n) {
var k = (n + m) >> 1;
var cmp = el - array[k];
if (cmp > 0) {
m = k + 1;
} else if(cmp < 0) {
n = k - 1;
} else {
return [k, k + 1];
}
}
return [n, n+1];
}
var range = binarySearch([0, 1.1, 2, 2.4, 4, 4.6, 5], 2.3);
console.log(range);
The above code:
Assumes that the array elements are all different and sorted ascending
Returns a pair of indexes [i, i+1]
Returns the pair of indexes such that array[i] <= el < array[i+1]
If the given value is outside the range of the array, the output will be [-1, 0] when it is lower than the first array value, or [n-1, n] when it is greater than the last array value (n being the array's length).
Has a O(log(n)) time complexity.
You can do using simple quicksort-style insertion function
var array = [0, 1.1, 2, 2.4, 4, 4.6, 5];
var element = 2.1;
function insert(element, array) {
array.splice(locationOf(element, array) + 1, 0, element);
return array;
}
function locationOf(element, array, start, end) {
start = start || 0;
end = end || array.length;
var pivot = parseInt(start + (end - start) / 2, 10);
if (end-start <= 1 || array[pivot] === element) return pivot;
if (array[pivot] < element) {
return locationOf(element, array, pivot, end);
} else {
return locationOf(element, array, start, pivot);
}
}
console.log(insert(element, array));
That can be achieved using binary insert algorithm.
Complexity:
It is in best case O(1) and in worst case O(N). Even if the Binary Insert in itself is only O(log(N)), the O(N) comes from the insert which will always be O(N) in the worst case if you insert a value at the beginning – you would have to push the rest of the values (N) to the end.
For instance, O(n) will be when you want to push 4 in follow array: let array=[3,6,9,10,20,21,23,24,26]
It takes O(log(N)) for search and O(n) for add.
How binary insert works:
var array = [0, 1.1, 2, 2.4, 4, 4.6, 5];
function binaryInsert(value, array, startVal, endVal){
var length = array.length;
var start = typeof(startVal) != 'undefined' ? startVal : 0;
var end = typeof(endVal) != 'undefined' ? endVal : length - 1;
var m = start + Math.floor((end - start)/2);
if(length == 0){
array.push(value);
return;
}
if(value > array[end]){
array.splice(end + 1, 0, value);
return;
}
if(value < array[start]){
array.splice(start, 0, value);
return;
}
if(start >= end){
return;
}
if(value < array[m]){
binaryInsert(value, array, start, m - 1);
return;
}
if(value > array[m]){
binaryInsert(value, array, m + 1, end);
return;
}
}
let number=2.1;
binaryInsert(number, array,0,array.length);
console.log(array.toString());
let index=array.indexOf(number);
console.log(index-1,index);
From a given array of positive integers, I want to know if the sum of E elements from the array is equal to a given number N.
For example, given the array arr = [1, 2, 3, 4] , e = 3 and n = 9. It means if the sum of 3 elements in arr equals to 9. The result is true since 2 + 3 + 4 is equal to 9.
Another example with arr = [1, 2, 3, 4] , e = 2 and n = 7. It is true since 3 + 4 is equal to 7.
I'm trying to resolve it with recursion, but I'm stuck. My idea is to nest loops dynamically to walk through the elements to the array and compare them.
My attempt is this:
function subsetsum(arr, elements, n) {
loop(arr, elements, n, [], 0);
}
function loop(arr, elements, n, aux, index) {
if(aux.length != elements) {
aux[index] = arr.length - 1;
loop(arr, elements, n, aux, index + 1);
} else {
if ((elements - index + 1) < 0) {
return 0;
} else {
if (aux[elements - index + 1] > 0) {
aux[elements - index + 1]--;
loop(arr, elements, n, aux, index);
}
}
}
}
subsetsum([1, 2, 3, 4], 3, 9));
A related question is at Find the highest subset of an integer array whose sums add up to a given target. That can be modified to restrict the number of elements in the subset as follows:
// Find subset of a, of length e, that sums to n
function subset_sum(a, e, n) {
if (n < 0) return null; // Nothing adds up to a negative number
if (e === 0) return n === 0 ? [] : null; // Empty list is the solution for a target of 0
a = a.slice();
while (a.length) { // Try remaining values
var v = a.shift(); // Take next value
var s = subset_sum(a, e - 1, n - v); // Find solution recursively
if (s) return s.concat(v); // If solution, return
}
}
I've been playing around with this for a while and decided to use a short-cut, mainly the permutation code from this previous SO question.
My code uses basically uses the permutation code to create an array of all the possible permutations from the input array, then for each array (using map) grabs a slice corresponding to the number specified as amount, sums that slice and if it is the same as total returns true.
some then returns the final result as to whether there are any permutations that equals the total.
function checker(arr, amount, total) {
var add = function (a, b) { return a + b; }
return permutator(arr).map(function(arr) {
var ns = arr.slice(0, amount);
var sum = ns.reduce(add);
return sum === total;
}).some(Boolean);
}
checker([1, 2, 3, 4], 3, 9); // true
I've included two demos - 1) a demo showing this code, and 2) code that provides a more detailed breakdown: basically map returns an object containing the slice info, the sum totals and whether the condition has been met.
This is probably not what you're looking for because it's a bit long-winded, but it was certainly useful for me to investigate :)
Edit - alternatively here's a hacked version of that permutation code from the previous question that delivers the results and an array of matches:
function permutator(inputArr, amount, total) {
var results = [], out = [];
function permute(arr, memo) {
var cur, memo = memo || [];
var add = function (a, b) { return a + b; }
for (var i = 0; i < arr.length; i++) {
cur = arr.splice(i, 1);
if (arr.length === 0) {
results.push(memo.concat(cur));
}
var a = arr.slice();
// this is the change
var sum = memo.concat(cur).reduce(add);
if (memo.concat(cur).length === amount && sum === total) {
out.push(memo.concat(cur))
}
permute(a, memo.concat(cur));
arr.splice(i, 0, cur[0]);
}
return [results, out];
}
return permute(inputArr);
}
permutator([1,2,3,4], 3, 9);
DEMO
If I understand you correctly, the solution of this task must be simple like this:
function subsetsum(arr, countElements, sum) {
var length = arr.length-1;
var temp = 0;
var lastElement = length-countElements;
console.log(lastElement);
for (var i = length; i > lastElement; i--) {
temp = temp+arr[i];
console.log('Sum: '+temp);
}
if (temp === sum) {
console.log('True!');
} else {console.log('False!')}
};
subsetsum([1, 2, 3, 4], 2, 7);
I have an array full of numbers. Here's an example:
myArray = [0,1,2,4,5];
I need to find the lowest unused number starting from 1, so in this case it will be 3.
I've been reading up of using indexOf but I'm unsure how to use it for my specific purpose.
Assuming the array isn't sorted, you always start at 0, and taking into account your desire to find a highest number if there isn't one missing:
var k = [6, 0, 1, 2, 4, 5];
k.sort(function(a, b) { return a-b; }); // To sort by numeric
var lowest = -1;
for (i = 0; i < k.length; ++i) {
if (k[i] != i) {
lowest = i;
break;
}
}
if (lowest == -1) {
lowest = k[k.length - 1] + 1;
}
console.log("Lowest = " + lowest);
Logs answer 3. If 3 was also in there, would log 7 since no other number is missing.
If you aren't always starting at zero, use an offset:
var k = [6, 2, 3, 4, 5];
k.sort(function(a, b) { return a-b; }); // To sort by numeric
var offset = k[0];
var lowest = -1;
for (i = 0; i < k.length; ++i) {
if (k[i] != offset) {
lowest = offset;
break;
}
++offset;
}
if (lowest == -1) {
lowest = k[k.length - 1] + 1;
}
console.log("Lowest = " + lowest);
Logs answer 7 since none are missing after 2 which starts the sequence.
This takes a sequence starting from a number (like 1 in your example) and returns the lowest unused number in the sequence.
function lowestUnusedNumber(sequence, startingFrom) {
const arr = sequence.slice(0);
arr.sort((a, b) => a - b);
return arr.reduce((lowest, num, i) => {
const seqIndex = i + startingFrom;
return num !== seqIndex && seqIndex < lowest ? seqIndex : lowest
}, arr.length + startingFrom);
}
Example:
> lowestUnusedNumber([], 1)
1
> lowestUnusedNumber([1,2,4], 1)
3
> lowestUnusedNumber([3], 1)
1
In exchange for readability, it's slightly less optimized than the other example because it loops over all items in the array instead of breaking as soon as it finds the missing item.