Switch to non recursive quicksort (#70)

* added test

* add Bentley-McIlroy 3-way partitioning

* switch to non recursive implementation

* fixed stack handling

* rename function

* escaped ts errors + enhanced performance

* remove escaped error

* additional performance enhancement
This commit is contained in:
muendlein 2025-12-07 23:56:17 +01:00 committed by GitHub
parent 1e333b0369
commit 34d7277eec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 82 additions and 17 deletions

View File

@ -349,12 +349,58 @@ function upperBound(value, arr) {
* @param {number} nodeSize
*/
function sort(values, boxes, indices, left, right, nodeSize) {
if (Math.floor(left / nodeSize) >= Math.floor(right / nodeSize)) return;
const stack = [];
let stackPointer = 0;
stack.push(left);
stackPointer++;
stack.push(right);
stackPointer++;
while (stackPointer > 0) {
// @ts-expect-error
const r = stack.pop();
stackPointer--;
const l = stack.pop();
stackPointer--;
// @ts-expect-error
if ((r - l) <= nodeSize) {
// @ts-expect-error
if (Math.floor(l / nodeSize) >= Math.floor(r / nodeSize)) continue;
}
// @ts-expect-error
const pivot = getPivot(values, l, r);
// @ts-expect-error
let i = l - 1;
// @ts-expect-error
let j = r + 1;
while (true) {
do i++; while (values[i] < pivot);
do j--; while (values[j] > pivot);
if (i >= j) break;
swap(values, boxes, indices, i, j);
}
stack.push(l, j, j + 1, r);
stackPointer += 4;
}
}
/**
* Determine pivot value.
* @param {Uint32Array} values
* @param {number} l
* @param {number} r
*/
function getPivot(values, l, r) {
// apply median of three method
const start = values[left];
const mid = values[(left + right) >> 1];
const end = values[right];
const start = values[l];
const mid = values[(l + r) >> 1];
const end = values[r];
let pivot = end;
@ -366,19 +412,7 @@ function sort(values, boxes, indices, left, right, nodeSize) {
} else if (x === mid) {
pivot = Math.max(start, end);
}
let i = left - 1;
let j = right + 1;
while (true) {
do i++; while (values[i] < pivot);
do j--; while (values[j] > pivot);
if (i >= j) break;
swap(values, boxes, indices, i, j);
}
sort(values, boxes, indices, left, j, nodeSize);
sort(values, boxes, indices, j + 1, right, nodeSize);
return pivot;
}
/**

31
test.js
View File

@ -250,4 +250,35 @@ test('quicksort should work with an inbalanced dataset', () => {
});
});
test('quicksort should work with duplicates', () => {
const n = 55000 + 5500 + 7700;
const index = new Flatbush(n);
let x = 0;
for (let p = 0; p < 55000; p++) {
index.add(x, 3.0, x, 3.0);
x++;
}
for (let p = 0; p < 5500; p++) {
index.add(x, 4.0, x, 4.0);
x++;
}
for (let p = 0; p < 7700; p++) {
index.add(x, 5.0, x, 5.0);
x++;
}
index.finish();
assert.doesNotThrow(() => {
index.search(0.5, -1, 6.5, 1);
});
});
function compare(a, b) { return a - b; }