|
@@ -1,458 +1,44 @@
|
|
|
-// array A[] has the items to sort; array B[] is a work array
|
|
|
-function BottomUpMergeSort(A: number[]) {
|
|
|
- const n = A.length;
|
|
|
- let src = A, target = new (A as any).constructor(n) as any;
|
|
|
- // Each 1-element run in A is already "sorted".
|
|
|
- // Make successively longer sorted runs of length 2, 4, 8, 16... until whole array is sorted.
|
|
|
- for (let width = 1; width < n; width = 2 * width) {
|
|
|
- // Array A is full of runs of length width.
|
|
|
- for (let i = 0; i < n; i = i + 2 * width) {
|
|
|
- // Merge two runs: A[i:i+width-1] and A[i+width:i+2*width-1] to B[]
|
|
|
- // or copy A[i:n-1] to B[] ( if(i+width >= n) )
|
|
|
- BottomUpMerge(src, i, Math.min(i + width, n), Math.min(i + 2 * width, n), target);
|
|
|
- }
|
|
|
- // Now work array B is full of runs of length 2*width.
|
|
|
- // Copy array B to array A for next iteration.
|
|
|
- // A more efficient implementation would swap the roles of A and B.
|
|
|
- const t = src;
|
|
|
- src = target;
|
|
|
- target = t;
|
|
|
- // Now array A is full of runs of length 2*width.
|
|
|
- }
|
|
|
- if (src !== A) {
|
|
|
- for (let i = 0; i < n; i++) src[i] = target[i];
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// Left run is A[iLeft :iRight-1].
|
|
|
-// Right run is A[iRight:iEnd-1 ].
|
|
|
-function BottomUpMerge(A: number[], iLeft: number, iRight: number, iEnd: number, B: number[]) {
|
|
|
- let i = iLeft, j = iRight;
|
|
|
- // While there are elements in the left or right runs...
|
|
|
- for (let k = iLeft; k < iEnd; k++) {
|
|
|
- // If left run head exists and is <= existing right run head.
|
|
|
- const u = A[i], v = A[j];
|
|
|
- if (i < iRight && (j >= iEnd || u <= v)) {
|
|
|
- B[k] = u;
|
|
|
- i = i + 1;
|
|
|
- } else {
|
|
|
- B[k] = v;
|
|
|
- j = j + 1;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function mergeSort(a: ArrayLike<number>) {
|
|
|
- BottomUpMergeSort(a as any);
|
|
|
- return a;
|
|
|
-}
|
|
|
-
|
|
|
-function heapify(arr: number[], n: number, i: number) {
|
|
|
- const l = 2 * i + 1; // left = 2*i + 1
|
|
|
- const r = 2 * i + 2; // right = 2*i + 2
|
|
|
-
|
|
|
- let largest = i; // Initialize largest as root
|
|
|
-
|
|
|
- // If left child is larger than root
|
|
|
- if (l < n && arr[l] > arr[largest])
|
|
|
- largest = l;
|
|
|
-
|
|
|
- // If right child is larger than largest so far
|
|
|
- if (r < n && arr[r] > arr[largest])
|
|
|
- largest = r;
|
|
|
-
|
|
|
- // If largest is not root
|
|
|
- if (largest !== i) {
|
|
|
- //swap(arr[i], arr[largest]);
|
|
|
- const t = arr[i];
|
|
|
- arr[i] = arr[largest];
|
|
|
- arr[largest] = t;
|
|
|
-
|
|
|
- // Recursively heapify the affected sub-tree
|
|
|
- heapify(arr, n, largest);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// main function to do heap sort
|
|
|
-function heapSort(arr: number[]) {
|
|
|
- const n = arr.length;
|
|
|
- // Build heap (rearrange array)
|
|
|
- for (let i = n / 2 - 1; i >= 0; i--)
|
|
|
- heapify(arr, n, i);
|
|
|
-
|
|
|
- // One by one extract an element from heap
|
|
|
- for (let i = n - 1; i >= 0; i--) {
|
|
|
- // Move current root to end
|
|
|
- //swap(arr[0], arr[i]);
|
|
|
- const t = arr[0];
|
|
|
- arr[0] = arr[i];
|
|
|
- arr[i] = t;
|
|
|
-
|
|
|
- // call max heapify on the reduced heap
|
|
|
- heapify(arr, i, 0);
|
|
|
- }
|
|
|
- return arr;
|
|
|
-}
|
|
|
+import * as B from 'benchmark'
|
|
|
+import * as Sort from '../structure/collections/sort'
|
|
|
|
|
|
function createTestData(n: number) {
|
|
|
- const data = []; //new Int32Array(n); //new Array(n);
|
|
|
+ const data = new Int32Array(n); //new Array(n);
|
|
|
for (let i = 0; i < n; i++) {
|
|
|
data[i] = (n * Math.random()) | 0;
|
|
|
}
|
|
|
return data;
|
|
|
-
|
|
|
- //return [ 5, 9, 10, 5, 7, 6, 8, 7, 0, 0, 0, 1, 2, 3, 4 ];
|
|
|
-}
|
|
|
-
|
|
|
-let swapCount = 0;
|
|
|
-function swap(arr: number[], i: number, j: number) {
|
|
|
- const temp = arr[i];
|
|
|
- arr[i] = arr[j];
|
|
|
- arr[j] = temp;
|
|
|
-}
|
|
|
-
|
|
|
-function medianPivot(arr: number[], left: number, right: number) {
|
|
|
- const l = arr[left], r = arr[right], m = arr[(left + right) >> 1];
|
|
|
- if (l > r) return l > m ? Math.max(m, r) : l;
|
|
|
- else return r > m ? Math.max(m, l) : r;
|
|
|
-}
|
|
|
-
|
|
|
-const _qsParts = [0, 0];
|
|
|
-
|
|
|
-function partition3(xs: number[], l: number, r: number) {
|
|
|
- const v = medianPivot(xs, l, r)
|
|
|
- let pivot = l, equals = l;
|
|
|
- for (let i = l; i <= r; i++) {
|
|
|
- const t = xs[i];
|
|
|
- if (t < v) {
|
|
|
- /*if (pivot !== i)*/ swap(xs, i, pivot);
|
|
|
- pivot++;
|
|
|
- } else if (t === v) {
|
|
|
- swap(xs, i, pivot);
|
|
|
- swap(xs, pivot, equals);
|
|
|
- equals++;
|
|
|
- pivot++;
|
|
|
- }
|
|
|
- }
|
|
|
- for (let i = l; i < equals; i++) {
|
|
|
- swap(xs, i, l + pivot - i - 1)
|
|
|
- }
|
|
|
- _qsParts[0] = pivot - equals + l;
|
|
|
- _qsParts[1] = pivot - 1;
|
|
|
}
|
|
|
|
|
|
-function partition3_1(xs: number[], l: number, r: number) {
|
|
|
- 'use strict';
|
|
|
- const v = medianPivot(xs, l, r);
|
|
|
- // console.log('P', v);
|
|
|
- // console.log('I', xs.slice(l, r + 1));
|
|
|
- let equals = l, tail = r;
|
|
|
-
|
|
|
- while (xs[tail] > v)--tail;
|
|
|
- for (let i = l; i <= tail; i++) {
|
|
|
- const t = xs[i];
|
|
|
- if (t > v) {
|
|
|
- swap(xs, i, tail);
|
|
|
- tail--;
|
|
|
- while (xs[tail] > v)--tail;
|
|
|
- i--;
|
|
|
- } else if (t === v) {
|
|
|
- swap(xs, i, equals);
|
|
|
- equals++;
|
|
|
- }
|
|
|
- }
|
|
|
- //console.log('M', xs.slice(l, r + 1), equals, tail);
|
|
|
- for (let i = l; i < equals; i++) {
|
|
|
- swap(xs, i, l + tail - i)
|
|
|
- }
|
|
|
- //console.log('F', xs.slice(l, r + 1), tail - equals + l + 1, tail);
|
|
|
- _qsParts[0] = tail - equals + l + 1;
|
|
|
- _qsParts[1] = tail;
|
|
|
+export function copyArray(xs: any) {
|
|
|
+ const ret = new xs.constructor(xs.length);
|
|
|
+ for (let i = 0, _i = xs.length; i < _i; i++) ret[i] = xs[i];
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
-function insertionSort(xs: number[], start: number, end: number) {
|
|
|
- 'use strict';
|
|
|
-
|
|
|
- for (let i = start + 1; i <= end; i++) {
|
|
|
- const key = xs[i];
|
|
|
- let j = i - 1;
|
|
|
- while (j >= 0 && xs[j] > key) {
|
|
|
- xs[j + 1] = xs[j];
|
|
|
- j = j - 1;
|
|
|
- }
|
|
|
- xs[j + 1] = key;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function quickSort(xs: number[], low: number, high: number) {
|
|
|
- 'use strict';
|
|
|
-
|
|
|
- while (low < high) {
|
|
|
- if (high - low < 16) {
|
|
|
- insertionSort(xs, low, high);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- partition3_1(xs, low, high);
|
|
|
- const li = _qsParts[0], ri = _qsParts[1];
|
|
|
-
|
|
|
- if (li - low < high - ri) {
|
|
|
- quickSort(xs, low, li - 1);
|
|
|
- low = ri + 1;
|
|
|
- } else { // Else recur for right part
|
|
|
- quickSort(xs, ri + 1, high);
|
|
|
- high = li - 1;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function checkSorted(arr: ArrayLike<number>) {
|
|
|
+export function checkSorted(arr: ArrayLike<number>) {
|
|
|
for (let i = 0; i < arr.length - 1; i++) {
|
|
|
if (arr[i] > arr[i + 1]) {
|
|
|
- console.log('not sorted');
|
|
|
- return;
|
|
|
+ return false;
|
|
|
}
|
|
|
}
|
|
|
- console.log('sorted');
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
-function defaultCmp(a: number, b: number) {
|
|
|
- if (a > b) return 1
|
|
|
- if (a < b) return -1
|
|
|
- return 0
|
|
|
-}
|
|
|
-
|
|
|
-function quicksortCmp(arr: number[], cmp: any, bb: number, ee: number) {
|
|
|
- cmp = cmp || defaultCmp
|
|
|
- var begin = bb || 0
|
|
|
- var end = (ee || arr.length) - 1
|
|
|
-
|
|
|
- var stack = []
|
|
|
- var sp = -1
|
|
|
- var left = begin
|
|
|
- var right = end
|
|
|
- var tmp = 0.0
|
|
|
- //var tmp2 = 0.0
|
|
|
-
|
|
|
- // function swap(a: number, b: number) {
|
|
|
- // tmp2 = arr[a]
|
|
|
- // arr[a] = arr[b]
|
|
|
- // arr[b] = tmp2
|
|
|
- // }
|
|
|
-
|
|
|
- var i, j
|
|
|
-
|
|
|
- while (true) {
|
|
|
- if (right - left <= 25) {
|
|
|
- for (j = left + 1; j <= right; ++j) {
|
|
|
- tmp = arr[j]
|
|
|
- i = j - 1
|
|
|
-
|
|
|
- while (i >= left && cmp(arr[i], tmp) > 0) {
|
|
|
- arr[i + 1] = arr[i]
|
|
|
- --i
|
|
|
- }
|
|
|
-
|
|
|
- arr[i + 1] = tmp
|
|
|
- }
|
|
|
-
|
|
|
- if (sp === -1) break
|
|
|
-
|
|
|
- right = stack[sp--] // ?
|
|
|
- left = stack[sp--]
|
|
|
- } else {
|
|
|
- var median = (left + right) >> 1
|
|
|
-
|
|
|
- i = left + 1
|
|
|
- j = right
|
|
|
-
|
|
|
- swap(arr, median, i)
|
|
|
-
|
|
|
- if (cmp(arr[left], arr[right]) > 0) {
|
|
|
- swap(arr, left, right)
|
|
|
- }
|
|
|
-
|
|
|
- if (cmp(arr[i], arr[right]) > 0) {
|
|
|
- swap(arr, i, right)
|
|
|
- }
|
|
|
-
|
|
|
- if (cmp(arr[left], arr[i]) > 0) {
|
|
|
- swap(arr, left, i)
|
|
|
- }
|
|
|
-
|
|
|
- tmp = arr[i]
|
|
|
-
|
|
|
- while (true) {
|
|
|
- do i++; while (cmp(arr[i], tmp) < 0)
|
|
|
- do j--; while (cmp(arr[j], tmp) > 0)
|
|
|
- if (j < i) break
|
|
|
- swap(arr, i, j)
|
|
|
- }
|
|
|
-
|
|
|
- arr[left + 1] = arr[j]
|
|
|
- arr[j] = tmp
|
|
|
-
|
|
|
- if (right - i + 1 >= j - left) {
|
|
|
- stack[++sp] = i
|
|
|
- stack[++sp] = right
|
|
|
- right = j - 1
|
|
|
- } else {
|
|
|
- stack[++sp] = left
|
|
|
- stack[++sp] = j - 1
|
|
|
- left = i
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return arr
|
|
|
-}
|
|
|
-
|
|
|
-(function test() {
|
|
|
- // console.log(medianPivot([1, 2, 3], 0, 2))
|
|
|
- // console.log(medianPivot([1, 3, 2], 0, 2))
|
|
|
- // console.log(medianPivot([2, 1, 3], 0, 2))
|
|
|
- // console.log(medianPivot([3, 1, 2], 0, 2))
|
|
|
- // console.log(medianPivot([2, 3, 1], 0, 2))
|
|
|
- // console.log(medianPivot([3, 2, 1], 0, 2))
|
|
|
-
|
|
|
- const n = 1000;
|
|
|
-
|
|
|
- Array.prototype.sort.call(createTestData(n), (a: number, b: number) => a - b);
|
|
|
- mergeSort(createTestData(n));
|
|
|
-
|
|
|
- let sd;
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- sd = createTestData(n);
|
|
|
- quickSort(sd as any, 0, sd.length - 1);
|
|
|
-
|
|
|
- sd = createTestData(n);
|
|
|
- quicksortCmp(sd as any, void 0, 0, sd.length - 1);
|
|
|
-
|
|
|
- //console.log(sd);
|
|
|
-
|
|
|
- // sd = createTestData(n);
|
|
|
- // console.time('merge');
|
|
|
- // mergeSort(sd);
|
|
|
- // console.timeEnd('merge');
|
|
|
- // checkSorted(sd);
|
|
|
-
|
|
|
- sd = createTestData(n);
|
|
|
-
|
|
|
- checkSorted(sd);
|
|
|
- console.time('heap');
|
|
|
- heapSort(sd as any);
|
|
|
- console.timeEnd('heap');
|
|
|
- checkSorted(sd);
|
|
|
-
|
|
|
- console.time('heap-sorted');
|
|
|
- heapSort(sd as any);
|
|
|
- console.timeEnd('heap-sorted');
|
|
|
- checkSorted(sd);
|
|
|
-
|
|
|
- console.log('--------------');
|
|
|
-
|
|
|
- //console.log('quick', sd);
|
|
|
-
|
|
|
- sd = createTestData(n);
|
|
|
- checkSorted(sd);
|
|
|
- console.time('qs');
|
|
|
- quickSort(sd as any, 0, sd.length - 1);
|
|
|
- console.timeEnd('qs');
|
|
|
- checkSorted(sd);
|
|
|
-
|
|
|
- console.time('qs-sorted');
|
|
|
- quickSort(sd as any, 0, sd.length - 1);
|
|
|
- console.timeEnd('qs-sorted');
|
|
|
- checkSorted(sd);
|
|
|
-
|
|
|
- let reverseSorted = new Int32Array(n);
|
|
|
- for (let i = 0; i < n; i++) {
|
|
|
- reverseSorted[i] = sd[n - i - 1];
|
|
|
- }
|
|
|
-
|
|
|
- console.time('qs-reverse-sorted');
|
|
|
- quickSort(reverseSorted as any, 0, reverseSorted.length - 1);
|
|
|
- console.timeEnd('qs-reverse-sorted');
|
|
|
- checkSorted(reverseSorted);
|
|
|
-
|
|
|
- reverseSorted = new Int32Array(n);
|
|
|
- for (let i = 0; i < n; i++) {
|
|
|
- reverseSorted[i] = sd[n - i - 1];
|
|
|
- }
|
|
|
-
|
|
|
- console.time('qs-reverse-sorted');
|
|
|
- quickSort(reverseSorted as any, 0, reverseSorted.length - 1);
|
|
|
- console.timeEnd('qs-reverse-sorted');
|
|
|
- checkSorted(reverseSorted);
|
|
|
-
|
|
|
- sd = createTestData(n);
|
|
|
- checkSorted(sd);
|
|
|
- console.time('qs');
|
|
|
- quickSort(sd as any, 0, sd.length - 1);
|
|
|
- console.timeEnd('qs');
|
|
|
- checkSorted(sd);
|
|
|
-
|
|
|
- console.log('swap count', swapCount);
|
|
|
-
|
|
|
- console.log('--------------');
|
|
|
-
|
|
|
- //console.log('quick', sd);
|
|
|
-
|
|
|
- sd = createTestData(n);
|
|
|
- checkSorted(sd);
|
|
|
- console.time('qs-a');
|
|
|
- quicksortCmp(sd as any, void 0, 0, sd.length - 1);
|
|
|
- console.timeEnd('qs-a');
|
|
|
- checkSorted(sd);
|
|
|
-
|
|
|
- console.time('qs-a-sorted');
|
|
|
- quicksortCmp(sd as any, void 0, 0, sd.length - 1);
|
|
|
- console.timeEnd('qs-a-sorted');
|
|
|
- checkSorted(sd);
|
|
|
-
|
|
|
- reverseSorted = new Int32Array(n);
|
|
|
- for (let i = 0; i < n; i++) {
|
|
|
- reverseSorted[i] = sd[n - i - 1];
|
|
|
- }
|
|
|
-
|
|
|
- console.time('qs-a-reverse-sorted');
|
|
|
- quicksortCmp(reverseSorted as any, void 0, 0, reverseSorted.length - 1);
|
|
|
- console.timeEnd('qs-a-reverse-sorted');
|
|
|
- checkSorted(reverseSorted);
|
|
|
-
|
|
|
- reverseSorted = new Int32Array(n);
|
|
|
- for (let i = 0; i < n; i++) {
|
|
|
- reverseSorted[i] = sd[n - i - 1];
|
|
|
- }
|
|
|
-
|
|
|
- console.time('qs-a-reverse-sorted');
|
|
|
- quicksortCmp(reverseSorted as any, void 0, 0, reverseSorted.length - 1);
|
|
|
- console.timeEnd('qs-a-reverse-sorted');
|
|
|
- checkSorted(reverseSorted);
|
|
|
-
|
|
|
- sd = createTestData(n);
|
|
|
- checkSorted(sd);
|
|
|
- console.time('qs-a');
|
|
|
- quicksortCmp(sd as any, void 0, 0, sd.length - 1);
|
|
|
- console.timeEnd('qs-a');
|
|
|
- //console.log(sd);
|
|
|
- checkSorted(sd);
|
|
|
-
|
|
|
- console.log('--------------');
|
|
|
+const _data = createTestData(10000);
|
|
|
+const data = () => copyArray(_data);
|
|
|
|
|
|
- sd = createTestData(n);
|
|
|
- checkSorted(sd);
|
|
|
- console.time('native');
|
|
|
- sd.sort((a: number, b: number) => a - b);
|
|
|
- console.timeEnd('native');
|
|
|
- checkSorted(sd);
|
|
|
+const suite = new B.Suite();
|
|
|
|
|
|
- console.time('native-sorted');
|
|
|
- sd.sort((a: number, b: number) => a - b);
|
|
|
- console.timeEnd('native-sorted');
|
|
|
- checkSorted(sd);
|
|
|
- //console.log(sd);
|
|
|
+function le(x: number, y: number) { return x - y; }
|
|
|
|
|
|
-}())
|
|
|
+suite
|
|
|
+ .add('native', () => Array.prototype.sort.call(data(), le))
|
|
|
+ .add('qsort (array asc)', () => Sort.sortArray(data()))
|
|
|
+ .add('qsort (generic)', () => Sort.sort(data(), _data.length, Sort.arrayLess, Sort.arraySwap))
|
|
|
+ .add('native sorted', () => Array.prototype.sort.call(_data, le))
|
|
|
+ .add('qsort sorted (array asc)', () => Sort.sortArray(_data))
|
|
|
+ .add('qsort sorted (generic)', () => Sort.sort(_data, _data.length, Sort.arrayLess, Sort.arraySwap))
|
|
|
+ .on('cycle', (e: any) => {
|
|
|
+ console.log(String(e.target));
|
|
|
+ })
|
|
|
+ .run();
|