From 0496404431705d3f67a4521839376a252013d760 Mon Sep 17 00:00:00 2001 From: Jacques Priso Date: Mon, 26 Jun 2023 19:53:11 +0200 Subject: [PATCH 1/2] feat: add quick select algorithm and test #6 --- sorts/quick_select.ts | 54 +++++++++++++++++++++++++++++++++ sorts/test/quick_select.test.ts | 18 +++++++++++ 2 files changed, 72 insertions(+) create mode 100644 sorts/quick_select.ts create mode 100644 sorts/test/quick_select.test.ts diff --git a/sorts/quick_select.ts b/sorts/quick_select.ts new file mode 100644 index 00000000..e1306c92 --- /dev/null +++ b/sorts/quick_select.ts @@ -0,0 +1,54 @@ +/** + * @function partition + * @description is a helper function used in the QuickSelect algorithm to partition the array around a pivot element + * @param {number[]} arr - The array to partition + * @param {number} left - The left boundary of the partition range + * @param {number} right - The right boundary of the partition range + * @param {number} pivotIndex - The index of the pivot element + * @returns {number} - The new index of the pivot element after partitioning + */ + +function partition(arr: number[], left: number, right: number, pivotIndex: number): number { + const pivotValue = arr[pivotIndex]; + [arr[pivotIndex], arr[right]] = [arr[right], arr[pivotIndex]]; + let storeIndex = left; + + for (let i = left; i < right; i++) { + if (arr[i] < pivotValue) { + [arr[i], arr[storeIndex]] = [arr[storeIndex], arr[i]]; + storeIndex++; + } + } + + [arr[storeIndex], arr[right]] = [arr[right], arr[storeIndex]]; + return storeIndex; +} + +/** + * @function quickSelect + * @description is an algorithm based on the QuickSort approach that selects the kth smallest element from an array + * @param {number[]} arr - The array from which to select the element + * @param {number} left - The left boundary of the array or subarray to consider + * @param {number} right - The right boundary of the array or subarray to consider + * @param {number} k - The index representing the kth smallest element to find + * @returns {number} - The kth smallest element from the array + */ + +export function quickSelect(arr: number[], left: number, right: number, k: number): number { + if (left === right) { + return arr[left]; + } + + const pivotIndex = Math.floor(Math.random() * (right - left + 1)) + left; + const pivotNewIndex = partition(arr, left, right, pivotIndex); + + if (k === pivotNewIndex) { + return arr[k]; + } else if (k < pivotNewIndex) { + return quickSelect(arr, left, pivotNewIndex - 1, k); + } else { + return quickSelect(arr, pivotNewIndex + 1, right, k); + } +} + + diff --git a/sorts/test/quick_select.test.ts b/sorts/test/quick_select.test.ts new file mode 100644 index 00000000..99313370 --- /dev/null +++ b/sorts/test/quick_select.test.ts @@ -0,0 +1,18 @@ +import { quickSelect } from "../quick_select"; + +describe("Quick Select", () => { + + it("should return the correct value for worst case", () => { + const arr = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; + const k = 2; // kth smallest element + + expect(quickSelect(arr, 0, arr.length - 1, k)).toBe(3); + }); + + it("should return the correct value for best case", () => { + const arr = [1, 4, 2, 9, 5, 7, 3, 8, 10, 6]; + const k = 5; // kth smallest element + + expect(quickSelect(arr, 0, arr.length - 1, k)).toBe(6); + }); +}); From a8b2b29e9129c01f22a803a7ecf15ac11fd5f881 Mon Sep 17 00:00:00 2001 From: Jacques Priso Date: Wed, 28 Jun 2023 13:13:13 +0200 Subject: [PATCH 2/2] feat: update quick select algorithm and test #6 --- sorts/quick_select.ts | 70 +++++++++++++-------------------- sorts/test/quick_select.test.ts | 33 ++++++++++------ 2 files changed, 50 insertions(+), 53 deletions(-) diff --git a/sorts/quick_select.ts b/sorts/quick_select.ts index e1306c92..23381ee2 100644 --- a/sorts/quick_select.ts +++ b/sorts/quick_select.ts @@ -1,54 +1,40 @@ +import {partition} from "./quick_sort"; /** - * @function partition - * @description is a helper function used in the QuickSelect algorithm to partition the array around a pivot element - * @param {number[]} arr - The array to partition - * @param {number} left - The left boundary of the partition range - * @param {number} right - The right boundary of the partition range - * @param {number} pivotIndex - The index of the pivot element - * @returns {number} - The new index of the pivot element after partitioning - */ - -function partition(arr: number[], left: number, right: number, pivotIndex: number): number { - const pivotValue = arr[pivotIndex]; - [arr[pivotIndex], arr[right]] = [arr[right], arr[pivotIndex]]; - let storeIndex = left; - - for (let i = left; i < right; i++) { - if (arr[i] < pivotValue) { - [arr[i], arr[storeIndex]] = [arr[storeIndex], arr[i]]; - storeIndex++; - } - } - - [arr[storeIndex], arr[right]] = [arr[right], arr[storeIndex]]; - return storeIndex; -} - -/** - * @function quickSelect + * @function QuickSelect * @description is an algorithm based on the QuickSort approach that selects the kth smallest element from an array - * @param {number[]} arr - The array from which to select the element - * @param {number} left - The left boundary of the array or subarray to consider - * @param {number} right - The right boundary of the array or subarray to consider + * @param {number[]} array - The array from which to select the element * @param {number} k - The index representing the kth smallest element to find + * @param {number} left - The left boundary of the array or subarray to consider (default: 0) + * @param {number} right - The right boundary of the array or subarray to consider (default: array.length - 1) * @returns {number} - The kth smallest element from the array + * @throws {Error} - If k is out of bounds (less than 0 or greater than or equal to array.length) */ -export function quickSelect(arr: number[], left: number, right: number, k: number): number { +export const QuickSelect = ( + array: number[], + k: number, + left: number = 0, + right: number = array.length - 1 +):number => { + if(k < 0 || k >= array.length) { + throw new Error('k is out of bounds'); + } if (left === right) { - return arr[left]; + // If the list contains only one element, return that element + return array[left]; } - const pivotIndex = Math.floor(Math.random() * (right - left + 1)) + left; - const pivotNewIndex = partition(arr, left, right, pivotIndex); + // Partition the array + let pivotIndex = partition(array, left, right); - if (k === pivotNewIndex) { - return arr[k]; - } else if (k < pivotNewIndex) { - return quickSelect(arr, left, pivotNewIndex - 1, k); + // The pivot is in its final sorted position + if (k === pivotIndex) { + return array[k]; + } else if (k < pivotIndex) { + // If k is less than the pivot index, look left + return QuickSelect(array, k, left, pivotIndex - 1); } else { - return quickSelect(arr, pivotNewIndex + 1, right, k); + // If k is greater than the pivot index, look right + return QuickSelect(array, k, pivotIndex + 1, right); } -} - - +}; diff --git a/sorts/test/quick_select.test.ts b/sorts/test/quick_select.test.ts index 99313370..885ed5e1 100644 --- a/sorts/test/quick_select.test.ts +++ b/sorts/test/quick_select.test.ts @@ -1,18 +1,29 @@ -import { quickSelect } from "../quick_select"; +import { QuickSelect } from "../quick_select"; -describe("Quick Select", () => { - - it("should return the correct value for worst case", () => { - const arr = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; - const k = 2; // kth smallest element +describe('QuickSelect', () => { + test('should return the kth smallest element in an array', () => { + const array = [8, 3, 5, 1, 4, 2]; + expect(QuickSelect(array, 0)).toBe(1); + expect(QuickSelect(array, 1)).toBe(2); + expect(QuickSelect(array, 2)).toBe(3); + expect(QuickSelect(array, 3)).toBe(4); + expect(QuickSelect(array, 4)).toBe(5); + expect(QuickSelect(array, 5)).toBe(8); + }); - expect(quickSelect(arr, 0, arr.length - 1, k)).toBe(3); + test('should work with arrays of size 1', () => { + const array = [4]; + expect(QuickSelect(array, 0)).toBe(4); }); - it("should return the correct value for best case", () => { - const arr = [1, 4, 2, 9, 5, 7, 3, 8, 10, 6]; - const k = 5; // kth smallest element + test('should work with large arrays', () => { + const array = Array.from({length: 1000}, (_, i) => i + 1); + expect(QuickSelect(array, 499)).toBe(500); + }); - expect(quickSelect(arr, 0, arr.length - 1, k)).toBe(6); + test('should throw error when k is out of bounds', () => { + const array = [8, 3, 5, 1, 4, 2]; + expect(() => QuickSelect(array, -1)).toThrow(); + expect(() => QuickSelect(array, 6)).toThrow(); }); });