Skip to content

Commit

Permalink
add MinBucketQueue
Browse files Browse the repository at this point in the history
add MaxBucketQueue
add more benchmarks
  • Loading branch information
keriati committed Jan 3, 2024
1 parent 8b81a5e commit 91c1a3c
Show file tree
Hide file tree
Showing 21 changed files with 769 additions and 208 deletions.
56 changes: 42 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ See [Bucket queue on Wikipedia](https://en.wikipedia.org/wiki/Bucket_queue) for
## Benchmarks

Compared to other Heap-based priority queue implementations, `BucketQueue` can offer a significant performance boost
given the right conditions. The following benchmarks were run on a 2021 MacBook Pro with Apple M1 Max chip, Node.js 20.10.0
given the right conditions. The following benchmarks - about pop and push operations - were run on a 2021 MacBook Pro
with Apple M1 Max chip, Node.js 20.10.0:

BucketQueue x 49,750,443 ops/sec ±0.14% (101 runs sampled)
HeapJS x 12,203,990 ops/sec ±0.18% (99 runs sampled)
Expand All @@ -37,10 +38,10 @@ npm install bucket-priority-queue
Here's how you can integrate BucketQueue into your project:

```typescript
import { BucketQueue } from "bucket-priority-queue";
import { MinBucketQueue, MaxBucketQueue } from "bucket-priority-queue";

// Initialize the queue with optional initial items
const queue = new BucketQueue<number>([
const queue = new MinBucketQueue<number>([
[1, 1],
[2, 2],
[3, 3],
Expand All @@ -51,21 +52,48 @@ queue.push(5, 1); // item 5 with priority 1
queue.push(6, 2); // item 6 with priority 2

// Retrieve items with the highest or lowest priority
const highest = queue.popHighest(); // returns item with highest priority
const lowest = queue.popLowest(); // returns item with lowest priority
const lowest = queue.pop(); // returns item with lowest priority
```

## API Reference

**constructor(items?: [T, Priority][]): void** - Initializes the queue, optionally with an array of items and their priorities.

**push(item: T, priority: Priority): void** - Adds an item with an associated priority to the queue.

**popHighest(): T | undefined** - Removes and returns the item with the highest priority.

**popLowest(): T | undefined** - Removes and returns the item with the lowest priority.

**size: number** - Returns the current size of the queue.
### MinBucketQueue<T>

`MinBucketQueue` is a data structure that operates similarly to a priority queue. It organizes elements in a way such that the element with the minimum priority is always at the front.

#### Methods

- **constructor(items?: [T, Priority][]):** Initialize a new MinBucketQueue with optional initial items.
- **push(item: T, priority: Priority):** Adds an item to the queue with an associated priority.
- **pop(): T | undefined:** Removes and returns the item with the minimum priority. Returns undefined if the queue is empty.
- **add(item: T, priority: Priority):** Alias for push.
- **poll(): T | undefined:** Alias for pop.
- **peek(): T | undefined:** Returns the item with the minimum priority without removing it from the queue.
- **clear():** Removes all items from the queue.
- **refill(items: [T, Priority][]):** Clears the queue and adds the provided items.
- **has(item: T):** Checks if the queue contains the specified item.
- **contains(item: T):** Alias for has.
- **toArray(): T[]:** Returns an array containing all the items in the queue.
- **get size(): number:** Returns the number of items in the queue.

### MaxBucketQueue<T>

`MaxBucketQueue` is a data structure that operates similarly to a priority queue. It organizes elements in a way such that the element with the maximum priority is always at the front.

#### Methods

- **constructor(items?: [T, Priority][]):** Initialize a new MaxBucketQueue with optional initial items.
- **push(item: T, priority: Priority):** Adds an item to the queue with an associated priority.
- **pop(): T | undefined:** Removes and returns the item with the maximum priority. Returns undefined if the queue is empty.
- **add(item: T, priority: Priority):** Alias for push.
- **poll(): T | undefined:** Alias for pop.
- **peek(): T | undefined:** Returns the item with the maximum priority without removing it from the queue.
- **clear():** Removes all items from the queue.
- **refill(items: [T, Priority][]):** Clears the queue and adds the provided items.
- **has(item: T):** Checks if the queue contains the specified item.
- **contains(item: T):** Alias for has.
- **toArray(): T[]:** Returns an array containing all the items in the queue.
- **get size(): number:** Returns the number of items in the queue.

## License

Expand Down
68 changes: 68 additions & 0 deletions benchmark/has.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Benchmark from "benchmark";
import { MinBucketQueue } from "../src";
import HeapJS from "heap-js";
import Heap from "heap";
import { MinHeap as HeapDS } from "@datastructures-js/heap";
import { PriorityQueue, MinPriorityQueue } from "priority-queue-typed";

const suit = new Benchmark.Suite();

const itemNumber = 1_000_000;
const priorityMax = 1000;

const items: number[] = [];

for (let i = 0; i < itemNumber; i++) {
items.push(i % priorityMax);
}

const bq = new MinBucketQueue<number>();
const heapjs = new HeapJS<number>((a, b) => a - b);
const heap = new Heap<number>((a, b) => a - b);
const heapds = new HeapDS<number>();
const mpq = new MinPriorityQueue<number>();
const pq = new PriorityQueue<number>([], { comparator: (a, b) => a - b });

for (let i = 0; i < items.length; i++) {
bq.push(items[i], items[i]);
heapjs.push(items[i]);
heap.push(items[i]);
heapds.push(items[i]);
mpq.add(items[i]);
pq.add(items[i]);
}

suit
.add("MinBucketQueue", () => {
for (let i = 0; i < 1000; i++) {
bq.has(i);
}
})
.add("HeapJS", () => {
for (let i = 0; i < 1000; i++) {
heapjs.contains(i);
}
})
.add("Heap", () => {
for (let i = 0; i < 1000; i++) {
heap.contains(i);
}
})
.add("MinPriorityQueue", () => {
for (let i = 0; i < 1000; i++) {
mpq.has(i);
}
})
.add("PriorityQueue", () => {
for (let i = 0; i < 1000; i++) {
pq.has(i);
}
})
.on("cycle", function (e: Event) {
console.log("" + e.target);
})
.on("complete", function () {
// @ts-expect-error - no error expected
console.log("Fastest is " + this.filter("fastest").map("name"));
})
.run();
8 changes: 4 additions & 4 deletions benchmark/popPush1000.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Benchmark from "benchmark";
import { BucketQueue } from "../src";
import { MinBucketQueue } from "../src";
import HeapJS from "heap-js";
import Heap from "heap";
import { MinHeap as HeapDS } from "@datastructures-js/heap";
Expand All @@ -16,7 +16,7 @@ for (let i = 0; i < itemNumber; i++) {
items.push(i % priorityMax);
}

const bq = new BucketQueue<number>();
const bq = new MinBucketQueue<number>();
const heapjs = new HeapJS<number>((a, b) => a - b);
const heap = new Heap<number>((a, b) => a - b);
const heapds = new HeapDS<number>();
Expand All @@ -33,10 +33,10 @@ for (let i = 0; i < items.length; i++) {
}

suit
.add("BucketQueue", () => {
.add("MinBucketQueue", () => {
const items: number[] = [];
for (let i = 0; i < 1000; i++) {
items.push(bq.popLowest() as number);
items.push(bq.pop() as number);
}
for (let i = 999; i >= 0; i--) {
bq.push(items[i], items[i]);
Expand Down
12 changes: 6 additions & 6 deletions benchmark/popPush3.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Benchmark from "benchmark";
import { BucketQueue } from "../src";
import { MinBucketQueue } from "../src";
import HeapJS from "heap-js";
import Heap from "heap";
import { MinHeap as HeapDS } from "@datastructures-js/heap";
Expand All @@ -16,7 +16,7 @@ for (let i = 0; i < itemNumber; i++) {
items.push(i % priorityMax);
}

const bq = new BucketQueue<number>();
const bq = new MinBucketQueue<number>();
const heapjs = new HeapJS<number>((a, b) => a - b);
const heap = new Heap<number>((a, b) => a - b);
const heapds = new HeapDS<number>();
Expand All @@ -33,10 +33,10 @@ for (let i = 0; i < items.length; i++) {
}

suit
.add("BucketQueue", () => {
const i1 = bq.popLowest() as number;
const i2 = bq.popLowest() as number;
const i3 = bq.popLowest() as number;
.add("MinBucketQueue", () => {
const i1 = bq.pop() as number;
const i2 = bq.pop() as number;
const i3 = bq.pop() as number;

bq.push(i3, i3);
bq.push(i2, i2);
Expand Down
75 changes: 75 additions & 0 deletions benchmark/refill.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import Benchmark from "benchmark";
import { MinBucketQueue } from "../src";
import HeapJS from "heap-js";
import Heap from "heap";
import { MinHeap as HeapDS } from "@datastructures-js/heap";
import { PriorityQueue, MinPriorityQueue } from "priority-queue-typed";

const suit = new Benchmark.Suite();

const itemNumber = 1_000_000;
const priorityMax = 1000;

const items: number[] = [];
const itemsPrio: [number, number][] = [];

for (let i = 0; i < itemNumber; i++) {
items.push(i % priorityMax);
itemsPrio.push([i % priorityMax, i % priorityMax]);
}

const bq = new MinBucketQueue<number>();
const heapjs = new HeapJS<number>((a, b) => a - b);
const heap = new Heap<number>((a, b) => a - b);
const heapds = new HeapDS<number>();
const mpq = new MinPriorityQueue<number>();
const pq = new PriorityQueue<number>([], { comparator: (a, b) => a - b });

for (let i = 0; i < items.length; i++) {
bq.push(items[i], items[i]);
heapjs.push(items[i]);
heap.push(items[i]);
heapds.push(items[i]);
mpq.add(items[i]);
pq.add(items[i]);
}

suit
.add("MinBucketQueue", () => {
bq.clear();
for (let i = 0; i < items.length; i++) {
bq.push(items[i], items[i]);
}
})
.add("HeapJS", () => {
heapjs.clear();
for (let i = 0; i < items.length; i++) {
heapjs.push(items[i]);
}
})
.add("HeapDS", () => {
heapds.clear();
for (let i = 0; i < items.length; i++) {
heapds.push(items[i]);
}
})
.add("Heap", () => {
heap.clear();
for (let i = 0; i < items.length; i++) {
heap.push(items[i]);
}
})
.add("MinPriorityQueue", () => {
mpq.refill(items);
})
.add("PriorityQueue", () => {
pq.refill(items);
})
.on("cycle", function (e: Event) {
console.log("" + e.target);
})
.on("complete", function () {
// @ts-expect-error - no error expected
console.log("Fastest is " + this.filter("fastest").map("name"));
})
.run();
36 changes: 26 additions & 10 deletions dist/index.d.mts
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
type Integer = number;
type Priority = Integer;
declare class BucketQueue<T> {
private buckets;
private priorityMax;
private priorityMin;
private _size;

declare abstract class BucketQueue<T> {
protected buckets: T[][];
protected pointer: number;
protected _size: number;
constructor(items?: [T, Priority][]);
abstract push(item: T, priority: Priority): void;
abstract pop(): T | undefined;
add(item: T, priority: Priority): void;
poll(): T | undefined;
peek(): T | undefined;
clear(): void;
refill(items: [T, Priority][]): void;
has(item: T): boolean;
contains: (item: T) => boolean;
toArray(): T[];
get size(): number;
}

declare class MaxBucketQueue<T> extends BucketQueue<T> {
push(item: T, priority: Priority): void;
pop(): T | undefined;
}

declare class MinBucketQueue<T> extends BucketQueue<T> {
push(item: T, priority: Priority): void;
private pop;
popHighest(): T | undefined;
popLowest(): T | undefined;
get size(): Integer;
pop(): T | undefined;
}

export { BucketQueue };
export { type Integer, MaxBucketQueue, MinBucketQueue, type Priority };
36 changes: 26 additions & 10 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
type Integer = number;
type Priority = Integer;
declare class BucketQueue<T> {
private buckets;
private priorityMax;
private priorityMin;
private _size;

declare abstract class BucketQueue<T> {
protected buckets: T[][];
protected pointer: number;
protected _size: number;
constructor(items?: [T, Priority][]);
abstract push(item: T, priority: Priority): void;
abstract pop(): T | undefined;
add(item: T, priority: Priority): void;
poll(): T | undefined;
peek(): T | undefined;
clear(): void;
refill(items: [T, Priority][]): void;
has(item: T): boolean;
contains: (item: T) => boolean;
toArray(): T[];
get size(): number;
}

declare class MaxBucketQueue<T> extends BucketQueue<T> {
push(item: T, priority: Priority): void;
pop(): T | undefined;
}

declare class MinBucketQueue<T> extends BucketQueue<T> {
push(item: T, priority: Priority): void;
private pop;
popHighest(): T | undefined;
popLowest(): T | undefined;
get size(): Integer;
pop(): T | undefined;
}

export { BucketQueue };
export { type Integer, MaxBucketQueue, MinBucketQueue, type Priority };
2 changes: 1 addition & 1 deletion dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 91c1a3c

Please sign in to comment.