Three Sum Algorithm can't find bug - javascript

I'm trying to solve Three Sum (find all triplets that add to 0 within an array avoiding duplicate cases) but currently running into a bug I can't seem to find.
When the input is [-1,0,1,2,-1,-4], it works fine.
With this input however, [-1,0,1,2,-1,-4,-2,-3,3,0,4] I'm getting this array as output:
[[-1,-1,2],[-1,0,1],[-2,0,2],[-3,0,3],[-3,1,2],[-4,0,4],[-4,1,3]].
The correct output should be
[[-4,0,4],[-4,1,3],[-3,-1,4],[-3,0,3],[-3,1,2],[-2,-1,3],[-2,0,2],[-1,-1,2],[-1,0,1]]
So for some reasons my solution is omitting the triplets [-3,-1,4] and [-2,-1,3].
var threeSum = function (nums) {
const sorted = nums.sort()
const output = []
for (let i = 0; i < sorted.length - 2; i++)
if (i === 0 || (i > 0 && sorted[i] !== sorted[i - 1])) {
let lower = i + 1
let higher = sorted.length - 1
while (lower < higher) {
const currentSum = sorted[i] + sorted[lower] + sorted[higher];
if (currentSum === 0) {
output.push([sorted[i], sorted[lower], sorted[higher]])
while (sorted[lower] === sorted[lower + 1]) lower++
while (sorted[higher] === sorted[higher - 1]) higher--
lower++
higher--
}
else if (currentSum < 0) {
lower++
} else {
higher--
}
}
}
}
return output
};

By default Javascript sorts via string comparison.
You want to sort numerically so use
nums.sort(function(a, b){return a - b});

Related

Find the Greatest Common Divisor

Here is the question.
Find the greatest common divisor of two positive integers. The integers can be large, so you need to find a clever solution.
The inputs x and y are always greater or equal to 1, so the greatest common divisor will always be an integer that is also greater or equal to 1.
Here is my solution.
function mygcd(x, y) {
//your code here
let gcd = [];
let lowestNum;
let bigestNum;
//detect the lowest and bigest numbers
if (x < y) {
lowestNum = x;
bigestNum = y;
} else if (x > y) {
lowestNum = y
bigestNum = x;
} else {
lowestNum = x
}
//check if the bigest num has a modolo == 0
//else loop the lowest num and push in the array
if (bigestNum % lowestNum === 0) {
return gcd += lowestNum;
} else {
let arrNum = []
for (let i = 1; i < lowestNum; i++) {
// console.log(i)
arrNum.push(i)
}
//loop the array backwards
for (i = arrNum.length - 1; i >= 1; i--) {
if (lowestNum % arrNum[i] === 0 && bigestNum % arrNum[i] === 0) {
console.log(arrNum[i])
if (gcd !== 0) {
return
} else {
// gcd += arrNum[i]
let vals = gcd.push(arrNum[i])
console.log(typeof(vals))
}
}
}
}
console.log(gcd)
return gcd[0];
}
console.log(mygcd(30, 12))
The above solution works for the test cases i tried it for, but the issue is that it returns the correct divisor and undefined.
This is what my logs look like
6
undefined
6
undefined
The test cases
test:
Log
6
expected undefined to equal 6
so it gets undefined instead of 6 or the correct divisor.
I also tired a different recursive approach below.
Note: This works well.
function mygcd(x, y) {
if (!x) return y
if (!y) return x
return mygcd(y, x % y)
}
console.log(mygcd(30, 12))
console.log(mygcd(8, 12))
But i am curious to understand why my original solution breaks. Any help would be really appreciated.
Thanks

Function in Javascript that inserts dashes or asterisks between each two odd or even numbers

I want to write a function that inserts dashes (' - ') between each two odd numbers and inserts asterisks (' * ') between each two even numbers. For instance:
Input: 99946
Output: 9-9-94*6
Input: 24877
Output: 2*4*87-7
My try
function dashAst (para) {
let stringArray = para.toString().split('');
let numbArray = stringArray.map(Number);
for (let i = 0; i<numbArray.length; i++) {
if (numbArray[i] %2 === 0 && numbArray[i+1] % 2 === 0) {
numbArray.splice(numbArray.indexOf(numbArray[i]), 0, '*')
}
else if (numbArray[i] %2 !== 0 && numbArray[i+1] %2 !== 0) {
numbArray.splice(numbArray.indexOf(numbArray[i]), 0, '-')
}
}
return numbArray
}
When I try to invoke the function it returns nothing. For instance, I tested the splice-command separately and it seems to be correct which makes it even more confusing to me. Thanks to everyone reading, or even helping a beginner out.
Looping through an Array that changes its length during the loop can be very messy (i needs to be adjusted every time you splice). It's easier to create a new result variable:
function dashAst(para) {
const stringArray = para.toString().split('');
const numbArray = stringArray.map(Number);
let result = "";
for (let i = 0; i < numbArray.length; i++) {
const n = numbArray[i], next = numbArray[i + 1];
result += n;
if (n % 2 == next % 2) {
result += n % 2 ? '-' : '*';
}
}
return result;
}
console.log(dashAst(99946)); // "9-9-94*6"
console.log(dashAst(24877)); // "2*4*87-7"
You could map the values by checking if the item and next item have the same modulo and take a separator which is defined by the modulo.
function dashAst(value) {
return [...value.toString()]
.map((v, i, a) => v % 2 === a[i + 1] % 2 ? v + '*-'[v % 2] : v)
.join('');
}
console.log(dashAst(99946)); // 9-9-94*6
console.log(dashAst(24877)); // 2*4*87-7
I hope this helps
var str = '24877';
function dashAst (para) {
let stringArray = para.toString().split('');
let numbArray = stringArray.map(x => parseInt(x));
console.log(numbArray);
var out=[];
for(let i = 0; i < numbArray.length; i++) {
if(numbArray[i] % 2 == 0){
out.push(numbArray[i]);
numbArray[i + 1] % 2 == 0 ? out.push('*') : 0;
}else if(numbArray[i] % 2 != 0) {
out.push(numbArray[i]);
numbArray[i + 1] != undefined ? out.push('-') : 0;
}
}
console.log(out.join(''));
return out;
}
dashAst(str);

