mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-11-10 11:09:43 +08:00
Merge ef95c02318
into ca3d16dcce
This commit is contained in:
commit
b87b7b75a2
113
src/algorithms/sorting/tim-sort/TimSort.js
Normal file
113
src/algorithms/sorting/tim-sort/TimSort.js
Normal file
@ -0,0 +1,113 @@
|
||||
/* eslint-disable linebreak-style */
|
||||
/* eslint-disable operator-assignment */
|
||||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable linebreak-style */
|
||||
/* eslint-disable no-plusplus */
|
||||
import Sort from '../Sort';
|
||||
|
||||
export default class TimSort extends Sort {
|
||||
sort(originalArray) {
|
||||
let RUN = 32;
|
||||
const array = [...originalArray];
|
||||
if (array.length < RUN) RUN = RUN / 2 + 1;
|
||||
|
||||
for (let i = 0; i < array.length; i += RUN) {
|
||||
this.insertionSort(array, i, Math.min(i + RUN - 1, array.length - 1));
|
||||
}
|
||||
|
||||
for (let size = RUN; size < array.length; size = 2 * size) {
|
||||
for (let left = 0; left < array.length; left += 2 * size) {
|
||||
// find ending point of
|
||||
// left sub array
|
||||
// mid+1 is starting point
|
||||
// of right sub array
|
||||
const mid = left + size - 1;
|
||||
const right = Math.min(left + 2 * size - 1, array.length - 1);
|
||||
|
||||
// merge sub array arr[left.....mid] &
|
||||
// arr[mid+1....right]
|
||||
this.merge(array, left, mid, right);
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
insertionSort(originalArray, leftIndex, rightIndex) {
|
||||
// Go through leftIndex to rightIndex Elements
|
||||
for (let i = leftIndex; i <= rightIndex; i += 1) {
|
||||
let currentIndex = i;
|
||||
|
||||
// Call visiting callback.
|
||||
this.callbacks.visitingCallback(originalArray[i]);
|
||||
|
||||
// Go and check if previous elements and greater then current one.
|
||||
// If this is the case then swap that elements.
|
||||
while (
|
||||
originalArray[currentIndex - 1] !== undefined
|
||||
&& this.comparator.lessThan(
|
||||
originalArray[currentIndex],
|
||||
originalArray[currentIndex - 1],
|
||||
)
|
||||
) {
|
||||
// Call visiting callback.
|
||||
this.callbacks.visitingCallback(originalArray[currentIndex - 1]);
|
||||
|
||||
// Swap the elements.
|
||||
[originalArray[currentIndex - 1], originalArray[currentIndex]] = [
|
||||
originalArray[currentIndex],
|
||||
originalArray[currentIndex - 1],
|
||||
];
|
||||
|
||||
// Shift current index left.
|
||||
currentIndex -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
merge(originalArray, l, m, r) {
|
||||
// Original array is broken in two parts
|
||||
// left and right array
|
||||
const len1 = m - l + 1;
|
||||
const len2 = r - m;
|
||||
|
||||
const left = [];
|
||||
const right = [];
|
||||
|
||||
for (let i = 0; i < len1; i++) left[i] = originalArray[l + i];
|
||||
for (let i = 0; i < len2; i++) right[i] = originalArray[m + 1 + i];
|
||||
|
||||
let i = 0;
|
||||
let j = 0;
|
||||
let k = left;
|
||||
|
||||
// After comparing, we
|
||||
// merge those two array
|
||||
// in larger sub array
|
||||
// eslint-disable-next-line max-len
|
||||
while (i < len1 && j < len2 && left[i] !== undefined && right[j] !== undefined && originalArray[k] !== undefined) {
|
||||
if (this.comparator.lessThan(left[i], right[j])) {
|
||||
originalArray[k] = left[i];
|
||||
i++;
|
||||
} else {
|
||||
originalArray[k] = right[j];
|
||||
j++;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
|
||||
// Copy remaining elements of left, if any
|
||||
while (i < len1 && left[i] !== undefined && originalArray[k] !== undefined) {
|
||||
originalArray[k] = left[i];
|
||||
k++;
|
||||
i++;
|
||||
}
|
||||
|
||||
// Copy remaining element of right, if any
|
||||
while (j < len2 && right[j] !== undefined && originalArray[k] !== undefined) {
|
||||
originalArray[k] = right[j];
|
||||
k++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
66
src/algorithms/sorting/tim-sort/__test__/TimSort.test.js
Normal file
66
src/algorithms/sorting/tim-sort/__test__/TimSort.test.js
Normal file
@ -0,0 +1,66 @@
|
||||
/* eslint-disable linebreak-style */
|
||||
import TimSort from '../TimSort';
|
||||
|
||||
import {
|
||||
equalArr,
|
||||
notSortedArr,
|
||||
reverseArr,
|
||||
sortedArr,
|
||||
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('TimSort', () => {
|
||||
it('should sort array', () => {
|
||||
SortTester.testSort(TimSort);
|
||||
});
|
||||
|
||||
it('should sort array with custom comparator', () => {
|
||||
SortTester.testSortWithCustomComparator(TimSort);
|
||||
});
|
||||
|
||||
it('should do stable sorting', () => {
|
||||
SortTester.testSortStability(TimSort);
|
||||
});
|
||||
|
||||
it('should sort negative numbers', () => {
|
||||
SortTester.testNegativeNumbersSort(TimSort);
|
||||
});
|
||||
|
||||
it('should visit EQUAL array element specified number of times', () => {
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
TimSort,
|
||||
equalArr,
|
||||
EQUAL_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit SORTED array element specified number of times', () => {
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
TimSort,
|
||||
sortedArr,
|
||||
SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit NOT SORTED array element specified number of times', () => {
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
TimSort,
|
||||
notSortedArr,
|
||||
NOT_SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should visit REVERSE SORTED array element specified number of times', () => {
|
||||
SortTester.testAlgorithmTimeComplexity(
|
||||
TimSort,
|
||||
reverseArr,
|
||||
REVERSE_SORTED_ARRAY_VISITING_COUNT,
|
||||
);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user