Skip to content

Commit

Permalink
Implement QuickSort and tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
HamsterCoder committed Sep 29, 2023
1 parent 7ddbf24 commit ae8e4d3
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 20 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
## About

This repository contains implementations of some data structures and algorithms. It serves the purpose of reference. Probable not production ready.
This repository contains implementations of some data structures and algorithms. It serves the purpose of reference. Not for production use.

### What do you have
### What do you have?

* Shell Sort
* Knuth Shuffle
* Knuth Shuffle - returns a shuffled copy of the array
* Shell Sort - returns a sorted copy of the array
* Quick Sort - returns a sorted copy of the array

## Development

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Implementation of some data structures and algorithms",
"main": "index.js",
"scripts": {
"test": "jest"
"test": "jest --verbose"
},
"author": "",
"license": "MIT",
Expand Down
85 changes: 85 additions & 0 deletions src/quickSort.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { quickSort, partition } from "./quickSort";
import { shuffle } from "./shuffle";

function lessThan(a: number, b: number): boolean {
return a < b;
}

function debugSort(array: number[]) {
let result = quickSort(array, lessThan);

// console.log(array, result);

return result;
}

const TEST_ARRAY = [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5];

function isPartitioned<T>(array: T[], lessThan: (a: T, b: T) => boolean, lt: number, gt: number): boolean {
for (let i = 0; i < array.length; i += 1) {

if (i < lt) {
if (!lessThan(array[i], array[lt])) {
return false;
}
} else if (i > gt) {
if (!lessThan(array[gt], array[i])) {
return false;
}
} else {
if (
lessThan(array[i], array[lt]) ||
lessThan(array[lt], array[i]) ||
lessThan(array[i], array[gt]) ||
lessThan(array[gt], array[i])
) {
return false;
}
}
}

return true;
}

describe('quickSort', () => {
describe('partition', () => {
test('Correct boundaries, correctly partitioned 1', () => {
let TEST_PARTITION = [4, 3, 1, 5, 4, 7, 6, 4, 2];
let res = partition(TEST_PARTITION, lessThan, 0, TEST_PARTITION.length - 1);
expect(res).toEqual([3, 5]);
expect(isPartitioned(TEST_PARTITION, lessThan, ...res)).toEqual(true);
});

test('Correct boundaries, correctly partitioned 2', () => {
let TEST_PARTITION = [2, 3, 1, 5, 4, 7, 6, 4, 2, 2];
let res = partition(TEST_PARTITION, lessThan, 0, TEST_PARTITION.length - 1);
expect(res).toEqual([1, 3]);
expect(isPartitioned(TEST_PARTITION, lessThan, ...res)).toEqual(true);
});
});

test('Returns a copy', () => {
expect(debugSort(TEST_ARRAY)).not.toBe(TEST_ARRAY);
});

test('Sorts a sorted array', () => {
expect(debugSort(TEST_ARRAY)).toEqual(TEST_ARRAY);
});

test('Sorts a reversed array', () => {
expect(debugSort(TEST_ARRAY.slice().reverse())).toEqual(TEST_ARRAY);
});

test('Sorts a shuffled array: attempt 1', () => {
expect(debugSort(shuffle(TEST_ARRAY))).toEqual(TEST_ARRAY);
});

test('Sorts a shuffled array: attempt 2', () => {
expect(debugSort(shuffle(TEST_ARRAY))).toEqual(TEST_ARRAY);
});

test('Sorts a shuffled array: attempt 3', () => {
expect(debugSort(shuffle(TEST_ARRAY))).toEqual(TEST_ARRAY);
});
});

52 changes: 52 additions & 0 deletions src/quickSort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { shuffleInplace } from "./shuffle";

// 3-way partition implementation
export function partition<T>(array: T[], lessThan: (a: T, b: T) => boolean, i: number, j: number): [number, number] {
let value = array[i];
let lt = i;
let gt = j;
let index = i + 1;

while (index <= gt) {
if (lessThan(array[index], value)) {
let temp = array[lt];
array[lt] = array[index];
array[index] = temp;
index += 1;
lt += 1;
} else if (lessThan(value, array[index])) {
let temp = array[gt];
array[gt] = array[index];
array[index] = temp;
gt -= 1;
} else {
index += 1;
}
}

return [lt, gt];
}

function sort<T>(array: T[], lessThan: (a: T, b: T) => boolean, i: number, j: number): T[] {
if (i >= j) {
return array;
}

// 1. Shuffle
array = shuffleInplace(array, i, j);

// 2. Partition
let [lt, gt] = partition(array,lessThan, i, j);

// 3. Sort recursively
array = sort(array, lessThan, i, lt - 1);
array = sort(array, lessThan, gt + 1, j);

return array;
}

export const quickSort = function<T> (array: T[], lessThan: (a: T, b: T) => boolean): T[] {
let sortedArray = array.slice();

return sort(sortedArray, lessThan, 0, sortedArray.length - 1);
};
2 changes: 1 addition & 1 deletion src/shellSort.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { shuffle } from "./shuffle";

const TEST_ARRAY = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

function lessThan(a: number, b: number) {
function lessThan(a: number, b: number): boolean {
return a < b;
}

Expand Down
4 changes: 1 addition & 3 deletions src/shellSort.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ export const shellSort = function<T> (array: T[], lessThan: (a: T, b: T) => bool
h = 3 * h + 1;
}

while (h >= 1) {
console.log(`h: ${h}`);

while (h >= 1) {
for (let i = 0; i < n - h; i += 1) {
for (let j = i; j < n; j += h) {
let k = j;
Expand Down
26 changes: 15 additions & 11 deletions src/shuffle.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
// Implementation of Knuth Shuffle Algorithm
export function shuffle<T>(array: T[]) {
let currentIndex = array.length,
randomIndex;

let shuffledArray = array.slice();

return shuffleInplace(shuffledArray, 0, shuffledArray.length - 1);
}

// Implementation of Knuth Shuffle Algorithm
export function shuffleInplace<T>(array: T[], i: number, j: number) {
let currentIndex = j,
randomIndex;

// While there remain elements to shuffle.
while (currentIndex != 0) {
while (currentIndex !== i) {
// Pick a remaining element.
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
randomIndex = Math.floor(Math.random() * (currentIndex - i) + i);
currentIndex -= 1;

// And swap it with the current element.
[shuffledArray[currentIndex], shuffledArray[randomIndex]] = [
shuffledArray[randomIndex],
shuffledArray[currentIndex],
[array[currentIndex], array[randomIndex]] = [
array[randomIndex],
array[currentIndex],
];
}

return shuffledArray;
return array;
}

0 comments on commit ae8e4d3

Please sign in to comment.