Smallest non-consecutive number in unsorted array

I want to write a function that returns the smallest non-consecutive number of an unsorted array. If the whole array is consecutive, the closest number that would extend the array.
nextId([1,2,3,4]) returns 5
nextId([1,4,3]) returns 2
My try:
function nextId(arr) {
let sortnum = arr.sort((a, b) => a - b);
for (let i = 0; i < arr.length - 1; i++) {
if (sortnum[i] + 1 !== sortnum[i + 1]) {
return sortnum[i] + 1
}
else(sortnum[sortnum.length - 1] === sortnum[sortnum.length - 2] + 1) {
return sortnum[sortnum.length - 1] + 1
}
}
}
If I outcomment the if or else-statement, they both work perfectly fine on their own, yet they don't work for some reason when I put both of them in one statement.
Would have to slice the array to make a copy if that's required, but this will work:
function nextId(arr) {
return arr.sort().find((v, i, a) => v + 1 != a[i + 1]) + 1;
}
console.log(nextId([1,2,3,4]));
console.log(nextId([1,4,3]));
For the case where all the values are subsequent, this works by virtue of the fact that number + 1 != undefined will always evaluate to true.
Just loop your array and compare if current element in sorted array is same as index + 1. If everything is in order, then just arr.length + 1 is next missing item.
function nextId(arr) {
let sortnum = arr.slice().sort((a, b) => a - b);
for (let i = 1; i <= arr.length; i++) {
if (i != sortnum[i - 1]) {
return i;
}
}
return arr.length + 1;
}
console.log(nextId([1,2,3,4]));
console.log(nextId([1,4,3]));

Greatest Prime Factor

I'm trying to complete an algorithm challenge to find the largest prime factor of 600851475143. I'm not necessarily asking for the answer. Just trying to figure out why this code isn't working. Why does it return 'undefined' instead of a number?
let isPrime = n => {
let div = n - 1;
while (div > 1) {
if (n % div == 0) return false;
div--;
}
return true;
};
let primeFactor = x => {
for (let i = Math.floor(x / 2); i > 1; i--) {
if (x % i == 0 && isPrime(i) == true) {
return i;
}
}
};
console.log(primeFactor(35)); // 7
console.log(primeFactor(13195)); // 29
console.log(primeFactor(600851475143)); // undefined
The problem is not your algorithm it is perfectly valid, check the below slightly modified algorithm, all I've done is replaced your starting point Math.floor(x/2) with a parameter that you can choose:
let isPrime = n => {
let div = n - 1;
while (div > 1) {
if (n % div == 0) return false;
div--;
}
return true;
};
function primeFactor(x, n){
for (let i = n; i > 1; i--) {
if (x % i == 0 && isPrime(i) == true) {
return i;
}
}
}
console.log(primeFactor(35, 35));
console.log(primeFactor(13195, 13195));
console.log(primeFactor(600851475143, 100000))
Using the above you'll get an answer that proves your implementation works, but the loop is too big to do the entire thing(i.e. from Math.floor(600851475143/2)). Say your computer can do 500million loops per second, going through every one from 300,425,737,571 down to 1 would take 167 hours, even at 5 billion loops per second it would take 16 and a half hours. Your method is extremely inefficient but will return the correct answer. The reason you're not getting an answer on JSBin is more likely to do with browser/service limitations.
Spoilers on more efficient solution below
The following implementation uses a prime sieve(Sieve of Eratosthenes) in order to generate any list of primes requested and then checks if they fully factor into the given number, as long as you use a large enough list of primes, this will work exactly as intended. it should be noted that because it generates a large list of primes it can take some time if ran incorrectly, a single list of primes should be generated and used for all calls below, and the cached list of primes will pay off eventually by having to perform less calculations later on:
function genPrimes(n){
primes = new Uint32Array(n+1);
primes.fill(1)
for(var i = 2; i < Math.sqrt(n); i++){
if(primes[i]){
for(var j = 2*i; j < n; j+=i){
primes[j] = 0;
}
}
}
primeVals = []
for(var i = 2; i < primes.length; i++){
if(primes[i]){
primeVals.push(i);
}
}
return primeVals;
}
function primeFactor(x, primes){
var c = x < primes.length ? x : primes.length
for (var i = c; i > 1; i--) {
if(x % primes[i] == 0){
return primes[i];
}
}
}
primes = genPrimes(15487457);
console.log(primeFactor(35, primes));
console.log(primeFactor(13195, primes));
console.log(primeFactor(600851475143, primes));
console.log(primeFactor(30974914,primes));
let primeFactor = x => {
if (x === 1 || x === 2) {
return x;
}
while (x % 2 === 0) {
x /= 2;
}
if (x === 1) {
return 2;
}
let max = 0;
for (let i = 3; i <= Math.sqrt(x); i += 2) {
while (x % i === 0) {
x /= i;
max = Math.max(i, max);
}
}
if (x > 2) {
max = Math.max(x, max);
}
return max;
};
console.log(primeFactor(35));
console.log(primeFactor(13195));
console.log(primeFactor(27));
console.log(primeFactor(1024));
console.log(primeFactor(30974914));
console.log(primeFactor(600851475143));
Optimizations
Dividing the number by 2 until it's odd since no even number is prime.
The iteration increment is 2 rather than 1 to skip all even numbers.
The iteration stops at sqrt(x). The explanation for that is here.

