diff --git a/README.md b/README.md index a1c5ac59..84f2bcf8 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ * Sorting * [Bubble Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/bubble-sort) * [Selection Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/selection-sort) + * [Insertion Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/insertion-sort) ## Running Tests @@ -98,4 +99,5 @@ Source: [Big O Cheat Sheet](http://bigocheatsheet.com/). | Name | Best | Average | Worst | Memory | Stable | Method | Notes | | --------------------- | :-----: | :-------: | :-----: | :-------: | :-------: | :------------ | :-------------- | | **Bubble sort** | n | n^2 | n^2 | 1 | Yes | Exchanging | Tiny code size | +| **Insertion sort** | n | n^2 | n^2 | 1 | Yes | Insertion | O(n + d), in the worst case over sequences that have d inversions | | **Selection sort** | n^2 | n^2 | n^2 | 1 | No | Selection | Stable with O(n) extra space, for example using lists | diff --git a/src/algorithms/sorting/bubble-sort/BubbleSort.js b/src/algorithms/sorting/bubble-sort/BubbleSort.js index faac4487..2da40d56 100644 --- a/src/algorithms/sorting/bubble-sort/BubbleSort.js +++ b/src/algorithms/sorting/bubble-sort/BubbleSort.js @@ -10,6 +10,9 @@ export default class BubbleSort extends Sort { for (let i = 0; i < array.length; i += 1) { swapped = false; + // Call visiting callback. + this.callbacks.visitingCallback(array[i]); + for (let j = 0; j < array.length - 1; j += 1) { // Call visiting callback. this.callbacks.visitingCallback(array[j]); diff --git a/src/algorithms/sorting/bubble-sort/__test__/BubbleSort.test.js b/src/algorithms/sorting/bubble-sort/__test__/BubbleSort.test.js index cb69f4ac..40a7a43f 100644 --- a/src/algorithms/sorting/bubble-sort/__test__/BubbleSort.test.js +++ b/src/algorithms/sorting/bubble-sort/__test__/BubbleSort.test.js @@ -21,7 +21,7 @@ describe('BubbleSort', () => { }); it('should visit EQUAL array element specified number of times', () => { - const expectedNumberOfVisits = 19; + const expectedNumberOfVisits = 20; SortTester.testAlgorithmTimeComplexity( BubbleSort, @@ -31,7 +31,7 @@ describe('BubbleSort', () => { }); it('should visit SORTED array element specified number of times', () => { - const expectedNumberOfVisits = 19; + const expectedNumberOfVisits = 20; SortTester.testAlgorithmTimeComplexity( BubbleSort, @@ -41,7 +41,7 @@ describe('BubbleSort', () => { }); it('should visit NOT SORTED array element specified number of times', () => { - const expectedNumberOfVisits = 266; + const expectedNumberOfVisits = 280; SortTester.testAlgorithmTimeComplexity( BubbleSort, @@ -51,7 +51,7 @@ describe('BubbleSort', () => { }); it('should visit REVERSE SORTED array element specified number of times', () => { - const expectedNumberOfVisits = 380; + const expectedNumberOfVisits = 400; SortTester.testAlgorithmTimeComplexity( BubbleSort, diff --git a/src/algorithms/sorting/insertion-sort/InsertionSort.js b/src/algorithms/sorting/insertion-sort/InsertionSort.js new file mode 100644 index 00000000..7b74acd7 --- /dev/null +++ b/src/algorithms/sorting/insertion-sort/InsertionSort.js @@ -0,0 +1,35 @@ +import Sort from '../Sort'; + +export default class InsertionSort extends Sort { + sort(originalArray) { + const array = originalArray.slice(0); + + // Go through all array elements... + for (let i = 0; i < array.length; i += 1) { + let currentIndex = i; + + // Call visiting callback. + this.callbacks.visitingCallback(array[i]); + + // Go and check if previous elements and greater then current one. + // If this is the case then swap that elements. + while ( + array[currentIndex - 1] && + this.comparator.lessThen(array[currentIndex], array[currentIndex - 1]) + ) { + // Call visiting callback. + this.callbacks.visitingCallback(array[currentIndex - 1]); + + // Swap the elements. + const tmp = array[currentIndex - 1]; + array[currentIndex - 1] = array[currentIndex]; + array[currentIndex] = tmp; + + // Shift current index left. + currentIndex -= 1; + } + } + + return array; + } +} diff --git a/src/algorithms/sorting/insertion-sort/README.md b/src/algorithms/sorting/insertion-sort/README.md new file mode 100644 index 00000000..ed3a753f --- /dev/null +++ b/src/algorithms/sorting/insertion-sort/README.md @@ -0,0 +1,12 @@ +# Insertion Sort + +Insertion sort is a simple sorting algorithm that builds +the final sorted array (or list) one item at a time. +It is much less efficient on large lists than more +advanced algorithms such as quicksort, heapsort, or merge +sort. + +![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/4/42/Insertion_sort.gif) + +![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/0/0f/Insertion-sort-example-300px.gif) + diff --git a/src/algorithms/sorting/insertion-sort/__test__/InsertionSort.test.js b/src/algorithms/sorting/insertion-sort/__test__/InsertionSort.test.js new file mode 100644 index 00000000..ba4fc9f2 --- /dev/null +++ b/src/algorithms/sorting/insertion-sort/__test__/InsertionSort.test.js @@ -0,0 +1,62 @@ +import InsertionSort from '../InsertionSort'; +import { + equalArr, + notSortedArr, + reverseArr, + sortedArr, + SortTester, +} from '../../SortTester'; + +describe('InsertionSort', () => { + it('should sort array', () => { + SortTester.testSort(InsertionSort); + }); + + it('should sort array with custom comparator', () => { + SortTester.testSortWithCustomComparator(InsertionSort); + }); + + it('should do stable sorting', () => { + SortTester.testSortStability(InsertionSort); + }); + + it('should visit EQUAL array element specified number of times', () => { + const expectedNumberOfVisits = 20; + + SortTester.testAlgorithmTimeComplexity( + InsertionSort, + equalArr, + expectedNumberOfVisits, + ); + }); + + it('should visit SORTED array element specified number of times', () => { + const expectedNumberOfVisits = 20; + + SortTester.testAlgorithmTimeComplexity( + InsertionSort, + sortedArr, + expectedNumberOfVisits, + ); + }); + + it('should visit NOT SORTED array element specified number of times', () => { + const expectedNumberOfVisits = 101; + + SortTester.testAlgorithmTimeComplexity( + InsertionSort, + notSortedArr, + expectedNumberOfVisits, + ); + }); + + it('should visit REVERSE SORTED array element specified number of times', () => { + const expectedNumberOfVisits = 210; + + SortTester.testAlgorithmTimeComplexity( + InsertionSort, + reverseArr, + expectedNumberOfVisits, + ); + }); +}); diff --git a/src/algorithms/sorting/selection-sort/SelectionSort.js b/src/algorithms/sorting/selection-sort/SelectionSort.js index 6a266548..e26347ec 100644 --- a/src/algorithms/sorting/selection-sort/SelectionSort.js +++ b/src/algorithms/sorting/selection-sort/SelectionSort.js @@ -8,6 +8,9 @@ export default class SelectionSort extends Sort { for (let i = 0; i < array.length - 1; i += 1) { let minIndex = i; + // Call visiting callback. + this.callbacks.visitingCallback(array[i]); + // Find minimum element in the rest of array. for (let j = i + 1; j < array.length; j += 1) { // Call visiting callback. diff --git a/src/algorithms/sorting/selection-sort/__test__/SelectionSort.test.js b/src/algorithms/sorting/selection-sort/__test__/SelectionSort.test.js index 23ff2337..856dd66d 100644 --- a/src/algorithms/sorting/selection-sort/__test__/SelectionSort.test.js +++ b/src/algorithms/sorting/selection-sort/__test__/SelectionSort.test.js @@ -17,7 +17,7 @@ describe('SelectionSort', () => { }); it('should visit EQUAL array element specified number of times', () => { - const expectedNumberOfVisits = 190; + const expectedNumberOfVisits = 209; SortTester.testAlgorithmTimeComplexity( SelectionSort, @@ -27,7 +27,7 @@ describe('SelectionSort', () => { }); it('should visit SORTED array element specified number of times', () => { - const expectedNumberOfVisits = 190; + const expectedNumberOfVisits = 209; SortTester.testAlgorithmTimeComplexity( SelectionSort, @@ -37,7 +37,7 @@ describe('SelectionSort', () => { }); it('should visit NOT SORTED array element specified number of times', () => { - const expectedNumberOfVisits = 190; + const expectedNumberOfVisits = 209; SortTester.testAlgorithmTimeComplexity( SelectionSort, @@ -47,7 +47,7 @@ describe('SelectionSort', () => { }); it('should visit REVERSE SORTED array element specified number of times', () => { - const expectedNumberOfVisits = 190; + const expectedNumberOfVisits = 209; SortTester.testAlgorithmTimeComplexity( SelectionSort,