Skip to content

Commit 34d7277

Browse files
authored
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
1 parent 1e333b0 commit 34d7277

2 files changed

Lines changed: 82 additions & 17 deletions

File tree

index.js

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -349,12 +349,58 @@ function upperBound(value, arr) {
349349
* @param {number} nodeSize
350350
*/
351351
function sort(values, boxes, indices, left, right, nodeSize) {
352-
if (Math.floor(left / nodeSize) >= Math.floor(right / nodeSize)) return;
352+
const stack = [];
353+
let stackPointer = 0;
354+
355+
stack.push(left);
356+
stackPointer++;
357+
stack.push(right);
358+
stackPointer++;
359+
360+
while (stackPointer > 0) {
361+
// @ts-expect-error
362+
const r = stack.pop();
363+
stackPointer--;
364+
const l = stack.pop();
365+
stackPointer--;
366+
367+
// @ts-expect-error
368+
if ((r - l) <= nodeSize) {
369+
// @ts-expect-error
370+
if (Math.floor(l / nodeSize) >= Math.floor(r / nodeSize)) continue;
371+
}
372+
373+
// @ts-expect-error
374+
const pivot = getPivot(values, l, r);
375+
376+
// @ts-expect-error
377+
let i = l - 1;
378+
// @ts-expect-error
379+
let j = r + 1;
380+
381+
while (true) {
382+
do i++; while (values[i] < pivot);
383+
do j--; while (values[j] > pivot);
384+
if (i >= j) break;
385+
swap(values, boxes, indices, i, j);
386+
}
387+
388+
stack.push(l, j, j + 1, r);
389+
stackPointer += 4;
390+
}
391+
}
353392

393+
/**
394+
* Determine pivot value.
395+
* @param {Uint32Array} values
396+
* @param {number} l
397+
* @param {number} r
398+
*/
399+
function getPivot(values, l, r) {
354400
// apply median of three method
355-
const start = values[left];
356-
const mid = values[(left + right) >> 1];
357-
const end = values[right];
401+
const start = values[l];
402+
const mid = values[(l + r) >> 1];
403+
const end = values[r];
358404

359405
let pivot = end;
360406

@@ -366,19 +412,7 @@ function sort(values, boxes, indices, left, right, nodeSize) {
366412
} else if (x === mid) {
367413
pivot = Math.max(start, end);
368414
}
369-
370-
let i = left - 1;
371-
let j = right + 1;
372-
373-
while (true) {
374-
do i++; while (values[i] < pivot);
375-
do j--; while (values[j] > pivot);
376-
if (i >= j) break;
377-
swap(values, boxes, indices, i, j);
378-
}
379-
380-
sort(values, boxes, indices, left, j, nodeSize);
381-
sort(values, boxes, indices, j + 1, right, nodeSize);
415+
return pivot;
382416
}
383417

384418
/**

test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,4 +250,35 @@ test('quicksort should work with an inbalanced dataset', () => {
250250
});
251251
});
252252

253+
test('quicksort should work with duplicates', () => {
254+
const n = 55000 + 5500 + 7700;
255+
const index = new Flatbush(n);
256+
257+
let x = 0;
258+
259+
for (let p = 0; p < 55000; p++) {
260+
index.add(x, 3.0, x, 3.0);
261+
x++;
262+
}
263+
264+
for (let p = 0; p < 5500; p++) {
265+
index.add(x, 4.0, x, 4.0);
266+
x++;
267+
}
268+
269+
for (let p = 0; p < 7700; p++) {
270+
index.add(x, 5.0, x, 5.0);
271+
x++;
272+
}
273+
274+
index.finish();
275+
276+
assert.doesNotThrow(() => {
277+
index.search(0.5, -1, 6.5, 1);
278+
});
279+
});
280+
281+
282+
283+
253284
function compare(a, b) { return a - b; }

0 commit comments

Comments
 (0)