Big O time efficiency for a quadratic function in JavaScript - javascript

I am trying to increase the efficiency of a function. It is currently quadratic and I would like to make it logarithmic.
The third to last line of the current function is confusing me somewhat as well and I would like some clarification.
function solution(arr){
let result = 0
for ( let i = 0; i < arr.length; i++)
for (let j = 0; j < arr.length; j++)
if (arr[i] == arr[j])
result = Math.max(result, Math.abs(i - j));
return result;
}
How do I solve this problem?

At least, you could change the indices for looping and omit self checking and to check the same pairs again.
function solution(arr){
let result = 0
for (let i = 0; i < arr.length - 1; i++)
for (let j = i; j < arr.length; j++)
if (arr[i] === arr[j])
result = Math.max(result, Math.abs(i - j));
return result;
}
The shortest approach is O(n) by taking an hash table for storing the first found index for a value.
function solution(array) {
var hash = {};
return array.reduce(
(m, v, i) => Math.max(m, i - (hash[v] = v in hash ? hash[v] : i)),
0
);
}
var array = [1, 3, 4, 5, 1, 3, 4, 5, 6, 2, 3];
console.log(solution(array));

In the above function, the goal is to find the maximum number from array. Now the meaning of third to last line which is result = Math.max(result, Math.abs(i - j)); , I will break it into two parts to explain here,
First of all, Math.abs(i-j) will be executed and provide the absolute value from the difference between i and j.
After this, the outer function Math.max() method will be called which will provide you the maximum value between result and absolute value obtained from first step. Now the maximum value will be store in result. This is how the function is working.
Now this statement is conditional based, which means it will only execute if arr[i]==arr[j].
I hope it cleared the work flow of this program.

Related

Is that a valid Insertion Sort Algorithm? or is it bubble sort? I'm confused

