diff --git a/src/algorithms/sorting/SortTester.js b/src/algorithms/sorting/SortTester.js index b8dc1eee..7853ac69 100644 --- a/src/algorithms/sorting/SortTester.js +++ b/src/algorithms/sorting/SortTester.js @@ -11,7 +11,6 @@ export class SortTester { 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(sortedArr)).toEqual(sortedArr); expect(sorter.sort(reverseArr)).toEqual(sortedArr); expect(sorter.sort(notSortedArr)).toEqual(sortedArr); @@ -33,6 +32,21 @@ export class SortTester { expect(sorter.sort([''])).toEqual(['']); expect(sorter.sort(['a'])).toEqual(['a']); expect(sorter.sort(['aa', 'a'])).toEqual(['a', 'aa']); + expect(sorter.sort(['aa', 'aa'])).toEqual(['aa', 'aa']); + } + + static testSortStability(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(['bb', 'aa', 'c'])).toEqual(['c', 'bb', 'aa']); } diff --git a/src/algorithms/sorting/bubble-sort/BubbleSort.js b/src/algorithms/sorting/bubble-sort/BubbleSort.js index dd7c026b..faac4487 100644 --- a/src/algorithms/sorting/bubble-sort/BubbleSort.js +++ b/src/algorithms/sorting/bubble-sort/BubbleSort.js @@ -1,11 +1,11 @@ import Sort from '../Sort'; export default class BubbleSort extends Sort { - sort(initialArray) { + sort(originalArray) { // Flag that holds info about whether the swap has occur or not. let swapped = false; // Clone original array to prevent its modification. - const array = initialArray.slice(0); + const array = originalArray.slice(0); for (let i = 0; i < array.length; i += 1) { swapped = false; diff --git a/src/algorithms/sorting/bubble-sort/__test__/BubbleSort.test.js b/src/algorithms/sorting/bubble-sort/__test__/BubbleSort.test.js index d2049f4f..cb69f4ac 100644 --- a/src/algorithms/sorting/bubble-sort/__test__/BubbleSort.test.js +++ b/src/algorithms/sorting/bubble-sort/__test__/BubbleSort.test.js @@ -16,6 +16,10 @@ describe('BubbleSort', () => { SortTester.testSortWithCustomComparator(BubbleSort); }); + it('should do stable sorting', () => { + SortTester.testSortStability(BubbleSort); + }); + it('should visit EQUAL array element specified number of times', () => { const expectedNumberOfVisits = 19; diff --git a/src/algorithms/sorting/selection-sort/README.md b/src/algorithms/sorting/selection-sort/README.md new file mode 100644 index 00000000..f3232c23 --- /dev/null +++ b/src/algorithms/sorting/selection-sort/README.md @@ -0,0 +1,12 @@ +# Selection Sort + +Selection sort is a sorting algorithm, specifically an +in-place comparison sort. It has O(n2) time complexity, +making it inefficient on large lists, and generally +performs worse than the similar insertion sort. +Selection sort is noted for its simplicity, and it has +performance advantages over more complicated algorithms +in certain situations, particularly where auxiliary +memory is limited. + +![](https://en.wikipedia.org/wiki/Selection_sort#/media/File:Selection-Sort-Animation.gif) diff --git a/src/algorithms/sorting/selection-sort/SelectionSort.js b/src/algorithms/sorting/selection-sort/SelectionSort.js new file mode 100644 index 00000000..6a266548 --- /dev/null +++ b/src/algorithms/sorting/selection-sort/SelectionSort.js @@ -0,0 +1,31 @@ +import Sort from '../Sort'; + +export default class SelectionSort extends Sort { + sort(originalArray) { + // Clone original array to prevent its modification. + const array = originalArray.slice(0); + + for (let i = 0; i < array.length - 1; i += 1) { + let minIndex = i; + + // Find minimum element in the rest of array. + for (let j = i + 1; j < array.length; j += 1) { + // Call visiting callback. + this.callbacks.visitingCallback(array[j]); + + if (this.comparator.lessThen(array[j], array[minIndex])) { + minIndex = j; + } + } + + // If new minimum element has been found then swap it with current i-th element. + if (minIndex !== i) { + const tmp = array[i]; + array[i] = array[minIndex]; + array[minIndex] = tmp; + } + } + + return array; + } +} diff --git a/src/algorithms/sorting/selection-sort/__test__/SelectionSort.test.js b/src/algorithms/sorting/selection-sort/__test__/SelectionSort.test.js new file mode 100644 index 00000000..23ff2337 --- /dev/null +++ b/src/algorithms/sorting/selection-sort/__test__/SelectionSort.test.js @@ -0,0 +1,58 @@ +import SelectionSort from '../SelectionSort'; +import { + equalArr, + notSortedArr, + reverseArr, + sortedArr, + SortTester, +} from '../../SortTester'; + +describe('SelectionSort', () => { + it('should sort array', () => { + SortTester.testSort(SelectionSort); + }); + + it('should sort array with custom comparator', () => { + SortTester.testSortWithCustomComparator(SelectionSort); + }); + + it('should visit EQUAL array element specified number of times', () => { + const expectedNumberOfVisits = 190; + + SortTester.testAlgorithmTimeComplexity( + SelectionSort, + equalArr, + expectedNumberOfVisits, + ); + }); + + it('should visit SORTED array element specified number of times', () => { + const expectedNumberOfVisits = 190; + + SortTester.testAlgorithmTimeComplexity( + SelectionSort, + sortedArr, + expectedNumberOfVisits, + ); + }); + + it('should visit NOT SORTED array element specified number of times', () => { + const expectedNumberOfVisits = 190; + + SortTester.testAlgorithmTimeComplexity( + SelectionSort, + notSortedArr, + expectedNumberOfVisits, + ); + }); + + it('should visit REVERSE SORTED array element specified number of times', () => { + const expectedNumberOfVisits = 190; + + SortTester.testAlgorithmTimeComplexity( + SelectionSort, + reverseArr, + expectedNumberOfVisits, + ); + }); +});