LeetCode problem 26 - Remove Duplicates From Sorted Array
Given an integer array nums sorted in non-decreasing order, remove the duplicates in-place such that each unique element appears only once. The relative order of the elements should be kept the same.
Since it is impossible to change the length of the array in some languages, you must instead have the result be placed in the first part of the array nums. More formally, if there are k elements after removing the duplicates, then the first k elements of nums should hold the final result. It does not matter what you leave beyond the first k elements.
Return k after placing the final result in the first k slots of nums.
Do not allocate extra space for another array. You must do this by modifying the input array in-place with O(1) extra memory.
var removeDuplicates = function (nums) {
// Iterating the full array
for (let i = 0; i < nums.length; i++) {
// Checking for the repeating number
if (nums[i] === nums[i + 1]) {
// Removing the element which is repeating
nums = nums.slice(0, i + 1).concat(nums.slice(i + 2));
// Resetting the index after removing the element
i--;
}
}
console.log(nums);
return nums.length;
};
console.log(removeDuplicates([0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 4]));
This is the output of the above JS code on Visual Studio Code.
I am not able to submit this code to LeetCode. The expected output is the edited nums array, whereas my output doesn't match. How do I resolve this issue?
Your solution is too complicated. To remove duplicates from an array, just do:
const removeDuplicates = (arr) => [...new Set(arr)]
console.log(removeDuplicates([0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 4]));
I am trying to understand why this part -- arr.splice(index, 1).push(0) -- of my code is not working.
why can't I splice and then push?
var moveZeros = function (arr) {
const newArray = arr.map((element, index) => element === 0 ? arr.splice(index, 1).push(element) : console.log(index, element))
}
Array.splice() returns an array of the elements that are removed from the array on which splice() was called on. Find documentation here:
Array.splice() documentation
You can achieve your goal by following code snippet:
let arr = [1, 0, 2, 0, 1, 0, 1, 0, 1, 0]
var moveZeros = function (arr) {
let zeroes = [];
let a = arr.reduce((finalArray, element) => {
element === 0 ? zeroes.push(element) : finalArray.push(element)
return finalArray;
}, []);
return [...a, ...zeroes];
}
console.log(moveZeros(arr));
splice returns an array of the removed elements. If you do
arr.splice(index, 1).push(0)
then you're pushing to the array of removed zeros (and that expression is subsequently discarded immediately afterwards).
You also shouldn't mutate an array while iterating over it - that'll make for very confusing behavior.
Consider filtering out zeros, while pushing to an array of zeros instead, and then combine the two arrays afterwards:
const moveZeros = (arr) => {
const zeros = [];
return arr
.filter((elm) => {
if (elm === 0) {
zeros.push(0);
return false;
}
return true;
})
.concat(zeros);
};
console.log(moveZeros([0, 1, 2, 3]));
console.log(moveZeros([3, 2, 1,0, 1, 2, 3]));
You could take a sorting instead and move all zeroes to the end. This approach rely on modern user agents which keep a stable sorting for equal (the onces whoes callback return zero) elements.
var array = [0, 1, 9, 2, 0, 0, 3, 5, 4, 2];
array.sort((a, b) => !a - !b);
console.log(array)
Giving an array, say [4,2,1,3,5], based on this array, we have a new array, which each number shows the count of elements on its left that are bigger than itself, which is [0,1,2,1,0]. Now write a function with given input of [0,1,2,1,0], return the original array. The range of array is 1 ~ n (n is the size of the array, you can assume all numbers in the original array are consecutive if sorted)
Now to recover the original array, I have tried a way to solve the problem by iterating through the range of array from the end to the front like this:
My approach:
say the range is 1 ~ 5, the original array would be [1, 2, 3, 4, 5] if sorted. Iterate from the end to the beg,
so first 5, there is no element can be bigger than 5, so its maximum count of bigger elements would be 0, then 4 would have 1 as its maximum count of bigger elements, 3 to 2, etc. Store the key-value pairs into an object.
Now iterating through the input from back to front,
0 -> 5
1 -> can be 4, 3, or 2
2 -> can be either 3, 2, or 1
1 -> any number bigger than the first one.
0 -> (can be anything, since 5 is taken, so it can be either 1, 2, 3, or 4)
Simply to map each element of the input as value to its key from the map is not enough. What would be an intuitive way to approach this with optimal performance? (avoiding O(n ^2) if possible.)
Initially make an AVL Tree from numbers 1 to n.
Start from rear i.e. at nth index (considering 1 based index).
Now the high level outline level of the algorithm should look like this:
1. At any ith index, say the number in array(not the originial array) is j
2. Search the number which is at (i-j)th position in your AVL tree(This can be done in O(logn) time. Comment if you need more explanation on this)
3. The element in the AVL tree is your required element. Delete that element from AVL tree.(O(logn))
So the total complexity would be O(nlogn).
Walkthrough
Initially the tree will contain all 5 elements.
You start at index 5(1-based indexing). Element is 0, i.e. i=5, j=0. So 5th largest element which is 5.
Now the tree contains four elements 1,2, 3, and 4. i=4, j=1. So 4-1 i..e 3rd largest element which is 3 in this case.
i=3, j=2. (3-2)rd largest element is 1 since the tree contains (1, 2, 4).
And so on.
Using Tree to find the ith largest number
We can do this by, storing the count of number of nodes in left subtree at the root node. So consider a tree, having elements 1, 2, 3,4 and 5 and tree structure as following:
4(3)
/ \
3(1) 5(0)
/ \
1(0) 2(0)
At root, number 4 is the value and the number in round bracket has the number of nodes in left subtree.
While constructing(insertion and deletion too) the tree, we can maintain the count.
Now, to find the ith node, say we want suppose 3rd nodes in the given tree. We start with the root, it says it has 3 elements smaller than it to the left so we move to left. Now the root i.e. 3 has 1 smaller left element which is less than 3(ith element) so we move to right of it. Subtract 1(the left count)+1(the root itself) out of 3. Now the root is 2 we want 1st element, the left count is 0. Hence the 1st element of the subtree rooted at 2 is 2.
Basic pseudocode is below:
while(true){
count = root->leftCount;
if((count+1)<i){
//move to right
i-=(count+1);
root = root->right;
}
else if(i==(count+1)){
//root is the ith node
break;
} else{
//move to the levft
root=root->left
}
}
You could use Array#reduceRight and use the value as negative index for generating the original array.
var array = [1, 2, 3, 4, 5],
countLeft = [0, 1, 2, 1, 0],
result = countLeft.reduceRight(function (r, a) {
return array.splice(array.length - 1 - a, 1).concat(r);
}, []);
console.log(result);
Shorter version with ES6 and reverse base array.
var array = [5, 4, 3, 2, 1],
countLeft = [0, 1, 2, 1, 0],
indices = array.map((_, i) => i),
result = [];
countLeft.forEach(a => {
result.unshift(array[indices[a]]);
indices = indices.filter((_, i) => i !== a);
});
console.log(result);
At last a proposal with complexity between O(n*(n-1)/2) and O(n).
This version uses a lazy array with progressive reduction of the length for every iteration. At the end, the offset array has zero elements.
var array = [5, 4, 3, 2, 1],
countLeft = [0, 1, 2, 1, 0],
result = [],
length = array.length;
countLeft.forEach((offset => (offset.length = countLeft.length, a => {
var i = offset[a] || 0;
result.unshift(array[i + a]);
offset.length--;
while (i < offset.length) {
offset[i] = (offset[i] || 0) + 1;
i++;
}
}))([]));
console.log(result);
A linear version, heavily inspired by the proposal of Oriol
var array = [1, 2, 3, 4, 5],
countLeft = [0, 1, 2, 1, 0],
swap = [],
i = 0,
l,
temp;
while (i < countLeft.length) {
l = countLeft[i];
while (l) {
swap.push(i + l - countLeft[i]);
l--;
}
i++;
}
i = swap.length;
while (i--) {
temp = array[swap[i]];
array[swap[i]] = array[swap[i] - 1];
array[swap[i] - 1] = temp;
}
console.log(array);
Here is a possible solution. See inline comments for a brief description of this method.
var a = [0,1,2,1,0],
n, b = [], res = [];
// build b = [5,4,3,2,1]
// we use this array to keep track of values to be pushed in res[],
// sorted in descending order
for(n = a.length; n > 0; n--) {
b.push(n);
}
// for each element of a, starting from the end:
// find correct value in b and remove it from b
while(a.length) {
res.push(b.splice(a.pop(), 1)[0]);
}
res = res.reverse();
console.log(res);
Output:
[4, 2, 1, 3, 5]
I propose an approach based on a custom sort, based on mergesort:
Split the array of inversions into two halves
Sort each part recursively, from greatest to lowest, maintaining stability
Merge the two parts
The difference with mergesort is the merge part. If we choose the j-th element of right part instead of the i-th of the left one, it will advance some elements, and therefore its number of inversions must be reduced by that amount.
Like mergesort, the complexity is O(n log n)
function undoInversions(inversions) {
function reorder(arr, from=0, to=arr.length) {
// Based on a stable decreasing mergesort
if(from >= to) return []; // Unusual base case
if(to === from + 1) return [arr[from]]; // Base case
var m = Math.floor((from + to)/2);
var l = reorder(arr, from, m), // Left recursive call
r = reorder(arr, m, to), // Right recursive call
ret = [], i=0, j=0;
while(i < l.length && j < r.length) { // Merge
if(r[j].value - l.length + i >= l[i].value) {
r[j].value -= l.length - i; // Reduce number of inversions
ret.push(r[j++]);
} else {
ret.push(l[i++]);
}
}
while(i < l.length) ret.push(l[i++]); // Merge remaining, if any
while(j < r.length) ret.push(r[j++]); // Merge remaining, if any
return ret;
}
var array = new Array(inversions.length);
reorder(inversions.map(function(inv, idx) {
return {value: inv, originalIndex: idx}; // Keep track of indices
})).forEach(function(obj, idx) {
if(obj.value !== 0) throw 'Invalid input';
array[obj.originalIndex] = idx + 1; // Invert the permutation
});
return array;
}
console.log(JSON.stringify(undoInversions([0,1,2,1,0])));
Here is an example to understand how it works:
[0,1,2,1,0] ~ [4,2,1,3,5]
⤩ ⤧
[0,0,2,1,0] ~ [2,4,1,3,5]
⤩ ⤧
[0,1,0,1,0] ~ [2,1,4,3,5]
⤩ ⤧
[0,0,0,1,0] ~ [1,2,4,3,5]
⤩ ⤧
[0,0,0,0,0] ——→ [1,2,3,4,5]
That is, each array of inversions corresponds to a permutation. We apply a permutation σ which transforms the input to the array of inversions [0,0,0,0,0], which corresponds to the permutation [1,2,3,4,5]. Since we kept track of the original indices, now we only need the to apply σ⁻¹ to [1,2,3,4,5] in order to get the permutation corresponding to the input.
Using Javascript, I am trying to convert some JSON data into the format used in Highcharts' basic line chart.
What I have to start with:
originalArray = [
['valueA', 1],
['valueA', 0],
['valueB', 9],
['valueB', 9],
['valueB', 3],
['valueC', 11]
]
And what I'm trying to create using the above:
desiredArray = [{
name: 'valueA',
data: [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}, {
name: 'valueB',
data: [0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0]
}, {
name: 'valueC',
data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
}]
For some additional context, the 0-11 in originalArray[i][1] references a month (0 = January), and the desiredArray is a list of unique names and a count of their occurrences by month.
So far, I can:
Convert the data into a new array of objects
For each unique name in originalArray
Create a new object in the desiredArray, and set the name attribute
Add a data attribute that contains an empty array
But then I run into trouble, and can't figure out how to:
Loop through the originalArray
If the name in the originalArray matches the name in the desiredArray
Increment a counter in the matching seriesArray[i].data array, using the value of originalArray[i][1] as the index (it always be 0-11).
So I'm asking:
What's a good way to iterate across my originalArray, match up unique values, and then act only on those matches to push to the desiredArray.
What's the best way to increment the counters in desiredArray[i].data
I'm open to using libraries, such as underscore.js. Have been trying to figure this out for a couple of days now, so pretty much anything goes within the bounds of Javascript.
Updated with proper array initialization, now.
var max = originalArray.reduce(function(p,c){return Math.max(p,c[1]);},0);
var initSums = function(size) {
var arr = new Array(size);
for (var i=0;i<size;i++)
arr[i]=0;
return arr;
}
var map = originalArray.reduce(function(sums,val){
if (!sums.hasOwnProperty(val[0])) {
sums[val[0]] = initSums(max+1);
}
sums[val[0]][val[1]]++;
return sums;
},{});
var desiredArray = Object.keys(map).map(function(key) {
return {name: key, data: map[key]};
});
What we're doing here is a multi-step process:
Decide how big our arrays are going to need to be, by first scanning for the largest value in the original array.
Use an object to aggregate the counts (using Array.reduce()).
Transform the object and its properties into an array of name/data pair objects (using Array.map).
Edit: An improvement on S McCochran's solution, skipping the extraneous search for the maximum value in originalArray, since there should always be 12 elements of each data array, one per month.
function formatForHighcharts(array) {
// Create a map from value name to array of month counts
var map = originalArray.reduce(function(sums, pair) {
var key = pair[0], val = pair[1];
if(!(key in sums))
sums[key] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
// val is the month index, which corresponds directly to array index, so increase that
sums[key][val]++;
return sums;
}, {});
// Map the object to an array of { name: ..., data: ... } pairs
var formatted = Object.keys(map).map(function (key) {
return { name: key, data: map[key] };
});
return formatted;
}
Usage:
var desiredArray = formatForHighcharts(originalArray);