Inserting into a number string

Have the function DashInsert(num) insert dashes ('-') between each two odd numbers in num. For example: if num is 454793 the output should be 4547-9-3. Don't count zero as an odd number.
Here is my code (not working). When I run it, I get the same response as an infinite loop where I have to kill the page but I can't see why. I know there are ways to do this by keeping it as a string but now I'm wondering why my way isn't working. Thanks...
function DashInsert(num) {
num = num.split("");
for (i = 1; i < num.length; i++) {
if (num[i - 1] % 2 != 0 && num[i] % 2 != 0) {
num.splice(i, 0, "-");
}
}
num = num.join("");
return num;
}
Using num.splice you are inserting new entries into the array, therefor increasing its length – and that makes the value of i “running behind” the increasing length of the array, so the break condition is never met.
And apart from that, on the next iteration after inserting a -, num[i-1] will be that - character, and therefor you are practically trying to check if '-' % 2 != 0 … that makes little sense as well.
So, when you insert a - into the array, you have to increase i by one as well – that will a) account for the length of the array having increased by one, and also it will check the next digit after the - on the next iteration:
function DashInsert(num) {
num = num.split("");
for (i = 1; i < num.length; i++) {
if (num[i - 1] % 2 != 0 && num[i] % 2 != 0) {
num.splice(i, 0, "-");
i++; // <- this is the IMPORTANT part!
}
}
num = num.join("");
return num;
}
alert(DashInsert("454793"));
http://jsfiddle.net/37wA9/
Once you insert a dash -, the if statement is checking this '-'%2 != 0 which is always true and thus inserts another dash, ad infinitum.
Here's one way to do it with replace using a regex and function:
function DashInsert(n) {
var f = function(m,i,s) { return m&s[i+1]&1 ? m+'-' : m; };
return String(n).replace(/\d/g,f);
}
DashInsert(454793) // "4547-9-3"
When you are adding a dash, this dash will be processed as a number on the next iteration. You need to forward one step.
function DashInsert(num) {
var num = num.split("");
for (var i = 1; i < num.length; i++) {
if ((num[i - 1] % 2 != 0) && (num[i] % 2 != 0)) {
num.splice(i, 0, "-");
i++; // This is the only thing that needs changing
}
}
num = num.join("");
return num;
}
It's because there are cases when you use the % operator on dash '-' itself, e.g. right after you splice a dash into the array.
You can correct this behavior by using a clone array.
function DashInsert(num) {
num = num.split("");
var clone = num.slice(0);
var offset = 0;
for (i = 1; i < num.length; i++) {
if (num[i - 1] % 2 != 0 && num[i] % 2 != 0) {
clone.splice(i + offset, 0, "-");
offset++;
}
}
return clone.join("");
}
alert(DashInsert("45739"));
Output: 45-7-3-9
Demo: http://jsfiddle.net/262Bf/
To complement the great answers already given, I would like to share an alternative implementation, that doesn't modify arrays in-place:
function DashInsert(num) {
var characters = num.split("");
var numbers = characters.map(function(chr) {
return parseInt(chr, 10);
});
var withDashes = numbers.reduce(function(result, current) {
var lastNumber = result[result.length - 1];
if(lastNumber == null || current % 2 === 0 || lastNumber % 2 === 0) {
return result.concat(current);
} else {
return result.concat("-", current);
}
}, []);
return withDashes.join("");
}
It's longer, but IMHO reveals the intention better, and avoids the original issue.

Categories