mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-09-20 07:43:04 +08:00
Add heap sort.
This commit is contained in:
parent
053b365f24
commit
36bbfed6a1
@ -34,6 +34,7 @@
|
||||
* [Bubble Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/bubble-sort)
|
||||
* [Selection Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/selection-sort)
|
||||
* [Insertion Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/insertion-sort)
|
||||
* [Heap Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/heap-sort)
|
||||
|
||||
## Running Tests
|
||||
|
||||
|
@ -7,6 +7,12 @@ import {
|
||||
SortTester,
|
||||
} from '../../SortTester';
|
||||
|
||||
// Complexity constants.
|
||||
const SORTED_ARRAY_VISITING_COUNT = 20;
|
||||
const NOT_SORTED_ARRAY_VISITING_COUNT = 280;
|
||||
const REVERSE_SORTED_ARRAY_VISITING_COUNT = 400;
|
||||
const EQUAL_ARRAY_VISITING_COUNT = 20;
|
||||
|
||||
describe('BubbleSort', () => {
|
||||
it('should sort array', () => {
|
||||
SortTester.testSort(BubbleSort);
|
||||
@ -21,42 +27,34 @@ describe('BubbleSort', () => {
|
||||
});
|
||||
|
||||
it('should visit EQUAL array element specified number of times', () => {
|
||||
const expectedNumberOfVisits = 20;
|
||||
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
BubbleSort,
|
||||
equalArr,
|
||||
expectedNumberOfVisits,
|
||||
EQUAL_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit SORTED array element specified number of times', () => {
|
||||
const expectedNumberOfVisits = 20;
|
||||
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
BubbleSort,
|
||||
sortedArr,
|
||||
expectedNumberOfVisits,
|
||||
SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit NOT SORTED array element specified number of times', () => {
|
||||
const expectedNumberOfVisits = 280;
|
||||
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
BubbleSort,
|
||||
notSortedArr,
|
||||
expectedNumberOfVisits,
|
||||
NOT_SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit REVERSE SORTED array element specified number of times', () => {
|
||||
const expectedNumberOfVisits = 400;
|
||||
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
BubbleSort,
|
||||
reverseArr,
|
||||
expectedNumberOfVisits,
|
||||
REVERSE_SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
30
src/algorithms/sorting/heap-sort/HeapSort.js
Normal file
30
src/algorithms/sorting/heap-sort/HeapSort.js
Normal file
@ -0,0 +1,30 @@
|
||||
import Sort from '../Sort';
|
||||
import MinHeap from '../../../data-structures/heap/MinHeap';
|
||||
|
||||
export default class HeapSort extends Sort {
|
||||
sort(originalArray) {
|
||||
const sortedArray = [];
|
||||
const minHeap = new MinHeap(this.callbacks.compareCallback);
|
||||
|
||||
// Insert all array elements to the heap.
|
||||
originalArray.forEach((element) => {
|
||||
// Call visiting callback.
|
||||
this.callbacks.visitingCallback(element);
|
||||
|
||||
minHeap.add(element);
|
||||
});
|
||||
|
||||
// Now we have min heap with minimal element always on top.
|
||||
// Let's poll that minimal element one by one and thus form the sorted array.
|
||||
while (!minHeap.isEmpty()) {
|
||||
const nextMinElement = minHeap.poll();
|
||||
|
||||
// Call visiting callback.
|
||||
this.callbacks.visitingCallback(nextMinElement);
|
||||
|
||||
sortedArray.push(nextMinElement);
|
||||
}
|
||||
|
||||
return sortedArray;
|
||||
}
|
||||
}
|
18
src/algorithms/sorting/heap-sort/README.md
Normal file
18
src/algorithms/sorting/heap-sort/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Heap Sort
|
||||
|
||||
Heapsort is a comparison-based sorting algorithm.
|
||||
Heapsort can be thought of as an improved selection
|
||||
sort: like that algorithm, it divides its input into
|
||||
a sorted and an unsorted region, and it iteratively
|
||||
shrinks the unsorted region by extracting the largest
|
||||
element and moving that to the sorted region. The
|
||||
improvement consists of the use of a heap data structure
|
||||
rather than a linear-time search to find the maximum.
|
||||
|
||||
![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/1/1b/Sorting_heapsort_anim.gif)
|
||||
|
||||
![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/4/4d/Heapsort-example.gif)
|
||||
|
||||
## References
|
||||
|
||||
[Wikipedia](https://en.wikipedia.org/wiki/Heapsort)
|
56
src/algorithms/sorting/heap-sort/__test__/HeapSort.test.js
Normal file
56
src/algorithms/sorting/heap-sort/__test__/HeapSort.test.js
Normal file
@ -0,0 +1,56 @@
|
||||
import HeapSort from '../HeapSort';
|
||||
import {
|
||||
equalArr,
|
||||
notSortedArr,
|
||||
reverseArr,
|
||||
sortedArr,
|
||||
SortTester,
|
||||
} from '../../SortTester';
|
||||
|
||||
// Complexity constants.
|
||||
const SORTED_ARRAY_VISITING_COUNT = 40;
|
||||
const NOT_SORTED_ARRAY_VISITING_COUNT = 40;
|
||||
const REVERSE_SORTED_ARRAY_VISITING_COUNT = 40;
|
||||
const EQUAL_ARRAY_VISITING_COUNT = 40;
|
||||
|
||||
describe('HeapSort', () => {
|
||||
it('should sort array', () => {
|
||||
SortTester.testSort(HeapSort);
|
||||
});
|
||||
|
||||
it('should sort array with custom comparator', () => {
|
||||
SortTester.testSortWithCustomComparator(HeapSort);
|
||||
});
|
||||
|
||||
it('should visit EQUAL array element specified number of times', () => {
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
HeapSort,
|
||||
equalArr,
|
||||
EQUAL_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit SORTED array element specified number of times', () => {
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
HeapSort,
|
||||
sortedArr,
|
||||
SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit NOT SORTED array element specified number of times', () => {
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
HeapSort,
|
||||
notSortedArr,
|
||||
NOT_SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit REVERSE SORTED array element specified number of times', () => {
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
HeapSort,
|
||||
reverseArr,
|
||||
REVERSE_SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
});
|
@ -7,6 +7,12 @@ import {
|
||||
SortTester,
|
||||
} from '../../SortTester';
|
||||
|
||||
// Complexity constants.
|
||||
const SORTED_ARRAY_VISITING_COUNT = 20;
|
||||
const NOT_SORTED_ARRAY_VISITING_COUNT = 101;
|
||||
const REVERSE_SORTED_ARRAY_VISITING_COUNT = 210;
|
||||
const EQUAL_ARRAY_VISITING_COUNT = 20;
|
||||
|
||||
describe('InsertionSort', () => {
|
||||
it('should sort array', () => {
|
||||
SortTester.testSort(InsertionSort);
|
||||
@ -21,42 +27,34 @@ describe('InsertionSort', () => {
|
||||
});
|
||||
|
||||
it('should visit EQUAL array element specified number of times', () => {
|
||||
const expectedNumberOfVisits = 20;
|
||||
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
InsertionSort,
|
||||
equalArr,
|
||||
expectedNumberOfVisits,
|
||||
EQUAL_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit SORTED array element specified number of times', () => {
|
||||
const expectedNumberOfVisits = 20;
|
||||
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
InsertionSort,
|
||||
sortedArr,
|
||||
expectedNumberOfVisits,
|
||||
SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit NOT SORTED array element specified number of times', () => {
|
||||
const expectedNumberOfVisits = 101;
|
||||
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
InsertionSort,
|
||||
notSortedArr,
|
||||
expectedNumberOfVisits,
|
||||
NOT_SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit REVERSE SORTED array element specified number of times', () => {
|
||||
const expectedNumberOfVisits = 210;
|
||||
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
InsertionSort,
|
||||
reverseArr,
|
||||
expectedNumberOfVisits,
|
||||
REVERSE_SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -7,6 +7,12 @@ import {
|
||||
SortTester,
|
||||
} from '../../SortTester';
|
||||
|
||||
// Complexity constants.
|
||||
const SORTED_ARRAY_VISITING_COUNT = 209;
|
||||
const NOT_SORTED_ARRAY_VISITING_COUNT = 209;
|
||||
const REVERSE_SORTED_ARRAY_VISITING_COUNT = 209;
|
||||
const EQUAL_ARRAY_VISITING_COUNT = 209;
|
||||
|
||||
describe('SelectionSort', () => {
|
||||
it('should sort array', () => {
|
||||
SortTester.testSort(SelectionSort);
|
||||
@ -17,42 +23,34 @@ describe('SelectionSort', () => {
|
||||
});
|
||||
|
||||
it('should visit EQUAL array element specified number of times', () => {
|
||||
const expectedNumberOfVisits = 209;
|
||||
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
SelectionSort,
|
||||
equalArr,
|
||||
expectedNumberOfVisits,
|
||||
EQUAL_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit SORTED array element specified number of times', () => {
|
||||
const expectedNumberOfVisits = 209;
|
||||
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
SelectionSort,
|
||||
sortedArr,
|
||||
expectedNumberOfVisits,
|
||||
SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit NOT SORTED array element specified number of times', () => {
|
||||
const expectedNumberOfVisits = 209;
|
||||
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
SelectionSort,
|
||||
notSortedArr,
|
||||
expectedNumberOfVisits,
|
||||
NOT_SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit REVERSE SORTED array element specified number of times', () => {
|
||||
const expectedNumberOfVisits = 209;
|
||||
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
SelectionSort,
|
||||
reverseArr,
|
||||
expectedNumberOfVisits,
|
||||
REVERSE_SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -1,10 +1,10 @@
|
||||
import Comparator from '../../utils/comparator/Comparator';
|
||||
|
||||
export default class MinHeap {
|
||||
constructor() {
|
||||
constructor(comparatorFunction) {
|
||||
// Array representation of the heap.
|
||||
this.heapContainer = [];
|
||||
this.compare = new Comparator();
|
||||
this.compare = new Comparator(comparatorFunction);
|
||||
}
|
||||
|
||||
static getLeftChildIndex(parentIndex) {
|
||||
@ -120,6 +120,10 @@ export default class MinHeap {
|
||||
}
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return !this.heapContainer.length;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.heapContainer.toString();
|
||||
}
|
||||
|
@ -6,12 +6,14 @@ describe('MinHeap', () => {
|
||||
|
||||
expect(minHeap).toBeDefined();
|
||||
expect(minHeap.peek()).toBeNull();
|
||||
expect(minHeap.isEmpty()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should add items to the heap and heapify it up', () => {
|
||||
const minHeap = new MinHeap();
|
||||
|
||||
minHeap.add(5);
|
||||
expect(minHeap.isEmpty()).toBeFalsy();
|
||||
expect(minHeap.peek()).toBe(5);
|
||||
expect(minHeap.toString()).toBe('5');
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user