Skip to content
9 changes: 4 additions & 5 deletions Backtracking/NQueens.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
class NQueens {
constructor(size) {
if (size < 0) {
throw RangeError('Invalid board size')
if (size <= 0) {
throw RangeError('Board size must be a positive integer')
}

this.board = new Array(size).fill('.').map(() => new Array(size).fill('.'))
this.size = size
this.solutionCount = 0
Expand Down Expand Up @@ -40,7 +41,7 @@ class NQueens {
solve(col = 0) {
if (col >= this.size) {
this.solutionCount++
return true
return
}

for (let i = 0; i < this.size; i++) {
Expand All @@ -50,8 +51,6 @@ class NQueens {
this.removeQueen(i, col)
}
}

return false
}

printBoard(output = (value) => console.log(value)) {
Expand Down
44 changes: 39 additions & 5 deletions Dynamic-Programming/CoinChange.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,57 @@
* @params {Number} amount
*/
export const change = (coins, amount) => {
// Create and initialize the storage
if (!Array.isArray(coins)) {
throw new TypeError('Coins must be an array')
}

if (typeof amount !== 'number' || amount < 0) {
throw new TypeError('Amount must be a non-negative number')
}

if (amount === 0) {
return 1
}

if (coins.length === 0) {
return 0
}

const combinations = new Array(amount + 1).fill(0)
combinations[0] = 1
// Determine the direction of smallest sub-problem

for (let i = 0; i < coins.length; i++) {
// Travel and fill the combinations array
for (let j = coins[i]; j < combinations.length; j++) {
combinations[j] += combinations[j - coins[i]]
}
}

return combinations[amount]
}

/**
* @params {Array} coins
* @params {Number} amount
*/
export const coinChangeMin = (coins, amount) => {
const map = { 0: 1 }
if (!Array.isArray(coins)) {
throw new TypeError('Coins must be an array')
}

if (typeof amount !== 'number' || amount < 0) {
throw new TypeError('Amount must be a non-negative number')
}

if (amount === 0) {
return 0
}

if (coins.length === 0) {
return -1
}

const map = { 0: 0 }

for (let i = 1; i <= amount; i++) {
let min = Infinity
for (const coin of coins) {
Expand All @@ -29,5 +62,6 @@ export const coinChangeMin = (coins, amount) => {
}
map[i] = min
}
return map[amount] === Infinity ? -1 : map[amount] - 1

return map[amount] === Infinity ? -1 : map[amount]
}
14 changes: 10 additions & 4 deletions Dynamic-Programming/LongestPalindromicSubsequence.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@
*/

export const longestPalindromeSubsequence = function (s) {
if (typeof s !== 'string') {
throw new TypeError('Input must be a string')
}

const n = s.length

const dp = new Array(n)
.fill(0)
.map((item) => new Array(n).fill(0).map((item) => 0))
if (n === 0) {
return 0
}

const dp = new Array(n).fill(0).map(() => new Array(n).fill(0))

// fill predefined for single character
// single character palindromes
for (let i = 0; i < n; i++) {
dp[i][i] = 1
}
Expand Down
22 changes: 12 additions & 10 deletions Search/BinarySearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,44 +8,46 @@
*/

function binarySearchRecursive(arr, x, low = 0, high = arr.length - 1) {
if (!Array.isArray(arr) || arr.length === 0) {
return -1
}

const mid = Math.floor(low + (high - low) / 2)

if (high >= low) {
if (arr[mid] === x) {
// item found => return its index
return mid
}

if (x < arr[mid]) {
// arr[mid] is an upper bound for x, so if x is in arr => low <= x < mid
return binarySearchRecursive(arr, x, low, mid - 1)
} else {
// arr[mid] is a lower bound for x, so if x is in arr => mid < x <= high
return binarySearchRecursive(arr, x, mid + 1, high)
}
} else {
// if low > high => we have searched the whole array without finding the item
return -1
}

return -1
}

function binarySearchIterative(arr, x, low = 0, high = arr.length - 1) {
if (!Array.isArray(arr) || arr.length === 0) {
return -1
}

while (high >= low) {
const mid = Math.floor(low + (high - low) / 2)

if (arr[mid] === x) {
// item found => return its index
return mid
}

if (x < arr[mid]) {
// arr[mid] is an upper bound for x, so if x is in arr => low <= x < mid
high = mid - 1
} else {
// arr[mid] is a lower bound for x, so if x is in arr => mid < x <= high
low = mid + 1
}
}
// if low > high => we have searched the whole array without finding the item

return -1
}

Expand Down
16 changes: 16 additions & 0 deletions Sliding-Windows/LongestSubarrayWithSumAtMost.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,32 @@
* @returns {number} - The length of the longest subarray with a sum <= target.
*/
export function longestSubarrayWithSumAtMost(arr, target) {
if (!Array.isArray(arr)) {
throw new TypeError('First argument must be an array')
}

if (typeof target !== 'number') {
throw new TypeError('Target must be a number')
}

if (arr.length === 0) {
return 0
}

let maxLength = 0
let windowSum = 0
let left = 0

for (let right = 0; right < arr.length; right++) {
windowSum += arr[right]

while (windowSum > target) {
windowSum -= arr[left]
left++
}

maxLength = Math.max(maxLength, right - left + 1)
}

return maxLength
}
40 changes: 23 additions & 17 deletions Sorts/BubbleSort.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
/* Bubble Sort is an algorithm to sort an array. It
* compares adjacent element and swaps their position
* The big O on bubble sort in worst and best case is O(N^2).
* Not efficient.
* Somehow if the array is sorted or nearly sorted then we can optimize bubble sort by adding a flag.
* compares adjacent elements and swaps their position.
* The big O on bubble sort in worst and best case is O(N^2).
*
* In bubble sort, we keep iterating while something was swapped in
* the previous inner-loop iteration. By swapped I mean, in the
* inner loop iteration, we check each number if the number proceeding
* it is greater than itself, if so we swap them.
*
* Wikipedia: https://en.wikipedia.org/wiki/Bubble_sort
* Animated Visual: https://www.toptal.com/developers/sorting-algorithms/bubble-sort
* Wikipedia: https://en.wikipedia.org/wiki/Bubble_sort
*/

/**
* Using 2 for loops.
*/
export function bubbleSort(items) {
export function bubbleSort(list) {
if (!Array.isArray(list)) {
throw new TypeError('Given input is not an array')
}

if (list.length === 0) {
return []
}

const items = [...list]
const length = items.length
let noSwaps

for (let i = length; i > 0; i--) {
// flag for optimization
noSwaps = true
// Number of passes
for (let j = 0; j < i - 1; j++) {
// Compare the adjacent positions
if (items[j] > items[j + 1]) {
// Swap the numbers
;[items[j], items[j + 1]] = [items[j + 1], items[j]]
noSwaps = false
}
Expand All @@ -43,7 +40,16 @@ export function bubbleSort(items) {
/**
* Using a while loop and a for loop.
*/
export function alternativeBubbleSort(arr) {
export function alternativeBubbleSort(list) {
if (!Array.isArray(list)) {
throw new TypeError('Given input is not an array')
}

if (list.length === 0) {
return []
}

const arr = [...list]
let swapped = true

while (swapped) {
Expand Down
38 changes: 18 additions & 20 deletions Sorts/QuickSort.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
/**
* @function QuickSort
* @description Quick sort is a comparison sorting algorithm that uses a divide and conquer strategy.
* @param {Integer[]} items - Array of integers
* @return {Integer[]} - Sorted array.
* @see [QuickSort](https://en.wikipedia.org/wiki/Quicksort)
* @function quickSort
* @description Quick Sort is a divide-and-conquer comparison-based sorting algorithm.
* @param {number[]} items - Array of integers
* @returns {number[]} - Sorted array
* @timecomplexity Average: O(n log n), Worst: O(n²)
* @spacecomplexity O(n)
* @see https://en.wikipedia.org/wiki/Quicksort
*/
function quickSort(items) {
const length = items.length
if (items.length <= 1) return items

if (length <= 1) {
return items
}
const PIVOT = items[0]
const GREATER = []
const LESSER = []
const pivotIndex = Math.floor(items.length / 2)
const pivot = items[pivotIndex]

const lesser = []
const greater = []

for (let i = 1; i < length; i++) {
if (items[i] > PIVOT) {
GREATER.push(items[i])
} else {
LESSER.push(items[i])
}
for (let i = 0; i < items.length; i++) {
if (i === pivotIndex) continue
if (items[i] < pivot) lesser.push(items[i])
else greater.push(items[i])
}

const sorted = [...quickSort(LESSER), PIVOT, ...quickSort(GREATER)]
return sorted
return [...quickSort(lesser), pivot, ...quickSort(greater)]
}

export { quickSort }
8 changes: 7 additions & 1 deletion Sorts/SelectionSort.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ export const selectionSort = (list) => {
if (!Array.isArray(list)) {
throw new TypeError('Given input is not an array')
}
const items = [...list] // We don't want to modify the original array

if (list.length === 0) {
return []
}

const items = [...list]

const length = items.length
for (let i = 0; i < length - 1; i++) {
if (typeof items[i] !== 'number') {
Expand Down