i am working on binary search, and this is first thing i came up with:
function letsGoBinary(firstArray,array,search){
const middle = Math.floor(array.length / 2);
if(search === array[middle]) {
const rv = firstArray.indexOf(array[middle]);
return rv
}else if(search < array[middle]){
var lowerArray = []
for(var i = 0; i < middle; i++){
lowerArray.push(array[i])
}
letsGoBinary(firstArray,lowerArray, search)
}else if(search > array[middle]){
var forwardArray = []
for(var i = middle + 1; i < array.length; i++){
forwardArray.push(array[i]);
}
letsGoBinary(firstArray,forwardArray,search)
}else {
return -1
}
}
console.log(letsGoBinary([1,4,7,14,16],[1,4,7,14,16], 4))
and this works if I add console.log() in first if statement (search === array[middle]) and log the rv it logs exact value, and same happens if I log not found in else statement, it logs but while logging letsGoBinary its value is undefined. how can i fix that?
beside your question please check how slice method works, loop is not nesessery to get a part of array
also this code is bit meaningless, if you use
const rv = firstArray.indexOf(array[middle])
then why just in the very beginning not to use
const rv = firstArray.indexOf(search)
this line of code makes meaningless all your binary search as searches elements one by one
There are very simple solution for it
function letsGoBinary(array, search){
let start = 0
let end = array.length - 1
while (start <= end) {
const middle = Math.floor((start + end) / 2)
if(search === array[middle]) {
return middle
} else if (search < array[middle]) {
end = middle - 1
} else {
start = middle + 1
}
}
return -1
}
In the cases where you make a recursive call, you need to return the result.
return letsGoBinary(firstArray,lowerArray, search);
When dealing with recursive function, you should have the basic cases and then when you want to like perfom a recursive call you are not only calling the function again but you should return the function as response.
For instance if the search number is present into the lowerArray is means that you should return letsGoBinary(firstArray,lowerArray, search) as answer.
I updated your code so that is it:
Note: Look at the line 11 and 17
function letsGoBinary(firstArray,array,search){
const middle = Math.floor(array.length / 2);
if(search === array[middle]) {
const rv = firstArray.indexOf(array[middle]);
return rv
}else if(search < array[middle]){
var lowerArray = []
for(var i = 0; i < middle; i++){
lowerArray.push(array[i])
}
return letsGoBinary(firstArray,lowerArray, search)
}else if(search > array[middle]){
var forwardArray = []
for(var i = middle + 1; i < array.length; i++){
forwardArray.push(array[i]);
}
return letsGoBinary(firstArray,forwardArray,search)
}else {
return -1
}
}
console.log(letsGoBinary([1,4,7,14,16],[1,4,7,14,16], 4))
It is happening because after the first execution when search === array[middle], it is completely returning from letsGoBinary function, hence you have to add another return statement, please find below code snippet:
function letsGoBinary(firstArray,array,search){
const middle = Math.floor(array.length / 2);
if(search === array[middle]) {
const rv = firstArray.indexOf(array[middle]);
return rv
}else if(search < array[middle]){
var lowerArray = []
for(var i = 0; i < middle; i++){
lowerArray.push(array[i])
}
return letsGoBinary(firstArray,lowerArray, search)
}else if(search > array[middle]){
var forwardArray = []
for(var i = middle + 1; i < array.length; i++){
forwardArray.push(array[i]);
}
return letsGoBinary(firstArray,forwardArray,search)
}else {
return -1
}
}
console.log(letsGoBinary([1,4,7,14,16],[1,4,7,14,16], 4))
The answer from Dmitry Reutov is great. If you'd prefer a recursive version, this is a similar approach, but using recursion rather than a while loop:
const letsGoBinary = (
sortedArray,
value,
start = 0,
end = sortedArray .length - 1,
middle = Math.floor ((end + start) / 2)
) =>
start > end
? -1
: sortedArray [middle] == value
? middle
: sortedArray [middle] < value
? letsGoBinary (sortedArray, value, middle + 1, end)
: letsGoBinary (sortedArray, value, start, middle - 1)
console .log (
letsGoBinary ([1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233], 34)
)
Both these solutions use only a single array, relying on start, end, and middle indices to track the current search position.
This version defaults start and end on the first call, and then passes them on subsequent searches. middle is calculated on each call as the closest integer midpoint between start and end.
For this example, the first call uses start and end of 0 and 12 making middle 6, and the value we're testing would be sortedArray[6], which is 13. This is less than the search value of 34, so we call again with 7 and 12, which makes middle into 9 and the test value 55. That is larger than 34 so we call with 7 and 8, middle of 7, test value 21. That one is less than our value, and we call one more time with start and end both 8, which gives us a middle of 8 and a test value of 34. Since that equals our value, we return 8. If we had missed -- perhaps we were searching for 35 instead -- then we would call again with start of 9 and end of 8, and would return -1, because start > end. Or if we had been searching for 33 instead, we would have start of 8 and end of 7, with the same -1 result.
Related
I wrote this code, but I dont uderstand why it works this way, especially using the third and fourth examples as input. Why the 'middle' position remains so behind? -in the number 5 (or index 2) using the [1, 3, 5, 6] array and the number 7 as target??
And how to make it better??
I cant think of a shorter or better way to check the if/elses when the target value is not in the array, especially if the input is an array with only one value and the target to find is 0.
Maybe a better way to check the possible different scenarios.
Or how to better check the correct place of the target without so many if/elses.
For example, is this code good enough to a coding interview? What can I do better?
from LeetCode:
Search Insert Position
Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.
You must write an algorithm with O(log n) runtime complexity.
Example 1:
Input: nums = [1,3,5,6], target = 5
Output: 2
Example 2:
Input: nums = [1,3,5,6], target = 2
Output: 1
Example 3:
Input: nums = [1,3,5,6], target = 7
Output: 4
And especially this one:
Example 4:
Input: nums=[1], target= 0
Constraints:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums contains distinct values sorted in ascending order.
-104 <= target <= 104
this is my code:
/**
* #param {number[]} nums
* #param {number} target
* #return {number}
*/
var searchInsert = function(nums, target) {
let left = 0;
let right = nums.length -1;
let middle;
while(left <= right){
middle = nums.length>1 ? (Math.floor(left + (right - left)/2)) : 0;
if(nums[middle] === target){
return middle;
} else if(target < nums[middle]){
right = middle -1;
} else {
left = middle + 1;
}
}
console.log(`Middle: ${middle}`);
console.log(`Middle-1: ${nums[middle-1]}`);
if(nums.lenght === 1){
return 0;
} else {
if((target < nums[middle] && target > nums[middle-1] )|| (target < nums[middle] && nums[middle-1] === undefined)){ /*
No more items to the left ! */
return middle;
} else if(target<nums[middle] && target<nums[middle-1]){
return middle-1;
} else if(target > nums[middle] && target > nums[middle + 1]) {
return middle + 2; /* Why the 'middle' is so behind here? using the THIRD example as input?? */
} else {
return middle + 1;
}
}
};
Problem
The issue lies in the variable you are checking for after the while loop.
In a "classical" binary search algorithm, reaching beyond the while loop would indicate the needle isn't present in the haystack. In case of this problem, though, we simply need to return right + 1 in this place in the code (rather than checking the middle).
Your code adjusted for this:
var searchInsert = function(nums, target) {
let left = 0;
let right = nums.length -1;
let middle;
while(left <= right){
middle = nums.length>1 ? (Math.floor(left + (right - left)/2)) : 0;
if(nums[middle] === target){
return middle;
} else if(target < nums[middle]){
right = middle -1;
} else {
left = middle + 1;
}
}
return right + 1;
};
console.log(
searchInsert([1,3,5,6], 5),
searchInsert([1,3,5,6], 2),
searchInsert([1,3,5,6], 7),
searchInsert([1], 0)
);
Side note
Also, the below is redundant...
middle = nums.length>1 ? (Math.floor(left + (right - left)/2)) : 0;
...and can be shortened to:
middle = Math.floor((left + right) / 2);
Revised variant
const searchInsertProblem = (arr, n) => {
let start = 0;
let end = arr.length - 1;
while (start <= end) {
const middle = Math.floor((start + end) / 2);
if (arr[middle] === n) { return middle; } // on target
if (arr[middle] > n) { end = middle - 1; } // overshoot
else { start = middle + 1; } // undershoot
}
return end + 1;
};
console.log(
searchInsertProblem([1,3,5,6], 5),
searchInsertProblem([1,3,5,6], 2),
searchInsertProblem([1,3,5,6], 7),
searchInsertProblem([1], 0)
);
What is the key point in deciding where to insert an element using binary search ?
Using binary search when the element is present returns its index.
function arr() {
this.arr = [5, 6, 8];
this.insert = function(element) {
var low = 0;
var high = this.arr.length - 1;
while (low <= high) {
var mid = parseInt((low + high) / 2);
if (element == this.arr[mid]) {
return mid;
} else if (element > this.arr[mid]) {
low = mid + 1;
} else {
high = mid - 1
}
}
return -1
}
}
var vector = new arr();
var x = vector.insert(6); // return 1
alert(x)
Here I can use splice to insert element on index 1, but what if do
var x = vector.insert(7);
7 isn't present in the array but should be inserted on 2th index.
How could I determine that ?
Try with something like this:
function arr() {
this.arr = [5, 6, 8];
this.insert = function(element) {
var low = 0;
var high = this.arr.length - 1;
while (low <= high) {
var mid = parseInt((low + high) / 2);
if (element == this.arr[mid]) {
return mid;
} else if (element > this.arr[mid]) {
low = mid + 1;
} else {
high = mid - 1
}
}
return mid;
}
}
You probably want to insert the element if it's not found inside your array, using splice(). Also I made small edits to your code.
The index of insertion is determined by your mid variable.
function arr() {
this.arr = [5, 6, 8];
this.insert = function(element) {
var low = 0;
var high = this.arr.length;
while (low <= high) {
var mid = Math.floor((low + high) / 2);
if (element == this.arr[mid]) {
return mid;
} else if (element > this.arr[mid]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
this.arr.splice(mid, 0, element);
return mid;
}
}
var vector = new arr();
var x = vector.insert(6);
console.log(x); // 1
var x = vector.insert(7);
console.log(x); // 2
var x = vector.insert(9);
console.log(x); // 4
console.log(vector.arr); // Array [ 5, 6, 7, 8, 9 ]
document.write('<p>Check your console :-)</p>');
Some binary search implementations return the one's complement of the insertion spot when the item isn't found. In your case, the item would be inserted at index 2 if it were found. But since it's not found, you return -3. The caller sees that the return value is less than 0, does a one's complement, and then inserts at that position.
Example:
result = find(7) // find the value in the array
if (result < 0) // if not found
{
index = ~result // complement the result
insert(7, index) // and insert (probably using splice)
}
In your code, rather than return -1, do a return ~mid
The reason for using the one's complement rather than the negative index is that if the item you're looking for is smaller than the smallest item in the array, it would have to be inserted at index 0. But if you returned -0, you'd get ... 0. So it's impossible to tell the difference between the item being found at zero and the item needing to be inserted at index 0. One's complement solves that problem because the one's complement of 0 is -1.
I have a string that contains a create query, and I'm trying to split it based on commas. Unfortunately, some of the lines have commas in them surrounded by parenthesis.
Example:
dbo.Display_Test1.Column,
CASE WHEN CHARINDEX(' To ', Test3) > 0 AND CHARINDEX('XVR', Test3) = 0 THEN LEFT(Test3, (CHARINDEX(' To ', Test3) - 12)) ELSE Test3 END AS Test3,
dbo.Display_Test2.Column,
ISNULL((CASE WHEN [2-Display-Test4].[Total Number] > 0 AND [1-Display-Test5].SumOfNumber = 0 THEN 0 ELSE (([2-Display-Test4].[Total Number] * 1000) / [1-Display-Test5].SumOfNumber) END), 0) AS Test6,
I want to split my string based on commas that aren't inside of (possibly multiple) parenthesis. For reference, my example should be split where the line breaks are (although my string doesn't have the line breaks in it).
I've tried a number of different solutions, but none work quite right:
/(?:\(*[^()]*\)|[^,])+/g works on lines 1,3, and 4, but fails on line 2. It breaks up the line into multiple matches.
/((?:[^,(]+|(\((?:[^()]+)|$1\)))+)/g works on lines 1,2, and 3, but fails on line 4. It also breaks up the line into multiple matches.
I can't quite seem to make it work. Any help is appreciated.
One solution possible : ( note : we remove the separations comas )
var str = "dbo.Display_Test1.Column, CASE WHEN CHARINDEX(' To ', Test3) > 0 AND CHARINDEX('XVR', Test3) = 0 THEN LEFT(Test3, (CHARINDEX(' To ', Test3) - 12)) ELSE Test3 END AS Test3, dbo.Display_Test2.Column, ISNULL((CASE WHEN [2-Display-Test4].[Total Number] > 0 AND [1-Display-Test5].SumOfNumber = 0 THEN 0 ELSE (([2-Display-Test4].[Total Number] * 1000) / [1-Display-Test5].SumOfNumber) END), 0) AS Test6,";
// if the string don't end by a coma , we add it.
var strArr = str.replace(/,*\s*$/ , ',').split('');
var res;
res = strArr.reduce(function( trans , charValue ){
if(charValue === '(') {
trans.deep++;
}
if(charValue === ')') {
trans.deep--;
}
if( trans.deep === 0){
if(charValue===',') {
trans.arr.push( trans.str);
trans.str = '';
}else{
trans.str += charValue;
}
}else{
trans.str += charValue;
}
return trans;
}, { arr : [] , str : '' ,deep : 0});
document.write('<pre>' + JSON.stringify(res.arr , null , ' ') + '</pre>');
Edit :
As suggested by Thriggle in the comments i've added the case where the string don't end by a coma.
changed variables name .
Of course if we want to work with unlimited nesting of parentheses then it would be impossible to avoid a loop. Still for some bounded nesting we do can write a regular expression capturing it.
For up to 1 parentheses level it could be
/((?:[^(),]|\([^()]*\))+)/g
For up to 2 parentheses level (probably it's your case)
/((?:[^(),]|\((?:[^()]|\([^()]*\))*\))+)/g
And so on, recursively substituting [^()]* with (?:[^()]|\([^()]*\))*
Regex won't get you where you want and still allow arbitrary complexity.
One approach is to iterate through each character in the string, keeping track of when you hit open and close parentheses, and only splitting on commas when the numbers of open and close parentheses cancel each other out (so you know you're not within a parenthetical statement).
function splitQuery(input){
var arr = [];
var lastStart = 0;
var open = 0;
for(var i = 0, len = input.length; i < len; i++){
var curr = input[i];
if(curr === "("){open += 1;}
else if(curr === ")"){ open = open < 1 ? 0 :open -=1;}
else if(curr === ","){
if(open === 0){
arr.push(input.substring(lastStart,i));
lastStart = i+1;
}
}else if(i+1 === len){
arr.push(input.substring(lastStart,i+1));
}
}
return arr;
}
Just be aware that this approach can be expensive (from a performance perspective) when dealing with especially large strings.
Here's a working example:
var query = "dbo.Display_Test1.Column, CASE WHEN CHARINDEX(' To ', Test3) > 0 AND CHARINDEX('XVR', Test3) = 0 THEN LEFT(Test3, (CHARINDEX(' To ', Test3) - 12)) ELSE Test3 END AS Test3, dbo.Display_Test2.Column, ISNULL((CASE WHEN [2-Display-Test4].[Total Number] > 0 AND [1-Display-Test5].SumOfNumber = 0 THEN 0 ELSE (([2-Display-Test4].[Total Number] * 1000) / [1-Display-Test5].SumOfNumber) END), 0) AS Test6,";
var split = splitQuery(query);
var output = document.getElementById("output")
for(var el in split){
output.insertAdjacentHTML("beforeend",el + ": "+ split[el]+"<hr/>");
}
function splitQuery(input){
var arr = [];
var lastStart = 0;
var open = 0;
for(var i = 0, len = input.length; i < len; i++){
var curr = input[i];
if(curr === "("){open += 1;}
else if(curr === ")"){ open = open < 1 ? 0 :open -=1;}
else if(curr === ","){
if(open === 0){
arr.push(input.substring(lastStart,i));
lastStart = i+1;
}
}else if(i+1 === len){
arr.push(input.substring(lastStart,i+1));
}
}
return arr;
}
<div id="output"></div>
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.
I'm trying to work through the following exercise. There is a problem with my code but I don't understand what it is...
Using the JavaScript language, have the function ArithGeo(arr) take the array of numbers stored in arr and return the string "Arithmetic" if the sequence follows an arithmetic pattern or return "Geometric" if it follows a geometric pattern. If the sequence doesn't follow either pattern return -1. An arithmetic sequence is one where the difference between each of the numbers is consistent, where as in a geometric sequence, each term after the first is multiplied by some constant or common ratio. Arithmetic example: [2, 4, 6, 8] and Geometric example: [2, 6, 18, 54]. Negative numbers may be entered as parameters, 0 will not be entered, and no array will contain all the same elements.
My code:
function ArithGeo(arr) {
if (for (i = 0; i< (arr.length - 2); i++) {
arr[i+1] - arr[i] == arr[i+2] - arr[i+1];
}){
return "Arithmetic";
} else if (for (i = 0; i< (arr.length - 2); i++) {
arr[i+1] / arr[i] == arr[i+2] / arr[i+1];
}){
return "Geometric";
} else {
return -1;
}
};
When I put an array like [5,10,15], I get "Unexpected token for". Any ideas?
Modified your code. Didn't change the logic, but the way it should be written.
function ArithGeo(arr) {
var ap, gp;
for (i = 0; i< (arr.length - 2); i++)
if(!(ap = arr[i+1] - arr[i] == arr[i+2] - arr[i+1])) break;
if(ap) return "Arithmetic";
for (i = 0; i< (arr.length - 2); i++)
if(!(gp = arr[i+1] / arr[i] == arr[i+2] / arr[i+1])) break;
if(gp) return "Geometric";
return -1;
};
It looks like you're trying to get the result of the for loop, but loops don't have return values. Consider defining a boolean variable (initially set to true) outside of the loop and AND it with the result of the comparison you perform inside the loop; the variable will be true at the end if each iteration of the loop ANDs true to it.
For example:
var test = true;
for(var i=0; i<5; i++) {
test = test && ( i != 6 );
}
if(test) {
alert("i was never 6");
}