I am having some problems turning this solution from O(n^2) to O(n). Can anyone kindly help? I am not able to think of any ways to make the time complexity O(n).
//MERGE SORTED ARRAY
/*arr1 = [0,3,4,31]
arr2 = [4,6,30]*/
const mergeSortedArrays = (arr1, arr2) => {
let arr = [];
let flag = true;
// MERGING ARRAYS
for (let i = 0; i < arr1.length; i++) {
arr.push(arr1[i]);//PUSHING ARRAY1 in arr
}
for (let i = 0; i < arr2.length; i++) {
arr.push(arr2[i]);//PUSING ARRAY2 in arr
}
//SORTING ARRAYS
while (flag) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] > arr[i + 1]) {
let temp = arr[i + 1];
arr[i + 1] = arr[i];
arr[i] = temp;
flag = true;
} else {
flag = false;
}
}
}
console.log(arr1);
console.log(arr2);
console.log(arr);//FINAL MERGED & SORTED ARRAY
// return arr;
}
mergeSortedArrays([0, 3, 4, 31], [4, 6, 30]);
Try visualising it. It is as if you had two sorted stacks of cards you wanted to sort. You can compare cards at the top of each stack, and put the smaller value on a third stack. And repeat until all cards are on the third sorted stack.
You can keep up two pointers, i and j, one for each array. This will emulate a stack.
The algorithm:
Repeat until the end of both arrays is reached:
if arr1[i] <= arr2[j]
push arr1[i] to the merged array and increment i
else
push arr2[j] to the merged array and increment j
And some javascript code:
let merged = [];
let i = 0;
let j = 0;
while(i < arr1.length || j < arr2.length){
if(j == arr2.length || (i < arr1.length && arr1[i] <= arr2[j])){
merged.push(arr1[i]);
i++;
} else{
merged.push(arr2[j]);
j++;
}
}
You can use two pointer method
(this solution is based on the assumption that the two lists will always be sorted):
let p1 = 0, p2 = 0;
let arr = [];
while (p1 < arr1.length && p2 < arr2.length) {
if (arr1[p1] < arr2[p2])
arr.push(arr1[p1++]);
else
arr.push(arr2[p2++]);
}
while (p1 < arr1.length)
arr.push(arr1[p1++]);
while (p2 < arr2.length)
arr.push(arr2[p2++]);
This code will run at the time complexity of O(N).
Updated answer based on comments from using Array#sort() to:
const mergeSortedArrays = (arr1, arr2) => {
const array = { arr1, arr2 }
const index = { a1: 0, a2: 0 }
const length = { a1: array.arr1.length, a2: array.arr2.length }
const merged = []
let current = 0
while (current < length.a1 + length.a2) {
const [a, i] =
!(index.a1 >= length.a1) &&
(index.a2 >= length.a2 || array.arr1[index.a1] < array.arr2[index.a2])
? ['arr1', 'a1']
: ['arr2', 'a2']
merged[current] = array[a][index[i]]
index[i]++
current++
}
return merged
}
const result = mergeSortedArrays([0, 3, 4, 31], [4, 6, 30])
console.log(result)
Related
I wonder if there's an easy to implement equivalent to python's array.pop() which returns the deleted element while deleting it from the array in parallel in javascript.
let nums = [2, 1, 3, 4, 5, 6];
function sortArray(nums) {
let arr = new Array();
let smallest_index;
for (let j = 0; j < nums.length; j++) {
smallest_index = find_smallest(nums);
console.log("smallest", smallest_index);
arr.push(nums.splice(smallest_index, 1).join(""));
}
return arr;
}
function find_smallest(arr) {
let smallest = arr[0];
let smallest_index = 0;
for (let i = 1; i < arr.length; i++) {
if (arr[i] < smallest) {
// console.log("this");
smallest = arr[i];
smallest_index = i;
}
}
return smallest_index;
}
it seems that if I replace javascript's (nums.splice(smallest_index, 1).join()) with python's (arr.append(nums.pop(smallest_index)) I would get a perfectly sorted array. is there a similar straightforward solution in javascript as well ?
OK, you use splice. Here's an example of the implementation below:
Array.prototype.pythonPop = function (index) {
return this.splice(index, 1)[0];
}
Now, I found the issue, you'll love the answer. So you were using num.length but your methods were augmenting the length of num array. Which is why your answer had only half the needed numbers. See code below. I cached the length prop of nums array
let nums = [2, 1, 3, 4, 5, 6];
function sortArray(nums) {
let arr = new Array();
let smallest_index;
console.log(nums)
for (let j = 0, length = nums.length; j < length; j++) {
smallest_index = find_smallest(nums);
console.log("smallest", smallest_index);
console.log(nums)
arr[j] = nums.splice(smallest_index, 1).join("");
}
return arr;
}
function find_smallest(arr) {
let smallest = arr[0];
let smallest_index = 0;
for (let i = 1; i < arr.length; i++) {
if (arr[i] < smallest) {
// console.log("this");
smallest = arr[i];
smallest_index = i;
}
}
return smallest_index;
}
console.log(sortArray(nums))
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.
My target here is to find 'N' for a 2D Array.
'N' = sum of corner elements * sum of non corner elements.
For 'N' calculation I change String & Boolean elements to their ASCII, 1 or 0 respectively. But my original array gets altered in this process.
Can't understand why?
function findN(arr) {
var temp = [...arr]
// first we change all elements to numbers
for (let i = 0; i < temp.length; i++) {
for (let j = 0; j < temp.length; j++) {
if (typeof temp[i][j] == 'string') {
temp[i][j] = temp[i][j].charCodeAt()
} else if (temp[i][j] == true) {
temp[i][j] = 1
} else if (temp[i][j] == false) {
temp[i][j] = 0
}
}
}
// N calculation starts here
let r = temp.length // rows
let c = temp[0].length // columns
var corner_Sum =
temp[0][0] + temp[0][c - 1] + temp[r - 1][0] + temp[r - 1][c - 1]
var total_Sum = 0
for (let i = 0; i < temp.length; i++) {
for (let j = 0; j < temp.length; j++) {
total_Sum = total_Sum + arr[i][j]
}
}
var N = corner_Sum * (total_Sum - corner_Sum)
return N
}
findN() ends here. It should return 'N', without altering the original array. As all calculations were done on temp array. But that's not the case.
Your problem is because arr is an array of arrays; when you copy it using
temp = [...arr]
temp becomes an array of references to the same subarrays in arr. Thus when you change a value in temp it changes the corresponding value in arr. You can see this in a simple example:
let arr = [[1, 2], [3, 4]];
let temp = [...arr];
temp[0][1] = 6;
console.log(arr);
console.log(temp);
To work around this, use a deep copy such as those described here or here. For example, if arr is at most 2-dimensional, you can nest the spread operator:
let arr = [[1, 2], [3, 4]];
let temp = [...arr.map(a => [...a])];
temp[0][1] = 6;
console.log(arr);
console.log(temp);
I've tried merging the two arrays but the output i get is [ 0, 3, 3, 4, 4 ]
function mergeSortedArrays(arr1, arr2) {
var i = 0;
var j = 0;
var arr3 = [];
if (arr1 === undefined || arr1.length == 0) {
return arr2;
}
if (arr2 === undefined || arr2.length == 0) {
return arr1;
}
while (i < arr1.length - 1 && j < arr2.length - 1) {
if (arr1[i] < arr2[j]) {
arr3.push(arr1[i]);
i++;
} else {
arr3.push(arr2[j]);
j++;
}
}
return arr3;
}
console.log(mergeSortedArrays([0, 3, 4, 31], [3, 4, 6, 30]));
For this example, i know i haven't accounted for the case in which arrays are of different size but that is for a later problem. The code currently isn't even working for the basic case. It does not iterate all the way through and breaks midway. Can someone please address this problem. I've worked around with the while loop but the code still doesnt work.
You are making two mistakes.
In the while loop condition check of length of array not length - 1. This will not add last elements of both array.
Your while loop will end when one of the array will be completely loop because of &&. So after while add the remaining elements of other array.
function mergeSortedArrays(arr1, arr2) {
var i = 0;
var j = 0;
var arr3 = [];
if (arr1 === undefined || arr1.length == 0) {
return arr2;
}
if (arr2 === undefined || arr2.length == 0) {
return arr1;
}
while (i < arr1.length && j < arr2.length) {
if (arr1[i] < arr2[j]) {
arr3.push(arr1[i]);
i++;
} else {
arr3.push(arr2[j]);
j++;
}
}
if(i === arr1.length){
return arr3.concat(arr2.slice(j))
}
else if(j === arr2.length){
return arr3.concat(arr1.slice(i))
}
}
console.log(mergeSortedArrays([0, 3, 4, 31], [3, 4, 6, 30]));
First of all, the loop needs to iterate till arr1.lenght and arr2.lenght not till lenght - 1,
while (i < arr1.length && j < arr2.length )
Also, after the loop breaks, the reason being either one of the array loop variables has a false condition, before returning the array, you need to check if there are values left which are not yet inserted. This is important.
You can do this:
arr3.concat(arr1.slice(i)).concat(arr2.slice(j));
Hope it helps.
There are two things missing in your code:
The while loop is running for i < arr1.length - 1 for arr1 and j < arr2.length - 1 for arr2. That means it will run till 3rd index of arr1 and arr2. So the last index will not be executed in the loop. So you should refactor the loop as i < arr1.length for arr1 and j < arr2.length for arr2.
Next missing thing, you need to add the loop for remaining elements of remaining array. That means when you execute the code then all the elements of one loop will be pushed in the arr3 then one elements of another array will be missed and it will not be pushed in the arr3 (for same length of both array) and/or more than one elements of another array will be missed (for different length of both array). So you need to push the remaining elements of another array to the arr3.
I have added the refactored code of your code.
function mergeSortedArrays(arr1, arr2) {
var i = 0;
var j = 0;
var arr3 = [];
if (arr1 === undefined || arr1.length == 0) {
return arr2;
}
if (arr2 === undefined || arr2.length == 0) {
return arr1;
}
while (i < arr1.length && j < arr2.length) {
if (arr1[i] < arr2[j]) {
arr3.push(arr1[i]);
i++;
} else {
arr3.push(arr2[j]);
j++;
}
}
while (i < arr1.length) {
arr3.push(arr1[i]);
i++;
}
while (j < arr2.length) {
arr3.push(arr2[j]);
j++;
}
return arr3;
}
OR
you can use spread operator to merge both array and then you can use sort method of JS.
function mergeTwo(arr1, arr2) {
let result = [...arr1, ...arr2];
return result.sort((a,b) => a-b);
}
There is a simple one line solutions that does involve a resort. Using default parameters to deal with undefined arrays, and the spread operator to populate a new array with the two merging arrays. The result array just needs to be sorted and then returned.
const mergeSorted = (a1 = [], a2 = []) => [...a1, ...a2].sort((a, b) => a - b);
Example
const mergeSorted= (a1 = [], a2 = []) => [...a1, ...a2].sort((a, b) => a - b);
console.log(mergeSorted([0, 3, 4, 31], [3, 4, 6, 30]).join(", "));
Or without the sort and using parameter defaults to avoid needing to test arrays for undefined
function mergeSorted(a1 = [], a2 = []) {
const res = [];
var idx1 = 0, idx2 = 0;
while (idx1 < a1.length || idx2 < a2.length) {
if (idx1 < a1.length && idx2 < a2.length) {
res.push(a1[idx1] < a2[idx2] ? a1[idx1++] : a2[idx2++]);
} else {
res.push(idx1 < a1.length ? a1[idx1++] : a2[idx2++]);
}
}
return res;
}
Example
function mergeSorted(a1 = [], a2 = []) {
const res = [], l1 = a1.length, l2 = a2.length;
var i1 = 0, i2 = 0;
while (i1 < l1 || i2 < l2) {
if (i1 < l1 && i2 < l2) { res.push(a1[i1] < a2[i2] ? a1[i1++] : a2[i2++]) }
else { res.push(i1 < l1 ? a1[i1++] : a2[i2++]) }
}
return res;
}
console.log(mergeSorted([0, 3, 4, 31, 99], [3, 4, 6, 30]).join(", "));
In the while condition when i or j indexes reach the length of the corresponding array finishes the loop returning an array of the lower length.
This works:
function mergeTwoSortedArrays(arr1, arr2) {
let merged = [];
let index1 = 0;
let index2 = 0;
let current = 0;
while (current < (arr1.length + arr2.length)) {
let isArr1Depleted = index1 >= arr1.length;
let isArr2Depleted = index2 >= arr2.length;
if (!isArr1Depleted && (isArr2Depleted || (arr1[index1] < arr2[index2]))) {
merged[current] = arr1[index1];
index1++;
} else {
merged[current] = arr2[index2];
index2++;
}
current++;
}
return merged;
}
Fore more info : https://wsvincent.com/javascript-merge-two-sorted-arrays/
I need help merging two arrays without using any of the array built in functions ( no concat, push, pop, shift, replace, sort, splice, etc)
And I've got to this point but I'm stuck.
function addTwoArrays(arr1, arr2){
var merge = [], p;
for(p = 0; p < arr1.length; p++){
merge[arr1[p]] = true;
}
for(p = 0; p < arr2.length; p++){
merge[arr2[p]] = true;
}
return Object.keys(merge);
}
window.alert(addTwoArrays([1,2,3,4],[4,3,2,1]));
return is 1,2,3,4 - instead of 1,2,3,4,4,3,2,1
You only need to loop once - simply take arr1.length as a start index and add to the array:
function addTwoArrays(arr1, arr2) {
let start = arr1.length;
for (let i = 0; i < arr2.length; i++) {
arr1[start++] = arr2[i];
}
return arr1;
}
console.log(addTwoArrays([1, 2, 3, 4], [4, 3, 2, 1]));
Keys are unique in a JSON object. So, Object.keys() will return unique occurrences of each element.
Instead try this:
function addTwoArrays(arr1, arr2){
var merge = [], p, index = 0;
for(p = 0; p < arr1.length; p++){
merge[index++] = arr1[p];
}
for(p = 0; p < arr2.length; p++){
merge[index++] = arr2[p];
}
return merge;
}
window.alert(addTwoArrays([1,2,3,4],[4,3,2,1]));
function mergedArray(arrayOne, arrayTwo) {
let newArr = arrayOne
let x = arrayOne.length
let y = arrayTwo.length
let z = arrayOne.length + arrayTwo.length
let i, j
for (i = x, j = 0; i < z && j < y; i++, j++) {
newArr[i] = arrayTwo[j]
}
return newArr
}