Add BubbleSort.

This commit is contained in:
Oleksii Trekhleb 2018-04-12 11:53:26 +03:00
parent 852093265e
commit 0224afbc42
7 changed files with 161 additions and 0 deletions

View File

@ -30,6 +30,8 @@
* Graph * Graph
* [Depth-First Search (DFS)](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/depth-first-search) * [Depth-First Search (DFS)](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/depth-first-search)
* [Breadth-First Search (BFS)](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/breadth-first-search) * [Breadth-First Search (BFS)](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/breadth-first-search)
* Sorting
* [Bubble Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/bubble-sort)
## Running Tests ## Running Tests

View File

@ -0,0 +1,34 @@
import Comparator from '../../utils/comparator/Comparator';
/**
* @typedef {Object} SorterCallbacks
* @property {function(a: *, b: *)} compareCallback - If provided then all elements comparisons
* will be done through this callback.
* @property {function(a: *)} visitingCallback - If provided it will be called each time the sorting
* function is visiting the next element.
*/
export default class Sort {
constructor(rawCallbacks) {
this.callbacks = Sort.initSortingCallbacks(rawCallbacks);
this.comparator = new Comparator(this.callbacks.compareCallback);
}
/**
* @param {SorterCallbacks} rawCallbacks
* @returns {SorterCallbacks}
*/
static initSortingCallbacks(rawCallbacks) {
const callbacks = rawCallbacks || {};
const stubCallback = () => {};
callbacks.compareCallback = callbacks.compareCallback || undefined;
callbacks.visitingCallback = callbacks.visitingCallback || stubCallback;
return callbacks;
}
sort() {
throw new Error('sort method must be implemented');
}
}

View File

@ -0,0 +1,37 @@
export const sortedArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
export const notSortedArray = [10, 5, 30, -1, 0, 0, 1, 2, -3, 2];
export const equalArray = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
export class SortTester {
static testSort(SortingClass) {
const sorter = new SortingClass();
expect(sorter.sort([])).toEqual([]);
expect(sorter.sort([1])).toEqual([1]);
expect(sorter.sort([1, 2])).toEqual([1, 2]);
expect(sorter.sort([2, 1])).toEqual([1, 2]);
expect(sorter.sort([1, 1, 1])).toEqual([1, 1, 1]);
expect(sorter.sort(sortedArray)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
expect(sorter.sort(notSortedArray)).toEqual([-3, -1, 0, 0, 1, 2, 2, 5, 10, 30]);
expect(sorter.sort(equalArray)).toEqual([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
}
static testSortWithCustomComparator(SortingClass) {
const callbacks = {
compareCallback: (a, b) => {
if (a.length === b.length) {
return 0;
}
return a.length < b.length ? -1 : 1;
},
};
const sorter = new SortingClass(callbacks);
expect(sorter.sort([''])).toEqual(['']);
expect(sorter.sort(['a'])).toEqual(['a']);
expect(sorter.sort(['aa', 'a'])).toEqual(['a', 'aa']);
expect(sorter.sort(['bb', 'aa', 'c'])).toEqual(['c', 'bb', 'aa']);
}
}

View File

@ -0,0 +1,12 @@
import Sort from '../Sort';
describe('Sort', () => {
it('should throw an error when trying to call Sort.sort() method directly', () => {
function doForbiddenSort() {
const sorter = new Sort();
sorter.sort();
}
expect(doForbiddenSort).toThrow();
});
});

View File

@ -0,0 +1,23 @@
import Sort from '../Sort';
export default class BubbleSort extends Sort {
sort(initialArray) {
const array = initialArray;
for (let i = 0; i < array.length; i += 1) {
for (let j = 0; j < array.length - 1; j += 1) {
// Call visiting callback.
this.callbacks.visitingCallback(array[j]);
// Swap elements if they are in wrong order.
if (this.comparator.lessThen(array[j + 1], array[j])) {
const tmp = array[j + 1];
array[j + 1] = array[j];
array[j] = tmp;
}
}
}
return array;
}
}

View File

@ -0,0 +1,8 @@
# Bubble Sort
Bubble sort, sometimes referred to as sinking sort, is a
simple sorting algorithm that repeatedly steps through
the list to be sorted, compares each pair of adjacent
items and swaps them if they are in the wrong order.
The pass through the list is repeated until no swaps
are needed, which indicates that the list is sorted.

View File

@ -0,0 +1,45 @@
import BubbleSort from '../BubbleSort';
import { equalArray, notSortedArray, sortedArray, SortTester } from '../../SortTester';
describe('bubbleSort', () => {
it('should sort array', () => {
SortTester.testSort(BubbleSort);
});
it('should sort array with custom comparator', () => {
SortTester.testSortWithCustomComparator(BubbleSort);
});
it('should visit sorted array element specified number of times', () => {
const visitingCallback = jest.fn();
const callbacks = { visitingCallback };
const sorter = new BubbleSort(callbacks);
sorter.sort(sortedArray);
expect(visitingCallback).toHaveBeenCalledTimes(90);
});
it('should visit not-sorted array element specified number of times', () => {
const visitingCallback = jest.fn();
const callbacks = { visitingCallback };
const sorter = new BubbleSort(callbacks);
sorter.sort(notSortedArray);
expect(visitingCallback).toHaveBeenCalledTimes(90);
});
it('should visit equal array element specified number of times', () => {
const visitingCallback = jest.fn();
const callbacks = { visitingCallback };
const sorter = new BubbleSort(callbacks);
sorter.sort(equalArray);
expect(visitingCallback).toHaveBeenCalledTimes(90);
});
});