From be1ef8a9c533f7b56b9168f336d907051b0bb69f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 6 Jul 2018 11:32:58 +0200 Subject: [PATCH] debt - less array creation in stable sort --- src/vs/base/common/arrays.ts | 62 +++++++++++++++----------- src/vs/base/test/common/arrays.test.ts | 5 +++ 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 8053f635f15..0a8cd0b2eb0 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -77,48 +77,56 @@ export function findFirstInSorted(array: T[], p: (x: T) => boolean): number { return low; } +type Compare = (a: T, b: T) => number; + /** * Like `Array#sort` but always stable. Usually runs a little slower `than Array#sort` * so only use this when actually needing stable sort. */ -export function mergeSort(data: T[], compare: (a: T, b: T) => number): T[] { - _divideAndMerge(data, compare); +export function mergeSort(data: T[], compare: Compare): T[] { + _sort(data, compare, 0, data.length - 1, []); return data; } -function _divideAndMerge(data: T[], compare: (a: T, b: T) => number): void { - if (data.length <= 1) { - // sorted - return; +function _merge(a: T[], compare: Compare, lo: number, mid: number, hi: number, aux: T[]): void { + let leftIdx = lo, rightIdx = mid + 1; + for (let i = lo; i <= hi; i++) { + aux[i] = a[i]; } - const p = (data.length / 2) | 0; - const left = data.slice(0, p); - const right = data.slice(p); - - _divideAndMerge(left, compare); - _divideAndMerge(right, compare); - - let leftIdx = 0; - let rightIdx = 0; - let i = 0; - while (leftIdx < left.length && rightIdx < right.length) { - let ret = compare(left[leftIdx], right[rightIdx]); - if (ret <= 0) { - // smaller_equal -> take left to preserve order - data[i++] = left[leftIdx++]; + for (let i = lo; i <= hi; i++) { + if (leftIdx > mid) { + // left side consumed + a[i] = aux[rightIdx++]; + } else if (rightIdx > hi) { + // right side consumed + a[i] = aux[leftIdx++]; + } else if (compare(aux[rightIdx], aux[leftIdx]) < 0) { + // right element is less -> comes first + a[i] = aux[rightIdx++]; } else { - // greater -> take right - data[i++] = right[rightIdx++]; + // left element comes first (less or equal) + a[i] = aux[leftIdx++]; } } - while (leftIdx < left.length) { - data[i++] = left[leftIdx++]; +} + +function _sort(a: T[], compare: Compare, lo: number, hi: number, aux: T[]) { + if (hi <= lo) { + return; } - while (rightIdx < right.length) { - data[i++] = right[rightIdx++]; + let mid = lo + ((hi - lo) / 2) | 0; + _sort(a, compare, lo, mid, aux); + _sort(a, compare, mid + 1, hi, aux); + if (compare(a[mid], a[mid + 1]) <= 0) { + // left and right are sorted and if the last-left element is less + // or equals than the first-right element there is nothing else + // to do + return; } + _merge(a, compare, lo, mid, hi, aux); } + export function groupBy(data: T[], compare: (a: T, b: T) => number): T[][] { const result: T[][] = []; let currentGroup: T[]; diff --git a/src/vs/base/test/common/arrays.test.ts b/src/vs/base/test/common/arrays.test.ts index ee1a6e18b58..cff3a614965 100644 --- a/src/vs/base/test/common/arrays.test.ts +++ b/src/vs/base/test/common/arrays.test.ts @@ -52,6 +52,11 @@ suite('Arrays', () => { assert.deepEqual(data, [1, 2, 3, 4, 5, 6, 7, 8]); }); + test('mergeSort, sorted array', function () { + let data = arrays.mergeSort([1, 2, 3, 4, 5, 6], (a, b) => a - b); + assert.deepEqual(data, [1, 2, 3, 4, 5, 6]); + }); + test('mergeSort, is stable', function () { let numbers = arrays.mergeSort([33, 22, 11, 4, 99, 1], (a, b) => 0); -- GitLab