let swapFun = (arrToSwap, indexFir, indexSec) => {
let temp = arrToSwap[indexFir]
arrToSwap[indexFir] = arrToSwap[indexSec]
arrToSwap[indexSec] = temp
}
let insertionSort = (arr, n = 0) => {
if (n === arr.length) {
return arr
}
for (let i = 1; i < arr.length; i++) {
while (arr[i - 1] > arr[i]) {
swapFun(arr, i - 1, i)
}
}
insertionSort(arr, n + 1)
return arr
}
console.log(insertionSort([5, 4, 33, 2, 8]))
It is actually Bubble Sort, but it is a bad implementation of Bubble Sort.
If you notice the for loop:
for (let i = 1; i < arr.length; i++) {
while (arr[i - 1] > arr[i]) {
swapFun(arr, i - 1, i)
}
}
it basically iterates the array, starting from index 1, to the end of the array, and for each item, it compares the item with the previous item. The comparison is made in the while loop, but it actually is not a loop (it just executes the body if item at index i-1 is bigger than item at index 1.) It can be replaced with a if statement:
for (let i = 1; i < arr.length; i++) {
if (arr[i - 1] > arr[i]) {
swapFun(arr, i - 1, i)
}
}
The for loop is executed in total n-1 times, because n=0 at the start and gets incremented each time the function gets called recursively, until it reached n=array length (this time the for loop will not be executed.) The body of the for-loop gets executed array.length-1 times (4 times in your example).
The biggest problem with this implementation, is the use of Recursion, which will use more Space (because Recursion uses a Stack). It also is not optimized regarding Time. Even if the array was already sorted, the for loop would be executed N-1 times, and its body will also be executed N-1 times. Which means that even in the Best Case (array is already sorted), this Bubble sort would have O(n^2) Time complexity, when in fact it can be O(n).
The optimized version of Bubble Sort:
const sort = array => {
let isSorted;
for (let i=0; i < array.length; i++) {
isSorted = true;
for (let j=1; j < array.length - i; j++)
if ( array[j] < array[j-1]) {
swap(array, j, j-1);
isSorted = false;
}
if (isSorted)
return;
}
}
const swap = (array, index1, index2) => {
let temp = array[index1];
array[index1] = array[index2];
array[index2] = temp;
}
const array = [5, 4, 33, 2, 8]
sort(array);
console.log(array);
The variable "isSorted", keeps track if array is Sorted or not. If no swaps were made, it means array is sorted, and I can stop the execution of the function.
Also note that in the inner loop: for (let j=1; j < array.length - i; j++) , we do not check the items at the "sorted part" (each time the inner loop if executed, one item goes to it's "final" index in the array and we do not need to make any comparisons with these items).
Hope this helped! Please ask if I did not explain something properly.

HackerRank - Minimum Swaps 2 Timeout

This challenge asks that you find the minimum number of swaps to sort an array of jumbled consecutive digits to ascending order. So far my code passes most of the tests, however there are four that fail due to timeout. Could anyone explain why my code is timing out? Is there a way to make it find an answer faster?
function minimumSwaps(arr) {
const min = Math.min(...arr);
let swapCount = 0;
const swap = (array, a, b) => {
let test = array[a];
array[a] = array[b];
array[b] = test;
}
for(let i=0; i<arr.length; i++){
if(arr[i]!==i+min){
swap(arr, i, arr.indexOf(i+min));
swapCount++;
}
}
return swapCount;
}
I thought it was a good solution since it only has to iterate over the length of the array once? I'd love to be able to understand why this isn't performing well enough
Your big issue is with the call to arr.indexOf, as that has to scan the entire array each time you do a swap. You can work around that by generating a reverse lookup from value to array index before starting to sort, and then maintaining that list during the sort. Note that you don't need to do a full swap, only copy the value from arr[i] to its correct place in the array since you don't revisit a number once you have passed it. Also you don't need min, as under the conditions of the question it is guaranteed to be 1, and you don't need to look at the last value in the array since by the time you get to it it has to be correct.
function minimumSwaps(arr) {
const indexes = arr.reduce((c, v, i) => (c[v] = i, c), []);
const len = arr.length - 1;
let swapCount = 0;
for (let i = 0; i < len; i++) {
if (arr[i] !== i + 1) {
arr[indexes[i+1]] = arr[i];
indexes[arr[i]] = indexes[i+1];
swapCount++;
}
}
return swapCount;
}
console.log(minimumSwaps([7, 1, 3, 2, 4, 5, 6]));
console.log(minimumSwaps([4, 3, 1, 2]));
console.log(minimumSwaps([2, 3, 4, 1, 5]));
console.log(minimumSwaps([1, 3, 5, 2, 4, 6, 7]));
I think we should flip function not search function.
function minimumSwaps($arr) {
$outp = 0;
$result = array_flip($arr);
for($i=0; $i<count($arr); $i++){
if($arr[$i] != $i +1){
$keyswp = $result[$i + 1];
$temp = $arr[$i];
$arr[$i] = $i + 1;
$arr[$keyswp] = $temp;
$tempr = $result[$i + 1];
$result[$i + 1] = $i +1;
$result[$temp] = $keyswp;
$outp = $outp + 1;
}
}
return $outp;
}
Here is my solution , it is similar to Nick's except instead of the Array.Proptytype.indexOf method I used an object literal to map the indices of each value for a better Time complexity since search in object literal is O(1) (You can use ES6 maps too). Here is the code
function minimumSwaps(arr) {
let count = 0;
let search = arr.reduce((o, e, i) => {
o[e] = i
return o
}, {})
for(let i =0 ; i < arr.length - 1; i++){
let j = search[i+1]
if(arr[i] !== i+1) {
[arr[i], arr[j]] = [arr[j], arr[i]]
search[arr[i]] = i;
search[arr[j]] = j;
count += 1
}
}
console.log(arr)
return count
}
As suggested by others, you shouldn't use the indexOf method as it makes your solution O(n^2) (As the indexOf method has to scan the entire array again). You can create a position map array before hand and use it later during the swap. It will keep your solution linear. Here is the detailed explanation and solution to the HackerRank Minimum Swaps 2 Problem in java, c, c++ and js, using this method.

How to return the length of a contiguous subarray with specific criteria

I'm tasked with creating an array that takes another array and returns the number of contiguous subarrays for each index that fulfills the following conditions:
-The value at index i is the maximum value of the subarray
-The contiguous subarray must begin or end with i
I'm almost there but I'm missing the code to ensure that the function checks the array elements that aren't sat directly either side of i (see my code below). I think I might need some sort of recursive call that runs the if statement again while passing through the updated values of 'forward' and 'backward'. Or maybe I'm taking the wrong approach altogether.
Any ideas?
function countSubarrays(arr){
var arr = [3, 4, 1, 6, 2];
var output = [];
for (var i = 0; i < arr.length; i++){
var total = 1;
var forward = 1;
var backward = 1;
if (arr[i] >= arr[i+forward]) {
total++;
forward++;
// Some sort of recursive call here?
}
if (arr[i] >= arr[i-backward]){
total++;
backward++;
// Some sort of recursive call here?
}
output.push(total);
}
console.log(output);
}
countSubarrays();
You need to go backwards or ahead and count the items who are smaller or equal to the actual element.
function countSubarrays(array) {
var output = [];
for (let i = 0; i < array.length; i++) {
let value = array[i],
total = 1;
j = i;
while (j-- && value >= array[j]) total++;
j = i;
while (++j < array.length && value >= array[j]) total++;
output.push(total);
}
return output;
}
console.log(countSubarrays([3, 4, 1, 6, 2]));

Javascript - Arrays - for-loop to forEach

Given an array of integers where every value appears twice except one, find the single, non-repeating value.
Follow up: do so with O(1) space.
1) This is incorrect, the idea is to iterate through twice and compare if any value the first time around is not equal to a the 2nd go around. If not, push the non equal value into a new array and return that.
2) Is forEach pretty much the same as a for-loop?
How could this be rewritten with a forEach?
This is not giving me the output I'd like, which for this example,
should just return 4
CODE
const nonRepeat = arr => {
let newArray = [];
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length; j++) {
if (arr[i] !== arr[j]) {
newArray.push(arr[i])
}
}
}
return newArray
}
console.log(nonRepeat([2, 5, 3, 2, 1, 3, 4, 5, 1]));
Convert from for loop to forEach loop
for
for (let i = 0; i < arr.length; i++) { [code] }
forEach eliminates need to create the iterative variable ( for example, i) and always checks every element in the array
arr.forEach( [code] )
See Here for Additional Syntax Help

