mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-11-10 11:09:43 +08:00
Add BubbleSort.
This commit is contained in:
parent
852093265e
commit
0224afbc42
@ -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
|
||||||
|
|
||||||
|
34
src/algorithms/sorting/Sort.js
Normal file
34
src/algorithms/sorting/Sort.js
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
37
src/algorithms/sorting/SortTester.js
Normal file
37
src/algorithms/sorting/SortTester.js
Normal 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']);
|
||||||
|
}
|
||||||
|
}
|
12
src/algorithms/sorting/__test__/Sort.test.js
Normal file
12
src/algorithms/sorting/__test__/Sort.test.js
Normal 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();
|
||||||
|
});
|
||||||
|
});
|
23
src/algorithms/sorting/bubble-sort/BubbleSort.js
Normal file
23
src/algorithms/sorting/bubble-sort/BubbleSort.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
8
src/algorithms/sorting/bubble-sort/README.md
Normal file
8
src/algorithms/sorting/bubble-sort/README.md
Normal 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.
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user