Per Codefighters:
Note: Write a solution with O(n) time complexity and O(1) additional
space complexity, since this is what you would be asked to do during a
real interview.
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, 3, 3, 1, 5, 2], the output should be firstDuplicate(a) =
3.
There are 2 duplicates: numbers 2 and 3. The second occurrence of 3
has a smaller index than than second occurrence of 2 does, so the
answer is 3.
For a = [2, 4, 3, 5, 1], the output should be firstDuplicate(a) = -1.
So here is what I came up with. It works but fails on the final test because it ran over 4000ms. I'm at a loss as to what else I can do. Any Ideas to improve speed?
function firstDuplicate(a) {
var test = [],
lowest = undefined;
for (var i=0; i<a.length; i++) {
if (test.indexOf(a[i]) > -1) {
lowest = lowest || i;
if (i < lowest) {
lowest = i;
}
}
else {
test.push(a[i]);
}
}
return lowest ? a[lowest] : -1;
}
Here was my second attempt but still failing on the last test...
function firstDuplicate(a) {
var low = undefined,
last = -1;
for (var i=0; i<a.length; i++) {
last = a.lastIndexOf(a[i])
if (last > i && (low === undefined || last < low)) {
low = last;
}
}
return low !== undefined ? a[low] : -1;
}
The requirements give a clue of how to solve this. The set of numbers contained in the array must match the following critera:
only numbers in the range from 1 to a.length
In other words, only positive numbers that are less than or equal to the length of the array. If the array contains ten numbers, none of them will be greater than 10.
With that insight, we have a means of keeping track of numbers that we have already seen. We can treat the numbers themselves as indexes into the array, modify the element at that index (in this case by making it negative) and if we run into the same number and the element at that index is less than zero, then we know we have seen it.
console.clear()
const test1 = [2, 3, 3, 1, 5, 2]
const test2 = [2, 4, 3, 5, 1]
function firstDuplicate(a) {
for (let i of a) {
let posi = Math.abs(i) - 1
if (a[posi] < 0) return posi + 1
a[posi] = a[posi] * -1
}
return -1
}
console.log(firstDuplicate(test1))
console.log(firstDuplicate(test2))
console.log(firstDuplicate([2,2]))
console.log(firstDuplicate([2,3,3]))
console.log(firstDuplicate([3,3,3]))
Original Incorrect Answer
Keep track of what numbers have already been seen and return the first one that has been seen before.
console.clear()
const test1 = [2, 3, 3, 1, 5, 2]
const test2 = [2, 4, 3, 5, 1]
function firstDuplicate(a){
const seen = {}
for (let v of a){
if (seen[v]) return v
seen[v] = v
}
return -1
}
console.log(firstDuplicate(test1))
console.log(firstDuplicate(test2))
As pointed out in the comments, however, this answer takes O(n) additional space, not O(1) additional space.
We will take advantage of the fact that the array a contains only numbers in the range from 1 to a.length, to remember that a value has been seen by reversing the sign of whatever is in that position in the array.
function lowestDuplicate(arr) {
for (let i = 0; i < arr.length; i++) {
const val = Math.abs(arr[i]);
if (arr[val - 1] < 0) return val;
arr[val - 1] = -arr[val - 1];
}
return -1;
}
console.log(lowestDuplicate([1, 2, 3, 4, 3, 2, 1]));
console.log(lowestDuplicate([1, 2, 3, 4, 5]));
console.log(lowestDuplicate([5, 4, 3, 2, 2]));
console.log(lowestDuplicate([2, 2]));
console.log(lowestDuplicate([2, 3, 3]));
console.log(lowestDuplicate([3, 3, 3]));
console.log(lowestDuplicate([2, 3, 3, 1, 5, 2]));
Python 3 version that passes the tests.
def firstDuplicate(a):
oldies={}
notfound=True
for i in range(len(a)):
try:
if oldies[a[i]]==a[i]:
notfound=False
return a[i]
except:
oldies[a[i]]=a[i]
if notfound:
return -1
You are iterating n times in both examples.
What if the array length was 200,000,000 and the first duplicate was found at index 3? The loop is still running 200,000,000 times unnecessarily.
So the idea is to break out of the loop once you find the first duplicate.
you can use break or just return.
A simple solution in JS. As the question title focus, you just need to find the first duplicate of the number you already traversed. So i guess this will do:
function solution(a) {
let duplicateArray = []
for(let i=0;i<a.length;i++){
if(duplicateArray.includes(a[i])){
return a[i];
}
duplicateArray.push(a[i])
}return -1;}
Hope this helps.
Related
Given an array of integers, where the values should be sorted in the following order:
if we have an array
[1, -1, -3, 9, -2, -5, 4, 8,]
we must rearrange it this way: largest number, smallest number, 2nd largest number, 2nd smallest number, ...
[9, -5, 8, -3, 4, -2, 1, -1 ]
I get the first largest and smallest numbers, but can't figure out how to make it dynamic for all values in the array.
I know that I must take two variables, say firstSmallest and firstLargest and point them to the first and last index of the array respectively, run a loop, which I do already in the code below, and store value into new array by incrementing firstSmallest and decrementing firstLargest, but couldn't implement into code.
let unsortedArr = [1, 5, 8 , 7, 6, -1, -5, 4, 9, 5]
let output = [];
function meanderArray(unsorted){
let sorted = unsorted.sort((a, b) => a-b);
let firstSmallest = sorted[0];
let firstLargest = sorted[unsorted.length-1];
for(let i = 0; i <= sorted.length; i++){
//I should increment firstSmallest and decrement firstLargest numbers and store in output
}
return output;
}
meanderArray(unsortedArr);
console.log(output);
You could take a toggle object which takes the property of either the first item or last from an array and iterate until no more items are available.
function meanderArray([...array]) {
const
result = [],
toggle = { shift: 'pop', pop: 'shift' };
let fn = 'shift';
array.sort((a, b) => a - b);
while (array.length) result.push(array[fn = toggle[fn]]());
return result;
}
console.log(...meanderArray([1, 5, 8, 7, 6, -1, -5, 4, 9, 5]));
You can sort an array by descending, then logic is the following: take first from start and first from end, then second from start-second from end, etc.
let unsortedArr = [1, 5, 8 , 7, 6, -1, -5, 4, 9, 5]
let output = [];
function meanderArray(unsorted){
let sorted = unsorted.sort((a, b) => b-a);
let output = []
for(let i = 0; i < sorted.length/2; i++){
output.push(sorted[i])
if(i !== sorted.length - 1 - i){
output.push(sorted[sorted.length - 1 - i])
}
}
return output;
}
let result = meanderArray(unsortedArr);
console.log(result);
You can sort, then loop and extract the last number with pop() and extract the first number with shift().
let unsortedArr = [1, -1, -3, 9, -2, -5, 4, 8,]
let output = [];
function meanderArray(unsorted){
let sorted = unsorted.sort((a, b) => a - b);
for(let i = 0; i < unsortedArr.length + 2; i++){
output.push(sorted.pop());
output.push(sorted.shift());
}
console.log(output);
return output;
}
meanderArray(unsortedArr);
Fastest Meandering Array method among all solutions mentioned above.
According to the JSBench.me, this solution is the fastest and for your reference i have attached a screenshot below.
I got a different approach, but i found that was very close to one of above answers from elvira.genkel.
In my solution for Meandering Array, First I sorted the given array and then i tried to find the middle of the array. After that i divided sorted array in to two arrays, which are indices from 0 to middle index and other one is from middle index to full length of sorted array.
We need to make sure that first half of array's length is greater than the second array. Other wise when applying for() loop as next step newly created array will contains some undefined values. For avoiding this issue i have incremented first array length by one.
So, always it should be firstArr.length > secondArr.length.
And planned to create new array with values in meandering order. As next step I created for() loop and try to push values from beginning of the first array and from end of the second array. Make sure that dynamically created index of second array will receive only zero or positive index. Other wise you can find undefined values inside newly created Meandering Array.
Hope this solution will be helpful for everyone, who loves to do high performance coding :)
Your comments and suggestions are welcome.
const unsorted = [1, 5, 8, 7, 6, -1, -5, 4, 9, 5];
const sorted = unsorted.sort((a,b)=>a-b).reverse();
const half = Math.round(Math.floor(sorted.length/2)) + 1;
const leftArr = sorted.slice(0, half);
const rightArr = sorted.slice(half, sorted.length);
const newArr = [];
for(let i=0; i<leftArr.length; i++) {
newArr.push(leftArr[i]);
if (rightArr.length-1-i >= 0) {
newArr.push(rightArr[rightArr.length-1-i]);
}
}
Given a list of numbers with only 3 unique numbers(1, 2, 3), sort the list in O(n) time. Plus sort the array using constant space O(1).
Example:
Input: [3, 3, 2, 1, 3, 2, 1]
Output: [1, 1, 2, 2, 3, 3, 3]
Here the solution i made (is no O(1) space and have empty spaces spaces in the array..):
What does this function is simple .. increases the size of the arrangement to double in the case that all its elements are 2; Then it goes to its previous length (current/2) to sort its elements .. if it is 1 it does nothing, if it finds a 2 it puts it in the previous maximum length + 1, it increases the variable len and eliminates the element and if it is 3 push and delete the element .. then you have empty spaces in the array and you don't meet the plus of the problem but it's O(n).
function sort(list) {
let len = list.length;
list.length=len*2
for(let i=0; i<list.length/2; i++){
let n=list[i]
if(n==2){
list[len]=n
delete list[i]
len++
}else if(n==3){
list.push(n)
delete list[i]
}
}
return list
}
console.log(sort([1,2,3,2,1,1]))
You could use the algorithm of the Dutch national flag problem:
The Dutch national flag problem 1 is a computer science programming problem proposed by Edsger Dijkstra (In a chapter of his book A Discipline of Programming Prentice-Hall, 1976). The flag of the Netherlands consists of three colors: red, white and blue. Given balls of these three colors arranged randomly in a line (the actual number of balls does not matter), the task is to arrange them such that all balls of the same color are together and their collective color groups are in the correct order.
var array = [3, 3, 2, 1, 3, 2, 1],
MID = 2,
i = 0,
j = 0,
n = array.length - 1;
while (j <= n) {
if (array[j] < MID) {
[array[i], array[j]] = [array[j], array[i]];
i++;
j++;
} else if (array[j] > MID) {
[array[n], array[j]] = [array[j], array[n]];
n--;
} else {
j++;
}
}
console.log(array);
You can count the number of occurrences of 1, 2, 3 and use that info to recreate/get the sorted array:
const arr = [3, 3, 2, 1, 3, 2, 1]
const count = arr.reduce((acc, curr) => {
acc[curr]++;
return acc;
}, {1: 0, 2: 0, 3: 0})
arr.forEach((_, j) => {
if (j < count[1])
arr[j] = 1
else if (j < count[1] + count[2])
arr[j] = 2
else
arr[j] = 3
})
console.log(arr)
Because of the fact that you know that the array can contain only 3 items, you can iterate over the entire array for each one of them, (this means that the algorithm runs 3*n times = O(3n) = O(n)).
The space restriction indicates that you need to work in-place, means, work on the input array.
This is my solution :]
function swap(i, j, arr) {
const currentVal = arr[j];
arr[j] = arr[i];
arr[i] = currentVal;
}
function sort(arr) {
let globalIndex = 0;
[1, 2, 3].forEach(item => {
for (let i = globalIndex; i < a.length; i++) {
if (arr[i] === item) {
swap(i, globalIndex, arr);
globalIndex++;
}
}
});
}
const a = [1, 2, 3, 2, 1, 1];
sort(a);
console.log(a);
According to me, you can simply initialize three variables as 0 for the counts of each 1, 2 and 3. Traverse the array once and increment the corresponding counter by 1 for values at every index i.e. if the value at a particular index is 2, increment the second variable by 1.
Once, you got the counts, (Count1 - 1)th index will be the last occurrence of 1, (Count1 + Count2 - 1)th index will be the last occurrence of 2 and (Count1 + Count2 + Count3 - 1)th index will be the last occurrence of 3.
You can traverse the whole array and assign the values accordingly.
This way is somewhat like Counting Sort but is of course not stable. However, you have another option as already mentioned in previous answers -Dutch National Flag Problem
I followed a simple bruteforce approach which still gives O(n) at the end. After all O(2N) + O(3) = O(N)
import sys
def sortNums(nums):
res = {}
mi = sys.maxsize
ma = -1 * sys.maxsize
mid = 0
for i in nums: #O(n)
if i in res:
res[i] += 1
else:
res[i] = 1
if mi > i:
mi = i
if ma < i:
ma = i
for ele in res.keys(): #O(3)
if ele != mi and ele != ma:
mid = ele
return ([mi] * res[mi]) + ( [mid] * res[mid] ) + ([ma] * res[ma]) # O(n)
print(sortNums([3, 3, 2, 1, 3, 2, 1]))
# [1, 1, 2, 2, 3, 3, 3]
I am having trouble with a problem and how to address it. Mind you, I am relatively new to JavaScript and the problem feels like I am over complicating it.
The problem:
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].
Thanks for all your comments! I wanted to update this to be a better question, and also inform you I have found a solution if someone would like to check it and see if there is a cleaner way to put it. :)
function almostIncreasingSequence(sequence) {
if(sequence.length == 2) return true;
var error = 0;
for(var i = 0; i < sequence.length - 1; i++){
if(sequence[i] >= sequence[i+1]){
var noStepBack = sequence[i-1] && sequence[i-1] >= sequence[i+1];
var noStepFoward = sequence[i+2] && sequence[i] >= sequence[i+2];
if(i > 0 && noStepBack && noStepFoward) {
error+=2;
}else{
error++;
}
}
if(error > 1){
return false;
}
}
return true;
}
Think about what your code:
sequence[i+1] - sequence[i] !== 1, variable++;
would do for the following array:[1,2,3,8,8].
From the problem description, it is not clear weather the program must remove one character. But if that is the case, the code below should do it.
function canGetStrictlyIncreasingSeq(numbers) {
var counter = 0;
var lastGreatestNumber = numbers[0];
for (var i = 1; i < numbers.length; i++) {
if (lastGreatestNumber >= numbers[i]) {
counter++;
lastGreatestNumber = numbers[i];
} else {
lastGreatestNumber = numbers[i];
}
}
if (counter <= 1)
return true;
return false;
}
var nums1 = [1, 2, 3, 4, 5]; //true
var nums2 = [1, 2, 2, 3, 4]; //true
var nums3 = [1, 3, 8, 1, 9]; //true
var nums4 = [3, 2, 5, 6, 9]; //true
var nums5 = [3, 2, 1, 0, 5]; //false
var nums6 = [1, 2, 2, 2, 3]; //false
var nums7 = [1, 1, 1, 1, 1]; //false
var nums8 = [1, 2]; //true
var nums9 = [1, 2, 2]; //true
var nums10 = [1, 1, 2, 3, 4, 5, 5]; //false
var nums11 = [10, 1, 2, 3, 4, 5]; //true
var nums12 = [1, 2, 3, 4, 99, 5, 6]; //true
console.log(canGetStrictlyIncreasingSeq(nums1));
console.log(canGetStrictlyIncreasingSeq(nums2));
console.log(canGetStrictlyIncreasingSeq(nums3));
console.log(canGetStrictlyIncreasingSeq(nums4));
console.log(canGetStrictlyIncreasingSeq(nums5));
console.log(canGetStrictlyIncreasingSeq(nums6));
console.log(canGetStrictlyIncreasingSeq(nums7));
console.log(canGetStrictlyIncreasingSeq(nums8));
console.log(canGetStrictlyIncreasingSeq(nums9));
console.log(canGetStrictlyIncreasingSeq(nums10));
console.log(canGetStrictlyIncreasingSeq(nums11));
console.log(canGetStrictlyIncreasingSeq(nums12));
Lets take a step back and think about the problem: "Given a sequence of Integers as an array" - we're dealing with arrays of data...but you already knew that.
"determine whether it is possible to obtain a strictly increasing sequence" ok, we need to make something that checks for valid sequence.
"by removing no more than one element from the array." so we can try plucking each element one-by-one and if at least one resulting array is sequential, its possible.
Now instead of one large problem we have two smaller ones
First, we're dealing with arrays, so avail yourself to JavaScript's built-in Array functions to make things easier. In the below, we use 'every()', 'forEach()', 'splice()', 'push()', and 'some()' You can read into how they work here https://www.w3schools.com/jsref/jsref_obj_array.asp It's not long and well worth your time.
Lets deal with the first problem: Determining if an array is sequential. The below function does this
function checkSequence(inputArray){
return inputArray.every(function(value, index, arr){
if (index == 0 && value < arr[index + 1]) {return true}
else if (index < arr.length && value < arr[index + 1] && value > arr[index - 1]) {return true}
else if (index = arr.length - 1 && value > arr[index - 1]) {return true}
else {return false}
});
}
It takes an input array, and uses an Array built-in function called every(), which runs a test on each element in an array
and returns 'true' if all the elements test true. Our test expects the first element to always be lower less than the second
for any given element to be greater than the previous, and less than the next, and for the last element to be greater than the next-to-last
if any element does not satisfy this test, the whole thing returns false
Now we have a means of seeing of an array is sequential, which will make the next part much easier
Now we make another function to pluck out individual elements and see if anythign works
function isPossible(input){
var results = []; //we will store our future results here
input.forEach(function(value, index, arr){
copy = Array.from(arr); //we work on a copy of 'arr' to avoid messing it up (splice mangles what you give it, and we need the un-maimed version for later iterations)
copy.splice(index, 1); //remove an element from copy (a copy of 'arr')
results.push(checkSequence(copy)); //see if it is still in sequence
});
return results.some(function(value){return value});
}
We first make an array to store the results of each attempt into the array 'results' we will use it later.
Then, we take a supplied array 'input' and use "forEach()", which performs a function with each element in an array.
for each element, we make a new array with that element removed from it, and we run the "checkSequence()"
function we made before on it, and finally store the result in the results array.
When the forEach is done, we take the results array and use 'some()' on it, which works just like 'every()'
only it returns true if at least one value is true
Now, you simply call isPossible(your_array) and it will satisfy the problem
Taking into account Patrick Barr's suggestion and assuming that es6 and arrow functions are fine, this solution using Array.prototype.filter could work. The filter itself will return array of elements that should be removed to satisfy conditions of the problem:
UPDATED
function isSequential(array) {
return array && array.length > 0 ? array.filter((x,i) => x >= array[i + 1] || array[i + 1] <= array[i - 1]).length < 2 : false;
}
console.log(isSequential([1]));
console.log(isSequential([1,2,4,5,6]));
console.log(isSequential([1,2,2,3,4,5,6]));
console.log(isSequential([1,4,3,2,5,6]));
console.log(isSequential([1,2,3,4,5,6]));
console.log(isSequential([1,2,0,3,4]));
console.log(isSequential([1,1]));
console.log(isSequential([1,2,0,1,2]));
console.log(isSequential([1,2,3,1,2,3]));
console.log(isSequential([]));
console.log(isSequential([1,0,0,1]));
console.log(isSequential([1,2,6,3,4])); //should be true, but return false
I've found several posts about this topic here on stackoverflow, like this one.
... but the main assumption in the suggested solutions is that the array is sorted or ascending or descending...
My problem is that I want to check if there is the sequence of 3 or more consecutive numbers in the array but without sorting the array because it consists of points' coordinates (in the coordinate system) - points are randomly added to the stage during the game... (I'm working in GeoGebra which applet is the stage that I'm adding points to...)
For example, I have the next array:
var array = [0, 4, 6, 5, 9, 8, 9, 12];
The code should only count numbers 4, 6 and 5 as consecutive (although the array isn't sorted), but not 9 and 9 or 9 and 8. Explanation: I've created the code in JS but it works only with sorted array and, besides that, the problem is also that it counts two equal values as well as only two consecutive numbers (like 8 and 9)... My code:
function findConsecutive(array) {
var nmbOfSeq = 0;
for(var i=0; i<array.length; i++) {
for(var j=0; j<array.length; j++) {
if(array[j]==array[i]+1) {
nmbOfSeq+=1;
}
}
}
return nmbOfSeq;
}
Thanks everybody in advance... I'm really stuck with this...
I suggest to get first the index of a group of three and then build an array with the groups of the indices.
var array = [0, 4, 6, 5, 9, 8, 9, 12, 1, 2, 3, 4],
indices = [],
result = [];
array.forEach(function (a, i, aa) {
var temp;
if (i < 2) { return; }
temp = aa.slice(i - 2, i + 1);
temp.sort(function (a, b) { return a - b; });
temp[0] + 1 === temp[1] && temp[1] + 1 === temp[2] && indices.push(i);
});
indices.forEach(function (a, i, aa) {
if (aa[i - 1] + 1 === a) {
result[result.length - 1].push(array[a]);
return;
}
result.push(array.slice(a - 2, a + 1));
});
console.log(indices);
console.log(result);
I was trying to solve some of the programming challenges at "free code camp" website, The problem was to find the symmetric difference between multiple arrays and returning an array of the symmetric difference of the provided arrays.
for example the following arrays:
[1, 2, 5], [2, 3, 5], [3, 4, 5]
should return [ 1, 4, 5 ]
so that's what I came up with:
function sym() {
var final = [];
var current_array = [];
for (var i = 0; i < arguments.length; i++) {
current_array = arguments[i];
//ensures duplicates inside each array are removed first
current_array = current_array.filter(function (element, index) {
return current_array.indexOf(element) == index;
});
for (var j = 0, end = current_array.length; j < end; j++) {
if(final.indexOf(current_array[j]) < 0)
final.push(current_array[j]);
else
// for some reason "splice" isn't working properly..
// final.splice(final.indexOf(current_array[j], 1));
delete final[final.indexOf(current_array[j])];
}
}
var final_2 = [];
// Removing the empty slots caused by the "delete" keyword usage
for (var m = 0; m < final.length; m++) {
if(typeof final[m] !== 'undefined')
final_2.push(final[m]);
}
return final_2;
}
in the previous logic I created an array called final that is supposed to hold all of the elements that only exist once in all of the passed arrays, firstly I loop over the arguments parameter which represents here the arrays and for each array I loop over its elements and check if that element exists in the final array or not. If it exists I remove it from the final array, else I push it to the array.
The problem here is if I use the splice method as given in the code above, it behaves very strangely, for example for the following arrays
[1, 2, 3], [5, 2, 1, 4], the result should be: [3, 5, 4]
when I use this line
final.splice(final.indexOf(current_array[j], 1));
instead of
delete final[final.indexOf(current_array[j])];
and return the final array it returns this [ 4 ]
here is the array values at each iteration
round (0, 0): 1
round (0, 1): 1,2
round (0, 2): 1,2,3
round (1, 0): 1,2,3,5
round (1, 1): 1
round (1, 2):
round (1, 3): 4
once it gets to an element that exists in the array it removes all of the elements starting from this element until the end of the array.
I don't know exactly if I'm missing something, I tried to search for any similar problems but most of what I came up with was a problem of removing elements from an array that the person was looping over and hence missing with its indices .. In my case the array I'm trying to modify got nothing to do with the arrays I'm iterating through.
Also I believe splice modifies the array in place and returns the removed elements.. please correct me if I'm not getting it well.
You've misplaced a ), here's the correction:
final.splice( final.indexOf(current_array[j]), 1 );
An additional note: the algorithm adds 5 for the first array, removes it for the second, and adds it again for the third (since it isn't present in final anymore), resulting in [1,4,5].
With an odd number of arguments, the value is preserved, with an even number, it is removed.
A simpler way to get all unique values from all arrays (if that is the intent), is to count the occurrences and filter on a single occurrence:
function sym2() {
var count = {};
for ( var i in arguments ) {
console.log("Processing ", i );
for ( var k = 0; k < arguments[i].length; k ++)
count[ arguments[i][k] ] = (count[ arguments[i][k] ]||0) + 1;
}
var final = [];
for ( var i in count )
if ( count[i] == 1 )
final.push( i );
return final;
}
sym2([1, 2, 5], [2, 3, 5], [3, 4, 5]);
Note that this will return [1,4] rather than [1,4,5].