From bf5d7b338ccefd4d1449a2490d27af179b40339b Mon Sep 17 00:00:00 2001 From: Robert Taussig Date: Sun, 27 May 2018 16:11:13 -0400 Subject: [PATCH] Add in-place sort to QuickSort.js (#16) * Add in-place sort to QuickSort.js * Fix linting errors and clean up comments * Change implementation to address lint errors * Trailing space and undefined variable * Create own class for in-place quicksort and use tests * Add trailing space at end of file * Fix placement of visitedCallback, explain itial destructuring --- .../sorting/quick-sort/QuickSort.js | 2 +- .../sorting/quick-sort/QuickSortInPlace.js | 59 +++++++++++++++++++ .../__test__/QuickSortInPlace.test.js | 56 ++++++++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 src/algorithms/sorting/quick-sort/QuickSortInPlace.js create mode 100644 src/algorithms/sorting/quick-sort/__test__/QuickSortInPlace.test.js diff --git a/src/algorithms/sorting/quick-sort/QuickSort.js b/src/algorithms/sorting/quick-sort/QuickSort.js index e1eb8558..8a9c113f 100644 --- a/src/algorithms/sorting/quick-sort/QuickSort.js +++ b/src/algorithms/sorting/quick-sort/QuickSort.js @@ -5,7 +5,7 @@ export default class QuickSort extends Sort { // Clone original array to prevent it from modification. const array = [...originalArray]; - // If array has less then or equal to one elements then it is already sorted. + // If array has less than or equal to one elements then it is already sorted. if (array.length <= 1) { return array; } diff --git a/src/algorithms/sorting/quick-sort/QuickSortInPlace.js b/src/algorithms/sorting/quick-sort/QuickSortInPlace.js new file mode 100644 index 00000000..fc7bb67d --- /dev/null +++ b/src/algorithms/sorting/quick-sort/QuickSortInPlace.js @@ -0,0 +1,59 @@ +import Sort from '../Sort'; + +export default class QuickSortInPlace extends Sort { + /* Sorting in place avoids unnecessary use of additional memory, but modifies input array. + * + * This process is difficult to describe, but much clearer with a visualization: + * http://www.algomation.com/algorithm/quick-sort-visualization + */ + sort(originalArray, inputLow, inputHigh) { + // Destructures array on initial passthrough, and then sorts in place. + const array = inputLow === undefined ? [...originalArray] : originalArray; + // Partition array segment and return index of last swap + const partition = (l, h) => { + const swap = (left, right) => { + const tempVariable = array[left]; + array[left] = array[right]; + array[right] = tempVariable; + }; + + const pivot = array[h]; + this.callbacks.visitingCallback(array[pivot]); + let firstRunner = l - 1; + + for (let secondRunner = l; secondRunner < h; secondRunner += 1) { + if (this.comparator.lessThan(array[secondRunner], pivot)) { + firstRunner += 1; + swap(firstRunner, secondRunner); + } + } + + if (this.comparator.lessThan(pivot, array[firstRunner + 1])) { + swap(firstRunner + 1, h); + } + + return firstRunner + 1; + }; + + /* + * While we can use a default parameter to set `low` to 0, we would + * still have to set `high`'s default within the function as we + * don't have access to `array.length - 1` when declaring paramaters + */ + const low = inputLow === undefined ? 0 : inputLow; + const high = inputHigh === undefined ? array.length - 1 : inputHigh; + + // Base case is when low and high converge + if (low < high) { + const partitionIndex = partition(low, high); + /* + * `partition()` swaps elements of the array based on their comparison to the `hi` parameter, + * and then returns the index where swapping is no longer necessary, which can be best thought + * of as the pivot used to split an array in a non-in-place quicksort + */ + this.sort(array, low, partitionIndex - 1); + this.sort(array, partitionIndex + 1, high); + } + return array; + } +} diff --git a/src/algorithms/sorting/quick-sort/__test__/QuickSortInPlace.test.js b/src/algorithms/sorting/quick-sort/__test__/QuickSortInPlace.test.js new file mode 100644 index 00000000..6e78ef29 --- /dev/null +++ b/src/algorithms/sorting/quick-sort/__test__/QuickSortInPlace.test.js @@ -0,0 +1,56 @@ +import QuickSortInPlace from '../QuickSortInPlace'; +import { + equalArr, + notSortedArr, + reverseArr, + sortedArr, + SortTester, +} from '../../SortTester'; + +// Complexity constants. +const SORTED_ARRAY_VISITING_COUNT = 19; +const NOT_SORTED_ARRAY_VISITING_COUNT = 12; +const REVERSE_SORTED_ARRAY_VISITING_COUNT = 19; +const EQUAL_ARRAY_VISITING_COUNT = 19; + +describe('QuickSortInPlace', () => { + it('should sort array', () => { + SortTester.testSort(QuickSortInPlace); + }); + + it('should sort array with custom comparator', () => { + SortTester.testSortWithCustomComparator(QuickSortInPlace); + }); + + it('should visit EQUAL array element specified number of times', () => { + SortTester.testAlgorithmTimeComplexity( + QuickSortInPlace, + equalArr, + EQUAL_ARRAY_VISITING_COUNT, + ); + }); + + it('should visit SORTED array element specified number of times', () => { + SortTester.testAlgorithmTimeComplexity( + QuickSortInPlace, + sortedArr, + SORTED_ARRAY_VISITING_COUNT, + ); + }); + + it('should visit NOT SORTED array element specified number of times', () => { + SortTester.testAlgorithmTimeComplexity( + QuickSortInPlace, + notSortedArr, + NOT_SORTED_ARRAY_VISITING_COUNT, + ); + }); + + it('should visit REVERSE SORTED array element specified number of times', () => { + SortTester.testAlgorithmTimeComplexity( + QuickSortInPlace, + reverseArr, + REVERSE_SORTED_ARRAY_VISITING_COUNT, + ); + }); +});