diff --git a/search/ternary_search.ts b/search/ternary_search.ts new file mode 100644 index 00000000..c6f96778 --- /dev/null +++ b/search/ternary_search.ts @@ -0,0 +1,62 @@ +/** + * @function ternarySearch + * @description Ternary search is a divide-and-conquer search algorithm similar to binary search. + * It divides the search space into three parts instead of two. It's useful for searching in sorted arrays, + * especially for searching peaks in mountains or similar structures. It can also be used as an alternative to binary search. + * @Complexity_Analysis + * Space complexity - O(log₃ n) (recursion depth) + * Time complexity + * Best case - O(1) + * When the element is at one of the dividing points + * Worst case - O(log₃ n) + * When we need to go through the entire search tree + * Average case - O(log₃ n) + * + * @param {number[]} arr - The sorted input array + * @param {number} target - The target value to search + * @return {number} - The index of the target if found, otherwise -1 + * @see [Ternary Search](https://en.wikipedia.org/wiki/Ternary_search) + * @example ternarySearch([1, 2, 3, 4, 5, 6, 7, 8, 9], 5) = 4 + */ + +export function ternarySearch(arr: number[], target: number): number { + return ternarySearchHelper(arr, target, 0, arr.length - 1); +} + +function ternarySearchHelper( + arr: number[], + target: number, + left: number, + right: number, +): number { + if (left > right) { + return -1; + } + + // Divide the array into 3 parts + const mid1 = Math.floor(left + (right - left) / 3); + const mid2 = Math.floor(right - (right - left) / 3); + + // Check if target is at mid1 + if (arr[mid1] === target) { + return mid1; + } + + // Check if target is at mid2 + if (arr[mid2] === target) { + return mid2; + } + + // If target is less than mid1, search in left third + if (target < arr[mid1]) { + return ternarySearchHelper(arr, target, left, mid1 - 1); + } + + // If target is greater than mid2, search in right third + if (target > arr[mid2]) { + return ternarySearchHelper(arr, target, mid2 + 1, right); + } + + // If target is between mid1 and mid2, search in middle third + return ternarySearchHelper(arr, target, mid1 + 1, mid2 - 1); +} diff --git a/search/test/ternary_search.test.ts b/search/test/ternary_search.test.ts new file mode 100644 index 00000000..82ec2f69 --- /dev/null +++ b/search/test/ternary_search.test.ts @@ -0,0 +1,36 @@ +import { ternarySearch } from '../ternary_search'; + +describe('Ternary Search', () => { + test('should find the target element', () => { + expect(ternarySearch([1, 2, 3, 4, 5, 6, 7, 8, 9], 5)).toBe(4); + }); + + test('should return -1 when target is not found', () => { + expect(ternarySearch([1, 2, 3, 4, 5, 6, 7, 8, 9], 10)).toBe(-1); + }); + + test('should find the first element', () => { + expect(ternarySearch([1, 2, 3, 4, 5], 1)).toBe(0); + }); + + test('should find the last element', () => { + expect(ternarySearch([1, 2, 3, 4, 5], 5)).toBe(4); + }); + + test('should handle single element array', () => { + expect(ternarySearch([5], 5)).toBe(0); + }); + + test('should return -1 for single element array when target not found', () => { + expect(ternarySearch([5], 3)).toBe(-1); + }); + + test('should handle empty array', () => { + expect(ternarySearch([], 5)).toBe(-1); + }); + + test('should find element in array with duplicates', () => { + const result = ternarySearch([1, 2, 2, 2, 3, 4, 5], 2); + expect([1, 2, 3]).toContain(result); + }); +}); diff --git a/sorts/radix_sort.ts b/sorts/radix_sort.ts new file mode 100644 index 00000000..382c87c5 --- /dev/null +++ b/sorts/radix_sort.ts @@ -0,0 +1,63 @@ +/** + * @function radixSort + * @description Radix sort is a non-comparative integer sorting algorithm that sorts numbers by processing digits. + * It works by sorting elements digit by digit, starting from the least significant digit to the most significant digit. + * This algorithm is efficient for sorting large numbers of integers with a fixed number of digits. + * @Complexity_Analysis + * Space complexity - O(n + k) where k is the range of input + * Time complexity + * Best case - O(n * d) + * where d is the number of digits + * Worst case - O(n * d) + * where d is the number of digits + * Average case - O(n * d) + * where d is the number of digits + * + * @param {number[]} arr - The input array + * @return {number[]} - The sorted array + * @see [Radix Sort](https://en.wikipedia.org/wiki/Radix_sort) + * @example radixSort([170, 45, 75, 90, 2, 802, 24, 2, 66]) = [2, 2, 24, 45, 66, 75, 90, 170, 802] + */ + +export function radixSort(arr: number[]): number[] { + if (arr.length === 0) return arr; + + const max = Math.max(...arr); + let exp = 1; + + while (max / exp > 0) { + countingSortByDigit(arr, exp); + exp *= 10; + } + + return arr; +} + +function countingSortByDigit(arr: number[], exp: number): void { + const n = arr.length; + const output: number[] = new Array(n); + const count: number[] = new Array(10).fill(0); + + // Count occurrences of each digit + for (let i = 0; i < n; i++) { + const digit = Math.floor((arr[i] / exp) % 10); + count[digit]++; + } + + // Change count[i] so that count[i] contains actual position + for (let i = 1; i < 10; i++) { + count[i] += count[i - 1]; + } + + // Build the output array + for (let i = n - 1; i >= 0; i--) { + const digit = Math.floor((arr[i] / exp) % 10); + output[count[digit] - 1] = arr[i]; + count[digit]--; + } + + // Copy the output array to arr + for (let i = 0; i < n; i++) { + arr[i] = output[i]; + } +} diff --git a/sorts/test/radix_sort.test.ts b/sorts/test/radix_sort.test.ts new file mode 100644 index 00000000..a2f5830c --- /dev/null +++ b/sorts/test/radix_sort.test.ts @@ -0,0 +1,29 @@ +import { radixSort } from '../radix_sort'; + +describe('Radix Sort', () => { + test('should sort an unsorted array', () => { + expect(radixSort([170, 45, 75, 90, 2, 802, 24, 2, 66])).toEqual([ + 2, 2, 24, 45, 66, 75, 90, 170, 802, + ]); + }); + + test('should sort a single element array', () => { + expect(radixSort([5])).toEqual([5]); + }); + + test('should sort an already sorted array', () => { + expect(radixSort([1, 2, 3, 4, 5])).toEqual([1, 2, 3, 4, 5]); + }); + + test('should sort a reverse sorted array', () => { + expect(radixSort([5, 4, 3, 2, 1])).toEqual([1, 2, 3, 4, 5]); + }); + + test('should handle empty array', () => { + expect(radixSort([])).toEqual([]); + }); + + test('should sort array with duplicate elements', () => { + expect(radixSort([3, 3, 1, 1, 2])).toEqual([1, 1, 2, 3, 3]); + }); +});