-
-
Notifications
You must be signed in to change notification settings - Fork 30.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add iterative quick sort #215
Open
yavorski
wants to merge
1
commit into
trekhleb:master
Choose a base branch
from
yavorski:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+170
−0
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
74 changes: 74 additions & 0 deletions
74
src/algorithms/sorting/quick-sort/__test__/QuickSortIterative.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}); | ||
}); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice addition! Just to nitpick, it might be a little cleaner to store arrays on the stack. That way, each item in the stack corresponds to a single iteration.
e.g.