mirror of
https://github.com/trekhleb/javascript-algorithms.git
synced 2025-07-15 00:21:17 +08:00
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
This commit is contained in:

committed by
Oleksii Trekhleb

parent
f93d12d5dd
commit
bf5d7b338c
@ -5,7 +5,7 @@ export default class QuickSort extends Sort {
|
|||||||
// Clone original array to prevent it from modification.
|
// Clone original array to prevent it from modification.
|
||||||
const array = [...originalArray];
|
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) {
|
if (array.length <= 1) {
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
59
src/algorithms/sorting/quick-sort/QuickSortInPlace.js
Normal file
59
src/algorithms/sorting/quick-sort/QuickSortInPlace.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
Reference in New Issue
Block a user