Add iterative quick sort

This commit is contained in:
yavorski 2018-09-27 21:58:56 +03:00
parent afa4948767
commit 152971f801
2 changed files with 170 additions and 0 deletions

View File

@ -0,0 +1,96 @@
import Sort from '../Sort';
import Stack from '../../../data-structures/stack/Stack';
export default class QuickSortIterative extends Sort {
/**
* Iterative Quick Sort
*
* @param {*[]} originalArray - Not sorted array.
* @param {number} inputLowIndex
* @param {number} inputHighIndex
* @return {*[]} - Sorted array.
*/
sort(
originalArray,
inputLowIndex = 0,
inputHighIndex = originalArray.length - 1,
sortInPlace = true,
) {
// Copies array on in case we don't want to sort in place
const array = sortInPlace ? originalArray : [...originalArray];
// If array has less than or equal to one elements then it is already sorted.
if (array.length <= 1) {
return array;
}
/**
* The partitionArray() operates on the subarray between lowIndex and highIndex, inclusive.
* It arbitrarily chooses the last element in the subarray as the pivot.
* Then, it partially sorts the subarray into elements than are less than the pivot,
* and elements that are greater than or equal to the pivot.
* Each time partitionArray() is executed, the pivot element is in its final sorted position.
*
* @param {number} lowIndex
* @param {number} highIndex
* @return {number}
*/
const partitionArray = (lowIndex, highIndex) => {
/**
* Swaps two elements in array.
* @param {number} leftIndex
* @param {number} rightIndex
*/
const swap = (leftIndex, rightIndex) => {
const temp = array[leftIndex];
array[leftIndex] = array[rightIndex];
array[rightIndex] = temp;
};
const pivot = array[highIndex];
// visitingCallback is used for time-complexity analysis.
this.callbacks.visitingCallback(array[pivot]);
let partitionIndex = lowIndex;
for (let currentIndex = lowIndex; currentIndex < highIndex; currentIndex += 1) {
if (this.comparator.lessThan(array[currentIndex], pivot)) {
swap(partitionIndex, currentIndex);
partitionIndex += 1;
}
}
// The element at the partitionIndex is guaranteed to be greater than or equal to pivot.
// All elements to the left of partitionIndex are guaranteed to be less than pivot.
// Swapping the pivot with the partitionIndex therefore places the pivot in its
// final sorted position.
swap(partitionIndex, highIndex);
return partitionIndex;
};
/**
* Replace recursion with auxiliary stack
*/
const stack = new Stack();
stack.push(inputLowIndex);
stack.push(inputHighIndex);
while (!stack.isEmpty()) {
const highIndex = stack.pop();
const lowIndex = stack.pop();
const partitionIndex = partitionArray(lowIndex, highIndex);
if (partitionIndex - 1 > lowIndex) {
stack.push(lowIndex);
stack.push(partitionIndex - 1);
}
if (partitionIndex + 1 < highIndex) {
stack.push(partitionIndex + 1);
stack.push(highIndex);
}
}
return array;
}
}

View File

@ -0,0 +1,74 @@
import QuickSortIterative from '../QuickSortIterative';
import {
equalArr,
notSortedArr,
reverseArr,
sortedArr,
SortTester,
} from '../../SortTester';
// Complexity constants.
const SORTED_ARRAY_VISITING_COUNT = 19;
const NOT_SORTED_ARRAY_VISITING_COUNT = 19;
const REVERSE_SORTED_ARRAY_VISITING_COUNT = 19;
const EQUAL_ARRAY_VISITING_COUNT = 19;
describe('QuickSortIterative', () => {
it('should sort array', () => {
SortTester.testSort(QuickSortIterative);
});
it('should sort array with custom comparator', () => {
SortTester.testSortWithCustomComparator(QuickSortIterative);
});
it('should sort negative numbers', () => {
SortTester.testNegativeNumbersSort(QuickSortIterative);
});
it('should visit EQUAL array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
QuickSortIterative,
equalArr,
EQUAL_ARRAY_VISITING_COUNT,
);
});
it('should visit SORTED array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
QuickSortIterative,
sortedArr,
SORTED_ARRAY_VISITING_COUNT,
);
});
it('should visit NOT SORTED array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
QuickSortIterative,
notSortedArr,
NOT_SORTED_ARRAY_VISITING_COUNT,
);
});
it('should visit REVERSE SORTED array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
QuickSortIterative,
reverseArr,
REVERSE_SORTED_ARRAY_VISITING_COUNT,
);
});
it('should sort in place', () => {
const sorter = new QuickSortIterative();
const originalArray = [7, 5, 1, 42, 30, 24, 14];
const sortedArray = sorter.sort(originalArray);
expect(originalArray).toEqual(sortedArray);
});
it('should not modify original array', () => {
const sorter = new QuickSortIterative();
const originalArray = [7, 5, 1, 42, 30, 24, 14];
const sortedArray = sorter.sort(originalArray, 0, originalArray.length - 1, false);
expect(originalArray).not.toEqual(sortedArray);
});
});