Skip to content

Commit 152971f

Browse files
author
yavorski
committedSep 27, 2018
Add iterative quick sort
1 parent afa4948 commit 152971f

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import Sort from '../Sort';
2+
import Stack from '../../../data-structures/stack/Stack';
3+
4+
export default class QuickSortIterative extends Sort {
5+
/**
6+
* Iterative Quick Sort
7+
*
8+
* @param {*[]} originalArray - Not sorted array.
9+
* @param {number} inputLowIndex
10+
* @param {number} inputHighIndex
11+
* @return {*[]} - Sorted array.
12+
*/
13+
sort(
14+
originalArray,
15+
inputLowIndex = 0,
16+
inputHighIndex = originalArray.length - 1,
17+
sortInPlace = true,
18+
) {
19+
// Copies array on in case we don't want to sort in place
20+
const array = sortInPlace ? originalArray : [...originalArray];
21+
22+
// If array has less than or equal to one elements then it is already sorted.
23+
if (array.length <= 1) {
24+
return array;
25+
}
26+
27+
/**
28+
* The partitionArray() operates on the subarray between lowIndex and highIndex, inclusive.
29+
* It arbitrarily chooses the last element in the subarray as the pivot.
30+
* Then, it partially sorts the subarray into elements than are less than the pivot,
31+
* and elements that are greater than or equal to the pivot.
32+
* Each time partitionArray() is executed, the pivot element is in its final sorted position.
33+
*
34+
* @param {number} lowIndex
35+
* @param {number} highIndex
36+
* @return {number}
37+
*/
38+
const partitionArray = (lowIndex, highIndex) => {
39+
/**
40+
* Swaps two elements in array.
41+
* @param {number} leftIndex
42+
* @param {number} rightIndex
43+
*/
44+
const swap = (leftIndex, rightIndex) => {
45+
const temp = array[leftIndex];
46+
array[leftIndex] = array[rightIndex];
47+
array[rightIndex] = temp;
48+
};
49+
50+
const pivot = array[highIndex];
51+
// visitingCallback is used for time-complexity analysis.
52+
this.callbacks.visitingCallback(array[pivot]);
53+
54+
let partitionIndex = lowIndex;
55+
for (let currentIndex = lowIndex; currentIndex < highIndex; currentIndex += 1) {
56+
if (this.comparator.lessThan(array[currentIndex], pivot)) {
57+
swap(partitionIndex, currentIndex);
58+
partitionIndex += 1;
59+
}
60+
}
61+
62+
// The element at the partitionIndex is guaranteed to be greater than or equal to pivot.
63+
// All elements to the left of partitionIndex are guaranteed to be less than pivot.
64+
// Swapping the pivot with the partitionIndex therefore places the pivot in its
65+
// final sorted position.
66+
swap(partitionIndex, highIndex);
67+
68+
return partitionIndex;
69+
};
70+
71+
/**
72+
* Replace recursion with auxiliary stack
73+
*/
74+
const stack = new Stack();
75+
stack.push(inputLowIndex);
76+
stack.push(inputHighIndex);
77+
78+
while (!stack.isEmpty()) {
79+
const highIndex = stack.pop();
80+
const lowIndex = stack.pop();
81+
const partitionIndex = partitionArray(lowIndex, highIndex);
82+
83+
if (partitionIndex - 1 > lowIndex) {
84+
stack.push(lowIndex);
85+
stack.push(partitionIndex - 1);
86+
}
87+
88+
if (partitionIndex + 1 < highIndex) {
89+
stack.push(partitionIndex + 1);
90+
stack.push(highIndex);
91+
}
92+
}
93+
94+
return array;
95+
}
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import QuickSortIterative from '../QuickSortIterative';
2+
import {
3+
equalArr,
4+
notSortedArr,
5+
reverseArr,
6+
sortedArr,
7+
SortTester,
8+
} from '../../SortTester';
9+
10+
// Complexity constants.
11+
const SORTED_ARRAY_VISITING_COUNT = 19;
12+
const NOT_SORTED_ARRAY_VISITING_COUNT = 19;
13+
const REVERSE_SORTED_ARRAY_VISITING_COUNT = 19;
14+
const EQUAL_ARRAY_VISITING_COUNT = 19;
15+
16+
describe('QuickSortIterative', () => {
17+
it('should sort array', () => {
18+
SortTester.testSort(QuickSortIterative);
19+
});
20+
21+
it('should sort array with custom comparator', () => {
22+
SortTester.testSortWithCustomComparator(QuickSortIterative);
23+
});
24+
25+
it('should sort negative numbers', () => {
26+
SortTester.testNegativeNumbersSort(QuickSortIterative);
27+
});
28+
29+
it('should visit EQUAL array element specified number of times', () => {
30+
SortTester.testAlgorithmTimeComplexity(
31+
QuickSortIterative,
32+
equalArr,
33+
EQUAL_ARRAY_VISITING_COUNT,
34+
);
35+
});
36+
37+
it('should visit SORTED array element specified number of times', () => {
38+
SortTester.testAlgorithmTimeComplexity(
39+
QuickSortIterative,
40+
sortedArr,
41+
SORTED_ARRAY_VISITING_COUNT,
42+
);
43+
});
44+
45+
it('should visit NOT SORTED array element specified number of times', () => {
46+
SortTester.testAlgorithmTimeComplexity(
47+
QuickSortIterative,
48+
notSortedArr,
49+
NOT_SORTED_ARRAY_VISITING_COUNT,
50+
);
51+
});
52+
53+
it('should visit REVERSE SORTED array element specified number of times', () => {
54+
SortTester.testAlgorithmTimeComplexity(
55+
QuickSortIterative,
56+
reverseArr,
57+
REVERSE_SORTED_ARRAY_VISITING_COUNT,
58+
);
59+
});
60+
61+
it('should sort in place', () => {
62+
const sorter = new QuickSortIterative();
63+
const originalArray = [7, 5, 1, 42, 30, 24, 14];
64+
const sortedArray = sorter.sort(originalArray);
65+
expect(originalArray).toEqual(sortedArray);
66+
});
67+
68+
it('should not modify original array', () => {
69+
const sorter = new QuickSortIterative();
70+
const originalArray = [7, 5, 1, 42, 30, 24, 14];
71+
const sortedArray = sorter.sort(originalArray, 0, originalArray.length - 1, false);
72+
expect(originalArray).not.toEqual(sortedArray);
73+
});
74+
});

0 commit comments

Comments
 (0)
Please sign in to comment.