Skip to content

algorithm : Added Quick Select algorithm #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 14 commits into from
Closed
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# dependencies
/node_modules
/temp

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

# intelliJ workspace folder
.idea
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm test
107 changes: 107 additions & 0 deletions Data-structures/QuickSelect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* [QuickSelect](https://www.geeksforgeeks.org/quickselect-algorithm/) is an algorithm to find the kth smallest number
*
* Notes:
* - QuickSelect is related to QuickSort, thus has optimal best and average case (O(n)) but unlikely poor worst case (O(n^2))
* - This implementation uses randomly selected pivots for better performance
* ----
* @complexity : O(n) (on average )
* @complexity : O(n^2) (worst case)
* ----
* @param items array
* @flow
*/

const QuickSelect = (items: Array<number>, kth: number): number => {
// eslint-disable-line no-unused-vars
if (kth < 1 || kth > items.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider making kth 0-based for consistency with JS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

help needed

throw new RangeError("Index Out of Bound");
}

return RandomizedSelect(items, 0, items.length - 1, kth);
};

/**
* @param items
* @param left
* @param right
* @param i
* @returns number
*/
const RandomizedSelect = (
items: Array<number>,
left: number,
right: number,
i: number
): number => {
if (left === right) return items[left];

const pivotIndex = RandomizedPartition(items, left, right);
const k = pivotIndex - left + 1;

if (i === k) return items[pivotIndex];
if (i < k) return RandomizedSelect(items, left, pivotIndex - 1, i);

return RandomizedSelect(items, pivotIndex + 1, right, i - k);
};
/**
*
* @param items
* @param left
* @param right
* @returns
*/
const RandomizedPartition = (
items: Array<number>,
left: number,
right: number
): number => {
const rand = getRandomInt(left, right);
Swap(items, rand, right);
return Partition(items, left, right);
};
/**
*
* @param items
* @param left
* @param right
* @returns
*/
const Partition = (items: Array<number>, left: number, right: number) : number => {
const x = items[right];
let pivotIndex = left - 1;

for (let j = left; j < right; j++) {
if (items[j] <= x) {
pivotIndex++;
Swap(items, pivotIndex, j);
}
}

Swap(items, pivotIndex + 1, right);

return pivotIndex + 1;
};

/**
*
* @param min
* @param max
* @returns
*/
const getRandomInt = (min : number, max : number) : number => {
return Math.floor(Math.random() * (max - min + 1)) + min;
}


/**
*
* @param arr array
* @param x array element to swap
* @param y array element to swap
*/
const Swap = (arr : Array<number>, x : number, y : number) : void => {
[arr[x], arr[y]] = [arr[y], arr[x]];
}

export { QuickSelect };
49 changes: 49 additions & 0 deletions Data-structures/test/QuickSelect.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { QuickSelect } from '../QuickSelect'

describe('QuickSelect tests', () => {
it('should return the only element of a list of length 1', () => {
// Test a mix of number types (i.e., positive/negative, numbers with decimals, fractions)
expect(QuickSelect([100], 1)).toEqual(100)
expect(QuickSelect([-23], 1)).toEqual(-23)
expect(QuickSelect([2007.102], 1)).toEqual(2007.102)
expect(QuickSelect([0.9], 1)).toEqual(0.9)
expect(QuickSelect([-0.075], 1)).toEqual(-0.075)
expect(QuickSelect([0], 1)).toEqual(0)
expect(QuickSelect([1], 1)).toEqual(1)
})

it('should throw an Error when k is greater than the length of the list', () => {
expect(() => QuickSelect([100, 2], 5)).toThrow('Index Out of Bound')
})

it('should throw an Error when k is less than 1', () => {
expect(() => QuickSelect([100, 2], 0)).toThrow('Index Out of Bound')
expect(() => QuickSelect([100, 2], -1)).toThrow('Index Out of Bound')
})

describe('varieties of list composition', () => {
it('should return the kth smallest element of a list that is in increasing order', () => {
expect(QuickSelect([10, 22, 33, 44, 55], 1)).toEqual(10)
expect(QuickSelect([10, 22, 33, 44, 55], 2)).toEqual(22)
expect(QuickSelect([10, 22, 33, 44, 55], 3)).toEqual(33)
expect(QuickSelect([10, 22, 33, 44, 55], 4)).toEqual(44)
expect(QuickSelect([10, 22, 33, 44, 55], 5)).toEqual(55)
})

it('should return the kth smallest element of an input list that is in decreasing order', () => {
expect(QuickSelect([82, 33.12, 4.0, 1], 1)).toEqual(1)
expect(QuickSelect([82, 33.12, 4.0, 1], 2)).toEqual(4.0)
expect(QuickSelect([82, 33.12, 4.0, 1], 2)).toEqual(4)
expect(QuickSelect([82, 33.12, 4.0, 1], 3)).toEqual(33.12)
expect(QuickSelect([82, 33.12, 4.0, 1], 4)).toEqual(82)
})

it('should return the kth smallest element of an input list that is no particular order', () => {
expect(QuickSelect([123, 14231, -10, 0, 15], 3)).toEqual(15)
expect(QuickSelect([0, 15, 123, 14231, -10], 3)).toEqual(15)
expect(QuickSelect([-10, 15, 123, 14231, 0], 3)).toEqual(15)
expect(QuickSelect([14231, 0, 15, 123, -10], 3)).toEqual(15)
expect(QuickSelect([14231, 0, 15, -10, 123], 3)).toEqual(15)
})
})
})
6 changes: 6 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }],
'@babel/preset-typescript'],

};
9 changes: 9 additions & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Config } from "@jest/types";
// Sync object
const config: Config.InitialOptions = {
verbose: true,
transform: {
"^.+\\.tsx?$": "ts-jest",
},
};
export default config;
Loading