Missing Number Algorithm Timeout Error (JavaScript)

I'm working on a Codewars Kata that is passing all tests, except it keeps timing out. Can anyone offer advice on how to optimize this? Thanks in advance!
Here is the question breakdown -
In this kata, we have an unsorted sequence of consecutive numbers from a to b, such that a < b always (remember a, is the minimum, and b the maximum value).
They were introduced an unknown amount of duplicates in this sequence and we know that there is an only missing value such that all the duplicate values and the missing value are between a and b, but never coincide with them.
Find the missing number with the duplicate numbers (duplicates should be output in a sorted array).
Let's see an example:
arr = [10,9,8,9,6,1,2,4,3,2,5,5,3]
find_dups_miss([10,9,8,9,6,1,2,4,3,2,5,5,3]) == [7,[2,3,5,9]]
And here is my solution -
function findDupsMiss(arr) {
let missingNum = [];
let newArr = [];
arr = arr.sort((a, b) => a - b);
let dup = [...new Set(arr)];
for (let y = 1; y < dup.length; y++) {
if (dup[y] - dup[y - 1] != 1) missingNum.push(dup[y] - 1)
}
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) newArr.push(arr[i])
}
}
missingNum.push(newArr);
return missingNum;
}
You could take the power of an object and the standard sorting of keys who could be used as indices of an array (positive 32 bit numbers).
This attempt has two parts
count all numbers
iterate all keys of the object with their appearances and
check if it has the missing number between the actual item and the previous item. If so, assign the missing values,
check the count and push the key if the count is greater than one.
This code completes in 7410 ms.
function findDupsMiss(arr) {
var hash = Object.create(null),
i = arr.length,
l,
v,
keys,
missing,
dupes = [],
previous, item;
while (i--) {
v = arr[i];
if (!hash[v]) {
hash[v] = 0;
}
hash[v]++;
}
keys = Object.keys(hash);
l = keys.length;
for (i = 0; i < l; i++) {
item = +keys[i];
if (previous + 1 !== item) {
missing = previous + 1;
}
if (hash[item] > 1) {
dupes.push(item);
}
previous = item;
}
return [missing, dupes];
}

Categories