Skip to content

Commit e6c7279

Browse files
committed
[Sorting and Searching Algorithms]
1 parent 56a4153 commit e6c7279

31 files changed

+497
-5
lines changed

examples/PacktDataStructuresAlgorithms.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
"lint:js": "eslint src/js && eslint test/js",
2222
"lint:ts": "tslint -c tslint.json 'src/ts/**/*.ts' && tslint -c tslint.json 'test/ts/**/*.ts'",
2323
"lint": "npm run lint:js && npm run lint:ts",
24-
"test:js": "mocha --compilers js:babel-core/register ./test/js/**/**/*.spec.js --reporter mochawesome",
25-
"test:ts": "mocha -r ts-node/register --recursive ./test/ts/**/**/*.spec.ts",
24+
"test:js": "mocha --compilers js:babel-core/register ./test/js --recursive --reporter mochawesome",
25+
"test:ts": "mocha -r ts-node/register ./test/ts/**/*.spec.ts ./test/ts/**/**/*.spec.ts --recursive",
2626
"test": "npm run test:js && npm run test:ts",
2727
"dev": "npm run clean && npm run lint && npm run webpack && npm run generate-report",
2828
"dev2": "npm run clean && npm run lint && npm run webpack && npm run generate-report2",
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Compare, defaultCompare, DOES_NOT_EXIST } from '../../util';
2+
import { quickSort } from '../sorting/quicksort';
3+
4+
export function binarySearch(array, value, compareFn = defaultCompare) {
5+
const sortedArray = quickSort(array);
6+
let low = 0;
7+
let high = sortedArray.length - 1;
8+
while (low <= high) {
9+
const mid = Math.floor((low + high) / 2);
10+
const element = sortedArray[mid];
11+
// console.log('mid element is ' + element);
12+
if (compareFn(element, value) === Compare.LESS_THAN) {
13+
low = mid + 1;
14+
// console.log('low is ' + low);
15+
} else if (compareFn(element, value) === Compare.BIGGER_THAN) {
16+
high = mid - 1;
17+
// console.log('high is ' + high);
18+
} else {
19+
// console.log('found it');
20+
return mid;
21+
}
22+
}
23+
return DOES_NOT_EXIST;
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {
2+
biggerEquals,
3+
Compare,
4+
defaultCompare,
5+
defaultEquals,
6+
defaultDiff,
7+
DOES_NOT_EXIST,
8+
lesserEquals
9+
} from '../../util';
10+
11+
export function interpolationSearch(
12+
array,
13+
value,
14+
compareFn = defaultCompare,
15+
equalsFn = defaultEquals,
16+
diffFn = defaultDiff
17+
) {
18+
const { length } = array;
19+
let low = 0;
20+
let high = length - 1;
21+
let position = -1;
22+
let delta = -1;
23+
while (
24+
low <= high &&
25+
biggerEquals(value, array[low], compareFn) &&
26+
lesserEquals(value, array[high], compareFn)
27+
) {
28+
delta = diffFn(value, array[low]) / diffFn(array[high], array[low]);
29+
position = low + Math.floor((high - low) * delta);
30+
if (equalsFn(array[position], value)) {
31+
return position;
32+
}
33+
if (compareFn(array[position], value) === Compare.LESS_THAN) {
34+
low = position + 1;
35+
} else {
36+
high = position - 1;
37+
}
38+
}
39+
return DOES_NOT_EXIST;
40+
}
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { defaultCompare, Compare } from '../../util';
2+
3+
export function findMaxValue(array, compareFn = defaultCompare) {
4+
if (array && array.length > 0) {
5+
let max = array[0];
6+
for (let i = 1; i < array.length; i++) {
7+
if (compareFn(max, array[i]) === Compare.LESS_THAN) {
8+
max = array[i];
9+
}
10+
}
11+
return max;
12+
}
13+
return undefined;
14+
}
15+
export function findMinValue(array, compareFn = defaultCompare) {
16+
if (array && array.length > 0) {
17+
let min = array[0];
18+
for (let i = 1; i < array.length; i++) {
19+
if (compareFn(min, array[i]) === Compare.BIGGER_THAN) {
20+
min = array[i];
21+
}
22+
}
23+
return min;
24+
}
25+
return undefined;
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { defaultEquals, DOES_NOT_EXIST } from '../../util';
2+
3+
export function sequentialSearch(array, value, equalsFn = defaultEquals) {
4+
for (let i = 0; i < array.length; i++) {
5+
if (equalsFn(value, array[i])) {
6+
return i;
7+
}
8+
}
9+
return DOES_NOT_EXIST;
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { swap } from '../../util';
2+
3+
export function shuffle(array) {
4+
let currentIndex = array.length;
5+
while (currentIndex !== 0) {
6+
const randomIndex = Math.floor(Math.random() * currentIndex);
7+
currentIndex--;
8+
swap(array, currentIndex, randomIndex);
9+
}
10+
return array;
11+
}
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { insertionSort } from './insertion-sort';
2+
3+
function createBuckets(array, bucketSize) {
4+
let minValue = array[0];
5+
let maxValue = array[0];
6+
for (let i = 1; i < array.length; i++) {
7+
if (array[i] < minValue) {
8+
minValue = array[i];
9+
} else if (array[i] > maxValue) {
10+
maxValue = array[i];
11+
}
12+
}
13+
const bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
14+
const buckets = [];
15+
for (let i = 0; i < bucketCount; i++) {
16+
buckets[i] = [];
17+
}
18+
for (let i = 0; i < array.length; i++) {
19+
buckets[Math.floor((array[i] - minValue) / bucketSize)].push(array[i]);
20+
}
21+
return buckets;
22+
}
23+
function sortBuckets(buckets) {
24+
const sortedArray = [];
25+
for (let i = 0; i < buckets.length; i++) {
26+
if (buckets[i] != null) {
27+
insertionSort(buckets[i]);
28+
for (let j = 0; j < buckets[i].length; j++) {
29+
sortedArray.push(buckets[i][j]);
30+
}
31+
}
32+
}
33+
return sortedArray;
34+
}
35+
export function bucketSort(array, bucketSize = 5) {
36+
if (array.length < 2) {
37+
return array;
38+
}
39+
const buckets = createBuckets(array, bucketSize);
40+
return sortBuckets(buckets);
41+
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { findMaxValue } from '../search/min-max-search';
2+
3+
export function countingSort(array) {
4+
if (array.length < 2) {
5+
return array;
6+
}
7+
const maxValue = findMaxValue(array);
8+
let sortedIndex = 0;
9+
const counts = new Array(maxValue + 1);
10+
array.forEach(element => {
11+
if (!counts[element]) {
12+
counts[element] = 0;
13+
}
14+
counts[element]++;
15+
});
16+
// console.log('Frequencies: ' + counts.join());
17+
counts.forEach((element, i) => {
18+
while (element > 0) {
19+
array[sortedIndex++] = i;
20+
element--;
21+
}
22+
});
23+
return array;
24+
}
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { findMaxValue, findMinValue } from '../search/min-max-search';
2+
3+
const countingSortForRadix = (array, radixBase, significantDigit, minValue) => {
4+
let bucketsIndex;
5+
const buckets = [];
6+
const aux = [];
7+
for (let i = 0; i < radixBase; i++) {
8+
buckets[i] = 0;
9+
}
10+
for (let i = 0; i < array.length; i++) {
11+
bucketsIndex = Math.floor(((array[i] - minValue) / significantDigit) % radixBase);
12+
buckets[bucketsIndex]++;
13+
}
14+
for (let i = 1; i < radixBase; i++) {
15+
buckets[i] += buckets[i - 1];
16+
}
17+
for (let i = array.length - 1; i >= 0; i--) {
18+
bucketsIndex = Math.floor(((array[i] - minValue) / significantDigit) % radixBase);
19+
aux[--buckets[bucketsIndex]] = array[i];
20+
}
21+
for (let i = 0; i < array.length; i++) {
22+
array[i] = aux[i];
23+
}
24+
return array;
25+
};
26+
export function radixSort(array, radixBase = 10) {
27+
if (array.length < 2) {
28+
return array;
29+
}
30+
const minValue = findMinValue(array);
31+
const maxValue = findMaxValue(array);
32+
// Perform counting sort for each significant digit, starting at 1
33+
let significantDigit = 1;
34+
while ((maxValue - minValue) / significantDigit >= 1) {
35+
// console.log('radix sort for digit ' + significantDigit);
36+
array = countingSortForRadix(array, radixBase, significantDigit, minValue);
37+
// console.log(array.join());
38+
significantDigit *= radixBase;
39+
}
40+
return array;
41+
}

src/js/index.js

+20
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,23 @@ export { dijkstra } from './algorithms/graph/dijkstra';
6161
export { floydWarshall } from './algorithms/graph/floyd-warshall';
6262
export { prim } from './algorithms/graph/prim';
6363
export { kruskal } from './algorithms/graph/kruskal';
64+
65+
// chapter 12
66+
export { shuffle } from './algorithms/shuffle/fisher–yates';
67+
68+
export { bubbleSort } from './algorithms/sorting/bubble-sort';
69+
export { modifiedBubbleSort } from './algorithms/sorting/bubble-sort-improved';
70+
export { bucketSort } from './algorithms/sorting/bucket-sort';
71+
export { countingSort } from './algorithms/sorting/counting-sort';
72+
export { insertionSort } from './algorithms/sorting/insertion-sort';
73+
export { mergeSort } from './algorithms/sorting/merge-sort';
74+
export { quickSort } from './algorithms/sorting/quicksort';
75+
export { radixSort } from './algorithms/sorting/radix-sort';
76+
export { selectionSort } from './algorithms/sorting/selection-sort';
77+
export { shellSort } from './algorithms/sorting/shell-sort';
78+
79+
export { binarySearch } from './algorithms/search/binary-search';
80+
export { interpolationSearch } from './algorithms/search/interpolation-search';
81+
export { sequentialSearch } from './algorithms/search/sequential-search';
82+
export { findMaxValue } from './algorithms/search/min-max-search';
83+
export { findMinValue } from './algorithms/search/min-max-search';

src/js/util.js

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
export const Compare = {
22
LESS_THAN: -1,
3-
BIGGER_THAN: 1
3+
BIGGER_THAN: 1,
4+
EQUALS: 0
45
};
56

7+
export const DOES_NOT_EXIST = -1;
8+
9+
export function lesserEquals(a, b, compareFn) {
10+
const comp = compareFn(a, b);
11+
return comp === Compare.LESS_THAN || comp === Compare.EQUALS;
12+
}
13+
14+
export function biggerEquals(a, b, compareFn) {
15+
const comp = compareFn(a, b);
16+
return comp === Compare.BIGGER_THAN || comp === Compare.EQUALS;
17+
}
18+
619
export function defaultCompare(a, b) {
720
if (a === b) {
821
return 0;
@@ -34,3 +47,7 @@ export function swap(array, a, b) {
3447
export function reverseCompare(compareFn) {
3548
return (a, b) => compareFn(b, a);
3649
}
50+
51+
export function defaultDiff(a, b) {
52+
return Number(a) - Number(b);
53+
}

src/ts/algorithms/sorting/counting-sort.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { findMaxValue } from '../..';
1+
import { findMaxValue } from '../search/min-max-search';
22

33
export function countingSort(array: number[]) {
44

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { binarySearch } from '../../../../src/js/index';
2+
import { testSearchAlgorithm } from './search-algorithms-tests';
3+
4+
testSearchAlgorithm(binarySearch, 'Binary Search');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { interpolationSearch } from '../../../../src/js/index';
2+
import { testSearchAlgorithm } from './search-algorithms-tests';
3+
4+
testSearchAlgorithm(interpolationSearch, 'Interpolation Search', { customEquals: false });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import 'mocha';
2+
import { expect } from 'chai';
3+
import { findMinValue, findMaxValue } from '../../../../src/js/index';
4+
5+
describe('Min and Max Values Search', () => {
6+
const SIZE = 10;
7+
8+
function createSortedArray() {
9+
const array = [];
10+
for (let i = 1; i <= SIZE; i++) {
11+
array.push(i);
12+
}
13+
return array;
14+
}
15+
16+
it('min value - works with empty arrays', () => {
17+
expect(findMinValue([])).to.equal(undefined);
18+
});
19+
20+
it('max value - works with empty arrays', () => {
21+
expect(findMaxValue([])).to.equal(undefined);
22+
});
23+
24+
it('min value', () => {
25+
expect(findMinValue(createSortedArray())).to.equal(1);
26+
});
27+
28+
it('max value', () => {
29+
expect(findMaxValue(createSortedArray())).to.equal(SIZE);
30+
});
31+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import 'mocha';
2+
import { expect } from 'chai';
3+
4+
const customEquals = (a, b) => a.key === b.key;
5+
6+
export function testSearchAlgorithm(
7+
searchAlgorithm,
8+
algorithmName,
9+
config = { customEquals: true }
10+
) {
11+
describe(algorithmName, () => {
12+
const SIZE = 10;
13+
14+
function createSortedArray() {
15+
const array = [];
16+
for (let i = 1; i <= SIZE; i++) {
17+
array.push(i);
18+
}
19+
return array;
20+
}
21+
22+
it('works with empty arrays', () => {
23+
expect(searchAlgorithm([], 1)).to.equal(-1);
24+
});
25+
26+
it('finds value at the first position', () => {
27+
const array = createSortedArray();
28+
expect(searchAlgorithm(array, 1)).to.equal(0);
29+
});
30+
31+
it('finds value at the last position', () => {
32+
const array = createSortedArray();
33+
expect(searchAlgorithm(array, SIZE)).to.equal(SIZE - 1);
34+
});
35+
36+
if (config.customEquals) {
37+
it('finds value with custom equals function', () => {
38+
const array = [{ key: 1 }, { key: 2 }, { key: 3 }];
39+
expect(searchAlgorithm(array, { key: 2 }, customEquals)).to.equal(1);
40+
});
41+
}
42+
});
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { sequentialSearch } from '../../../../src/js/index';
2+
import { testSearchAlgorithm } from './search-algorithms-tests';
3+
4+
testSearchAlgorithm(sequentialSearch, 'Sequential Search');

0 commit comments

Comments
 (0)