diff --git a/README.md b/README.md index 5412c60a..5836b72f 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ TypeScript Repository of TheAlgorithms, which implements various algorithms and [![TypeScript Banner][banner]](DIRECTORY.md) + [![codecov](https://codecov.io/gh/TheAlgorithms/TypeScript/graph/badge.svg?token=Z51PSAC1FD)](https://codecov.io/gh/TheAlgorithms/TypeScript) [![Contributions Welcome][welcome]](CONTRIBUTING.md) [![Discord chat][chat]][discord-server] diff --git a/data_structures/heap/heap.ts b/data_structures/heap/heap.ts index 100ce1b3..65ce9f0b 100644 --- a/data_structures/heap/heap.ts +++ b/data_structures/heap/heap.ts @@ -3,14 +3,12 @@ * In a complete binary tree each level is filled before lower levels are added * Each level is filled from left to right * - * In a (min|max) heap the value of every node is (less|greater) than that if its children + * In a (min|max) heap the value of every node is (less|greater) than that of its children * - * The heap if often implemented using an array structure. + * The heap is often implemented using an array structure. * In the array implementation, the relationship between a parent index and its two children * are ((parentindex * 2) + 1) and ((parentindex * 2) + 2) - * */ - export abstract class Heap { protected heap: T[] // A comparison function. Returns true if a should be the parent of b. @@ -23,17 +21,16 @@ export abstract class Heap { /** * Compares the value at parentIndex with the value at childIndex - * In a maxHeap the value at parentIndex should be larger than the value at childIndex - * In a minHeap the value at parentIndex should be smaller than the value at childIndex - * + * In a maxHeap, the value at parentIndex should be larger than the value at childIndex + * In a minHeap, the value at parentIndex should be smaller than the value at childIndex */ - private isRightlyPlaced(childIndex: number, parentIndex: number) { + private isRightlyPlaced(childIndex: number, parentIndex: number): boolean { return this.compare(this.heap[parentIndex], this.heap[childIndex]) } /** - * In a maxHeap the index with the larger value is returned - * In a minHeap the index with the smaller value is returned + * In a maxHeap, the index with the larger value is returned + * In a minHeap, the index with the smaller value is returned */ private getChildIndexToSwap( leftChildIndex: number, @@ -68,11 +65,11 @@ export abstract class Heap { return this.size() === 0 } - protected swap(a: number, b: number) { + protected swap(a: number, b: number): void { ;[this.heap[a], this.heap[b]] = [this.heap[b], this.heap[a]] } - protected bubbleUp(index = this.size() - 1): void { + protected bubbleUp(index: number = this.size() - 1): void { let parentIndex while (index > 0) { @@ -111,7 +108,7 @@ export abstract class Heap { } public check(): void { - return this._check() + this._check() } private _check(index: number = 0): void { @@ -122,14 +119,16 @@ export abstract class Heap { if ( this.heap[leftChildIndex] && !this.isRightlyPlaced(leftChildIndex, index) - ) + ) { throw new Error('Heap does not adhere to heap invariant') + } if ( this.heap[rightChildIndex] && !this.isRightlyPlaced(rightChildIndex, index) - ) + ) { throw new Error('Heap does not adhere to heap invariant') + } this._check(leftChildIndex) this._check(rightChildIndex) @@ -137,26 +136,17 @@ export abstract class Heap { } export class MinHeap extends Heap { - constructor( - compare = (a: T, b: T) => { - return a < b - } - ) { + constructor(compare: (a: T, b: T) => boolean = (a: T, b: T) => a < b) { super(compare) } } export class MaxHeap extends Heap { - constructor( - compare = (a: T, b: T) => { - return a > b - } - ) { + constructor(compare: (a: T, b: T) => boolean = (a: T, b: T) => a > b) { super(compare) } } -// Priority queue that supports increasePriority() in O(log(n)). The limitation is that there can only be a single element for each key, and the max number or keys must be specified at heap construction. Most of the functions are wrappers around MinHeap functions and update the keys array. export class PriorityQueue extends MinHeap { // Maps from the n'th node to its index within the heap. private keys: number[] @@ -166,29 +156,27 @@ export class PriorityQueue extends MinHeap { constructor( keys_index: (a: T) => number, num_keys: number, - compare = (a: T, b: T) => { - return a < b - } + compare: (a: T, b: T) => boolean = (a: T, b: T) => a < b ) { super(compare) this.keys = Array(num_keys).fill(-1) this.keys_index = keys_index } - protected swap(a: number, b: number) { + protected swap(a: number, b: number): void { const akey = this.keys_index(this.heap[a]) const bkey = this.keys_index(this.heap[b]) ;[this.keys[akey], this.keys[bkey]] = [this.keys[bkey], this.keys[akey]] super.swap(a, b) } - public insert(value: T) { + public insert(value: T): void { this.keys[this.keys_index(value)] = this.size() super.insert(value) } public extract(): T { - // Unmark the the highest priority element and set key to zero for the last element in the heap. + // Unmark the highest priority element and set key to zero for the last element in the heap. this.keys[this.keys_index(this.heap[0])] = -1 if (this.size() > 1) { this.keys[this.keys_index(this.heap[this.size() - 1])] = 0 @@ -196,8 +184,8 @@ export class PriorityQueue extends MinHeap { return super.extract() } - public increasePriority(idx: number, value: T) { - if (this.keys[idx] == -1) { + public increasePriority(idx: number, value: T): void { + if (this.keys[idx] === -1) { // If the key does not exist, insert the value. this.insert(value) return diff --git a/data_structures/tries/tries.ts b/data_structures/tries/tries.ts index 78dc1432..2ec99c94 100644 --- a/data_structures/tries/tries.ts +++ b/data_structures/tries/tries.ts @@ -5,12 +5,12 @@ class TrieNode { /** * An object that stores child nodes for each character in the alphabet. */ - children: { [key: string]: TrieNode } = {} + children: Record = {} /** * Indicates whether the node represents the end of a word. */ - isWord: boolean = false + isWord = false } /** @@ -22,11 +22,6 @@ export class Trie { */ root: TrieNode = new TrieNode() - /** - * Creates a new Trie instance. - */ - constructor() {} - /** * Inserts a word into the Trie. * @@ -51,7 +46,7 @@ export class Trie { * If false, the method returns true only if an exact match is found. * @returns True if the word (or prefix) is found in the Trie; otherwise, false. */ - public find(word: string, isPrefixMatch: boolean = false): boolean { + public find(word: string, isPrefixMatch = false): boolean { return this.searchNode(this.root, word, isPrefixMatch) } diff --git a/dynamic_programming/coin_change.ts b/dynamic_programming/coin_change.ts index 0734fb9a..2e9e7b0d 100644 --- a/dynamic_programming/coin_change.ts +++ b/dynamic_programming/coin_change.ts @@ -17,12 +17,12 @@ export const coinChange = (money: number, coins: number[]): CoinChange => { minCoins[0] = 0 // Fill in the DP table - for (let i = 0; i < coins.length; i++) { + for (const coin of coins) { for (let j = 0; j <= money; j++) { - if (j >= coins[i]) { - if (minCoins[j] > 1 + minCoins[j - coins[i]]) { - minCoins[j] = 1 + minCoins[j - coins[i]] - lastCoin[j] = coins[i] + if (j >= coin) { + if (minCoins[j] > 1 + minCoins[j - coin]) { + minCoins[j] = 1 + minCoins[j - coin] + lastCoin[j] = coin } } } diff --git a/dynamic_programming/knapsack.ts b/dynamic_programming/knapsack.ts index 853e50ab..0bc51012 100644 --- a/dynamic_programming/knapsack.ts +++ b/dynamic_programming/knapsack.ts @@ -1,56 +1,54 @@ /** - * @function knapsack - * @description Given weights and values of n (numberOfItems) items, put these items in a knapsack of capacity to get the maximum total value in the knapsack. In other words, given two integer arrays values[0..n-1] and weights[0..n-1] which represent values and weights associated with n items respectively. Also given an integer capacity which represents knapsack capacity, find out the maximum value subset of values[] such that sum of the weights of this subset is smaller than or equal to capacity. You cannot break an item, either pick the complete item or don’t pick it (0-1 property). - * @Complexity_Analysis - * Space complexity - O(1) - * Time complexity (independent of input) : O(numberOfItems * capacity) - * - * @return maximum value subset of values[] such that sum of the weights of this subset is smaller than or equal to capacity. - * @see [Knapsack](https://www.geeksforgeeks.org/0-1-knapsack-problem-dp-10/) - * @example knapsack(3, 8, [3, 4, 5], [30, 50, 60]) = 90 + * Solves the 0-1 Knapsack Problem. + * @param capacity Knapsack capacity + * @param weights Array of item weights + * @param values Array of item values + * @returns Maximum value subset such that sum of the weights of this subset is smaller than or equal to capacity + * @throws If weights and values arrays have different lengths + * @see [Knapsack](https://www.geeksforgeeks.org/0-1-knapsack-problem-dp-10/) + * @example knapsack(3, [3, 4, 5], [30, 50, 60]) // Output: 90 */ + export const knapsack = ( capacity: number, weights: number[], values: number[] -) => { - if (weights.length != values.length) { +): number => { + if (weights.length !== values.length) { throw new Error( - 'weights and values arrays should have same number of elements' + 'Weights and values arrays should have the same number of elements' ) } - const numberOfItems = weights.length + const numberOfItems: number = weights.length - // Declaring a data structure to store calculated states/values + // Initializing a 2D array to store calculated states/values const dp: number[][] = new Array(numberOfItems + 1) - - for (let i = 0; i < dp.length; i++) { - // Placing an array at each index of dp to make it a 2d matrix - dp[i] = new Array(capacity + 1) - } + .fill(0) + .map(() => new Array(capacity + 1).fill(0)) // Loop traversing each state of dp - for (let i = 0; i < numberOfItems; i++) { - for (let j = 0; j <= capacity; j++) { - if (i == 0) { - if (j >= weights[i]) { - // grab the first item if it's weight is less than remaining weight (j) - dp[i][j] = values[i] - } else { - // if weight[i] is more than remaining weight (j) leave it - dp[i][j] = 0 - } - } else if (j < weights[i]) { - // if weight of current item (weights[i]) is more than remaining weight (j), leave the current item and just carry on previous items - dp[i][j] = dp[i - 1][j] + for (let itemIndex = 1; itemIndex <= numberOfItems; itemIndex++) { + const weight = weights[itemIndex - 1] + const value = values[itemIndex - 1] + for ( + let currentCapacity = 1; + currentCapacity <= capacity; + currentCapacity++ + ) { + if (weight <= currentCapacity) { + // Select the maximum value of including the current item or excluding it + dp[itemIndex][currentCapacity] = Math.max( + value + dp[itemIndex - 1][currentCapacity - weight], + dp[itemIndex - 1][currentCapacity] + ) } else { - // select the maximum of (if current weight is collected thus adding it's value) and (if current weight is not collected thus not adding it's value) - dp[i][j] = Math.max(dp[i - 1][j - weights[i]] + values[i], dp[i - 1][j]) + // If the current item's weight exceeds the current capacity, exclude it + dp[itemIndex][currentCapacity] = dp[itemIndex - 1][currentCapacity] } } } - // Return the final maximized value at last position of dp matrix - return dp[numberOfItems - 1][capacity] + // Return the final maximized value at the last position of the dp matrix + return dp[numberOfItems][capacity] } diff --git a/graph/dijkstra.ts b/graph/dijkstra.ts index 7804a592..1ad76fa0 100644 --- a/graph/dijkstra.ts +++ b/graph/dijkstra.ts @@ -1,4 +1,4 @@ -import { MinHeap, PriorityQueue } from '../data_structures/heap/heap' +import { PriorityQueue } from '../data_structures/heap/heap' /** * @function dijkstra * @description Compute the shortest path from a source node to all other nodes. The input graph is in adjacency list form. It is a multidimensional array of edges. graph[i] holds the edges for the i'th node. Each edge is a 2-tuple where the 0'th item is the destination node, and the 1'th item is the edge weight. @@ -32,7 +32,7 @@ export const dijkstra = ( distances[start] = 0 while (priorityQueue.size() > 0) { - const [node, _] = priorityQueue.extract() + const node = priorityQueue.extract()[0] graph[node].forEach(([child, weight]) => { const new_distance = distances[node] + weight if (new_distance < distances[child]) { diff --git a/graph/edmonds_karp.ts b/graph/edmonds_karp.ts new file mode 100644 index 00000000..fb781a42 --- /dev/null +++ b/graph/edmonds_karp.ts @@ -0,0 +1,97 @@ +import { StackQueue } from '../data_structures/queue/stack_queue' + +/** + * @function edmondsKarp + * @description Compute the maximum flow from a source node to a sink node using the Edmonds-Karp algorithm. + * @Complexity_Analysis + * Time complexity: O(V * E^2) where V is the number of vertices and E is the number of edges. + * Space Complexity: O(E) due to residual graph representation. + * @param {[number, number][][]} graph - The graph in adjacency list form. + * @param {number} source - The source node. + * @param {number} sink - The sink node. + * @return {number} - The maximum flow from the source node to the sink node. + * @see https://en.wikipedia.org/wiki/Edmonds%E2%80%93Karp_algorithm + */ +export default function edmondsKarp( + graph: [number, number][][], + source: number, + sink: number +): number { + const n = graph.length + + // Initialize residual graph + const residualGraph: [number, number][][] = Array.from( + { length: n }, + () => [] + ) + + // Build residual graph from the original graph + for (let u = 0; u < n; u++) { + for (const [v, cap] of graph[u]) { + if (cap > 0) { + residualGraph[u].push([v, cap]) // Forward edge + residualGraph[v].push([u, 0]) // Reverse edge with 0 capacity + } + } + } + + const findAugmentingPath = (parent: (number | null)[]): number => { + const visited = Array(n).fill(false) + const queue = new StackQueue() + queue.enqueue(source) + visited[source] = true + parent[source] = null + + while (queue.length() > 0) { + const u = queue.dequeue() + for (const [v, cap] of residualGraph[u]) { + if (!visited[v] && cap > 0) { + parent[v] = u + visited[v] = true + if (v === sink) { + // Return the bottleneck capacity along the path + let pathFlow = Infinity + let current = v + while (parent[current] !== null) { + const prev = parent[current]! + const edgeCap = residualGraph[prev].find( + ([node]) => node === current + )![1] + pathFlow = Math.min(pathFlow, edgeCap) + current = prev + } + return pathFlow + } + queue.enqueue(v) + } + } + } + return 0 + } + + let maxFlow = 0 + const parent = Array(n).fill(null) + + while (true) { + const pathFlow = findAugmentingPath(parent) + if (pathFlow === 0) break // No augmenting path found + + // Update the capacities and reverse capacities in the residual graph + let v = sink + while (parent[v] !== null) { + const u = parent[v]! + // Update capacity of the forward edge + const forwardEdge = residualGraph[u].find(([node]) => node === v)! + forwardEdge[1] -= pathFlow + // Update capacity of the reverse edge + const reverseEdge = residualGraph[v].find(([node]) => node === u)! + reverseEdge[1] += pathFlow + + v = u + } + + maxFlow += pathFlow + } + + return maxFlow +} diff --git a/graph/prim.ts b/graph/prim.ts index 3261c633..b3f565c7 100644 --- a/graph/prim.ts +++ b/graph/prim.ts @@ -51,8 +51,7 @@ const add_children = ( priorityQueue: PriorityQueue, node: number ) => { - for (let i = 0; i < graph[node].length; ++i) { - const out_edge = graph[node][i] + for (const out_edge of graph[node]) { // By increasing the priority, we ensure we only add each vertex to the queue one time, and the queue will be at most size V. priorityQueue.increasePriority( out_edge[0], diff --git a/graph/test/edmonds_karp.test.ts b/graph/test/edmonds_karp.test.ts new file mode 100644 index 00000000..22711ab9 --- /dev/null +++ b/graph/test/edmonds_karp.test.ts @@ -0,0 +1,82 @@ +import edmondsKarp from '../edmonds_karp' + +describe('Edmonds-Karp Algorithm', () => { + it('should find the maximum flow in a simple graph', () => { + const graph: [number, number][][] = [ + [ + [1, 3], + [2, 2] + ], // Node 0: Edges to node 1 (capacity 3), and node 2 (capacity 2) + [[3, 2]], // Node 1: Edge to node 3 (capacity 2) + [[3, 3]], // Node 2: Edge to node 3 (capacity 3) + [] // Node 3: No outgoing edges + ] + const source = 0 + const sink = 3 + const maxFlow = edmondsKarp(graph, source, sink) + expect(maxFlow).toBe(4) + }) + + it('should find the maximum flow in a more complex graph', () => { + const graph: [number, number][][] = [ + [ + [1, 10], + [2, 10] + ], // Node 0: Edges to node 1 and node 2 (both capacity 10) + [ + [3, 4], + [4, 8] + ], // Node 1: Edges to node 3 (capacity 4), and node 4 (capacity 8) + [[4, 9]], // Node 2: Edge to node 4 (capacity 9) + [[5, 10]], // Node 3: Edge to node 5 (capacity 10) + [[5, 10]], // Node 4: Edge to node 5 (capacity 10) + [] // Node 5: No outgoing edges (sink) + ] + const source = 0 + const sink = 5 + const maxFlow = edmondsKarp(graph, source, sink) + expect(maxFlow).toBe(14) + }) + + it('should return 0 when there is no path from source to sink', () => { + const graph: [number, number][][] = [ + [], // Node 0: No outgoing edges + [], // Node 1: No outgoing edges + [] // Node 2: No outgoing edges (sink) + ] + const source = 0 + const sink = 2 + const maxFlow = edmondsKarp(graph, source, sink) + expect(maxFlow).toBe(0) + }) + + it('should handle graphs with no edges', () => { + const graph: [number, number][][] = [ + [], // Node 0: No outgoing edges + [], // Node 1: No outgoing edges + [] // Node 2: No outgoing edges + ] + const source = 0 + const sink = 2 + const maxFlow = edmondsKarp(graph, source, sink) + expect(maxFlow).toBe(0) + }) + + it('should handle graphs with self-loops', () => { + const graph: [number, number][][] = [ + [ + [0, 10], + [1, 10] + ], // Node 0: Self-loop with capacity 10, and edge to node 1 (capacity 10) + [ + [1, 10], + [2, 10] + ], // Node 1: Self-loop and edge to node 2 + [] // Node 2: No outgoing edges (sink) + ] + const source = 0 + const sink = 2 + const maxFlow = edmondsKarp(graph, source, sink) + expect(maxFlow).toBe(10) + }) +}) diff --git a/search/binary_search.ts b/search/binary_search.ts index 39d380a4..cf86f39a 100644 --- a/search/binary_search.ts +++ b/search/binary_search.ts @@ -8,25 +8,25 @@ * * @param {number[]} array - sorted list of numbers * @param {number} target - target number to search for - * @return {number} - index of the target number in the list, or -1 if not found + * @return {number} - index of the target number in the list, or null if not found * @see [BinarySearch](https://www.geeksforgeeks.org/binary-search/) * @example binarySearch([1,2,3], 2) => 1 - * @example binarySearch([4,5,6], 2) => -1 + * @example binarySearch([4,5,6], 2) => null */ export const binarySearchIterative = ( array: number[], - target: number -): number => { - if (array.length === 0) return -1 - - // declare pointers for the start, middle and end indices - let start = 0, - end = array.length - 1, - middle = (start + end) >> 1 + target: number, + start: number = 0, + end: number = array.length - 1 +): number | null => { + if (array.length === 0) return null // ensure the target is within the bounds of the array - if (target < array[start] || target > array[end]) return -1 + if (target < array[start] || target > array[end]) return null + + // declare pointers for the middle index + let middle = (start + end) >> 1 while (array[middle] !== target && start <= end) { // if the target is less than the middle value, move the end pointer to be middle -1 to narrow the search space @@ -37,7 +37,7 @@ export const binarySearchIterative = ( middle = (start + end) >> 1 } // return the middle index if it is equal to target - return array[middle] === target ? middle : -1 + return array[middle] === target ? middle : null } export const binarySearchRecursive = ( @@ -45,16 +45,16 @@ export const binarySearchRecursive = ( target: number, start = 0, end = array.length - 1 -): number => { - if (array.length === 0) return -1 +): number | null => { + if (array.length === 0) return null // ensure the target is within the bounds of the array - if (target < array[start] || target > array[end]) return -1 + if (target < array[start] || target > array[end]) return null const middle = (start + end) >> 1 if (array[middle] === target) return middle // target found - if (start > end) return -1 // target not found + if (start > end) return null // target not found // if the target is less than the middle value, move the end pointer to be middle -1 to narrow the search space // otherwise, move the start pointer to be middle + 1 diff --git a/search/exponential_search.ts b/search/exponential_search.ts new file mode 100644 index 00000000..4a8eba47 --- /dev/null +++ b/search/exponential_search.ts @@ -0,0 +1,40 @@ +import { binarySearchIterative } from './binary_search' + +/** + * @description Exponential search algorithm for a sorted array. + * + * The algorithm searches for a specific value in a sorted array by first finding a range + * where the value may be present and then performing a binary search within that range. + * + * Compared with binary search, exponential search can be more convenient and advantageous + * in cases where the element to be searched is closer to the beginning of the array, + * thus avoiding several comparisons that would make the search more verbose. + * + * @param {number[]} array - sorted list of numbers + * @param {number} x - target number to search for + * @return {number | null} - index of the target number in the list, or null if not found + * @see [ExponentialSearch](https://www.geeksforgeeks.org/exponential-search/) + * @example exponentialSearch([1, 2, 3, 4, 5], 3) => 2 + * @example exponentialSearch([10, 20, 30, 40, 50], 35) => null + */ + +export const exponentialSearch = ( + array: number[], + x: number +): number | null => { + const arrayLength = array.length + if (arrayLength === 0) return null + + if (array[0] === x) return 0 + + let i = 1 + while (i < arrayLength && array[i] <= x) { + i = i * 2 + } + + const start = Math.floor(i / 2) + const end = Math.min(i, arrayLength - 1) + const result = binarySearchIterative(array, x, start, end) + + return result +} diff --git a/search/fibonacci_search.ts b/search/fibonacci_search.ts new file mode 100644 index 00000000..b0125277 --- /dev/null +++ b/search/fibonacci_search.ts @@ -0,0 +1,57 @@ +/** + * @description Fibonacci search algorithm for a sorted array. + * + * The algorithm searches for a specific value in a sorted array using Fibonacci numbers + * to divide the array into smaller subarrays. This algorithm is useful for large arrays where + * the cost of accessing elements is high. + * + * @param {number[]} array - sorted list of numbers + * @param {number} target - target number to search for + * @return {number | null} - index of the target number in the list, or null if not found + * @see [FibonacciSearch](https://www.geeksforgeeks.org/fibonacci-search/) + * @example fibonacciSearch([1,2,3], 2) => 1 + * @example fibonacciSearch([4,5,6], 2) => null + */ + +export const fibonacciSearch = ( + array: number[], + target: number +): number | null => { + const arrayLength = array.length + let a = 0 // (n-2)'th Fibonacci No. + let b = 1 // (n-1)'th Fibonacci No. + let c = a + b // n'th Fibonacci + + while (c < arrayLength) { + a = b + b = c + c = a + b + } + + let offset = -1 + + while (c > 1) { + let i = Math.min(offset + a, arrayLength - 1) + + if (array[i] < target) { + c = b + b = a + a = c - b + offset = i + } else if (array[i] > target) { + c = a + b = b - a + a = c - b + } else { + // Element found then return index + return i + } + } + + if (b && array[offset + 1] === target) { + return offset + 1 + } + + // Element not found then return null + return null +} diff --git a/search/jump_search.ts b/search/jump_search.ts index 9707dbf3..e54aa196 100644 --- a/search/jump_search.ts +++ b/search/jump_search.ts @@ -22,8 +22,8 @@ export const jumpSearch = (array: number[], target: number): number => { if (array.length === 0) return -1 // declare pointers for the current and next indexes and step size + const stepSize: number = Math.floor(Math.sqrt(array.length)) let currentIdx: number = 0, - stepSize: number = Math.floor(Math.sqrt(array.length)), nextIdx: number = stepSize while (array[nextIdx - 1] < target) { diff --git a/search/test/binary_search.test.ts b/search/test/binary_search.test.ts index 6ebd12d9..13b13251 100644 --- a/search/test/binary_search.test.ts +++ b/search/test/binary_search.test.ts @@ -2,7 +2,7 @@ import { binarySearchIterative, binarySearchRecursive } from '../binary_search' describe('BinarySearch', () => { const testArray: number[] = [1, 2, 3, 4] - type FunctionsArray = { (array: number[], index: number): number }[] + type FunctionsArray = { (array: number[], index: number): number | null }[] const functions: FunctionsArray = [ binarySearchIterative, binarySearchRecursive @@ -12,14 +12,16 @@ describe('BinarySearch', () => { it('should be defined', () => { expect(func(testArray, 2)).toBeDefined() }) - it('should return a number', () => { - expect(typeof func(testArray, 2)).toBe('number') + it('should return a number or null', () => { + expect( + typeof func(testArray, 2) === 'number' || func(testArray, 2) === null + ).toBe(true) }) - it('should return -1 if the target is not found in the array', () => { - expect(func(testArray, 5)).toBe(-1) + it('should return null if the target is not found in the array', () => { + expect(func(testArray, 5)).toBe(null) }) - it('should return -1 if there are no elements in the array', () => { - expect(func([], 5)).toBe(-1) + it('should return null if there are no elements in the array', () => { + expect(func([], 5)).toBe(null) }) it('should return the index of the target if it is found in the array', () => { expect(func(testArray, 2)).toBe(1) diff --git a/search/test/exponential_search.test.ts b/search/test/exponential_search.test.ts new file mode 100644 index 00000000..80f6a07f --- /dev/null +++ b/search/test/exponential_search.test.ts @@ -0,0 +1,19 @@ +import { exponentialSearch } from '../exponential_search' + +describe('Exponential search', () => { + test.each([ + [[1, 2, 3, 4, 5], 3, 2], + [[10, 20, 30, 40, 50], 35, null], + [[10, 20, 30, 40, 50], 10, 0], + [[10, 20, 30, 40, 50], 50, 4], + [[10, 20, 30, 40, 50], 60, null], + [[], 10, null], + [[1, 2, 3, 4, 5], 1, 0], + [[1, 2, 3, 4, 5], 5, 4] + ])( + 'of %o, searching for %o, expected %i', + (array: number[], target: number, expected: number | null) => { + expect(exponentialSearch(array, target)).toBe(expected) + } + ) +}) diff --git a/search/test/fibonacci_search.test.ts b/search/test/fibonacci_search.test.ts new file mode 100644 index 00000000..5b2b54b7 --- /dev/null +++ b/search/test/fibonacci_search.test.ts @@ -0,0 +1,18 @@ +import { fibonacciSearch } from '../fibonacci_search' + +describe('Fibonacci search', () => { + test.each([ + [[1, 2, 3], 2, 1], + [[4, 5, 6], 2, null], + [[10, 22, 35, 40, 45, 50, 80, 82, 85, 90, 100], 85, 8], + [[], 1, null], + [[1], 1, 0], + [[1, 3, 5, 7, 9, 11, 13], 11, 5], + [[1, 3, 5, 7, 9, 11, 13], 8, null] + ])( + 'of %o, searching for %o, expected %i', + (array: number[], target: number, expected: number | null) => { + expect(fibonacciSearch(array, target)).toBe(expected) + } + ) +}) diff --git a/sorts/counting_sort.ts b/sorts/counting_sort.ts index cedad89f..49e76232 100644 --- a/sorts/counting_sort.ts +++ b/sorts/counting_sort.ts @@ -13,7 +13,7 @@ export const countingSort = (inputArr: number[], min: number, max: number) => { const count = new Array(max - min + 1).fill(0) - for (let i = 0; i < inputArr.length; i++) count[inputArr[i] - min]++ + for (const element of inputArr) count[element - min]++ count[0] -= 1 diff --git a/sorts/shell_sort.ts b/sorts/shell_sort.ts index 4b1061ba..1a409659 100644 --- a/sorts/shell_sort.ts +++ b/sorts/shell_sort.ts @@ -9,11 +9,11 @@ * Average case - O(n log(n)^2) * * @param {T[]} arr - The input array - * @return {Array} - The sorted array. + * @return {T[]} - The sorted array. * @see [Shell Sort] (https://www.geeksforgeeks.org/shellsort/) * @example shellSort([4, 1, 8, 10, 3, 2, 5, 0, 7, 6, 9]) = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] */ -export function shellSort(arr: T[]): Array { +export function shellSort(arr: T[]): T[] { // start with the biggest gap, reduce gap twice on each step for (let gap = arr.length >> 1; gap > 0; gap >>= 1) { for (let i = gap; i < arr.length; i++) {