d-ary-heap 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +218 -0
- package/dist/index.cjs +519 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +388 -0
- package/dist/index.d.ts +388 -0
- package/dist/index.js +509 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
- package/src/PriorityQueue.ts +599 -0
- package/src/comparators.ts +106 -0
- package/src/index.ts +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+

|
|
2
|
+

|
|
3
|
+
|
|
4
|
+
# d-Heap Priority Queue (TypeScript) v2.1.1
|
|
5
|
+
|
|
6
|
+
A high-performance, generic d-ary heap priority queue with O(1) item lookup, supporting both min-heap and max-heap behavior.
|
|
7
|
+
|
|
8
|
+
## Strengths
|
|
9
|
+
|
|
10
|
+
- **Flexible behavior**: min-heap or max-heap via comparator functions, and configurable arity `d` at construction time.
|
|
11
|
+
- **Efficient operations** on n items:
|
|
12
|
+
- O(1): access the highest-priority item (`front()`, `peek()`).
|
|
13
|
+
- O(log_d n): `insert()` and upward reheapification.
|
|
14
|
+
- O(d · log_d n): delete-top (`pop()`), with up to d children examined per level.
|
|
15
|
+
- **O(1) item lookup**: internal Map tracks positions by item key, enabling efficient priority updates.
|
|
16
|
+
- **Practical API**: `insert`, `front`, `peek`, `pop`, `increasePriority`, `decreasePriority`, `isEmpty`, `len`, `contains`.
|
|
17
|
+
- **Unified API**: Cross-language standardized methods matching C++, Rust, and Zig implementations.
|
|
18
|
+
- **TypeScript-native**: Full type safety, generics, and IDE support.
|
|
19
|
+
- **Zero dependencies**: No runtime dependencies.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install d-ary-heap
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { PriorityQueue, minBy, maxBy } from 'd-ary-heap';
|
|
31
|
+
|
|
32
|
+
// Define your item type
|
|
33
|
+
interface Task {
|
|
34
|
+
id: number;
|
|
35
|
+
priority: number;
|
|
36
|
+
name: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Create a min-heap (lower priority value = higher importance)
|
|
40
|
+
const pq = new PriorityQueue<Task, number>({
|
|
41
|
+
d: 4, // 4-ary heap
|
|
42
|
+
comparator: minBy(task => task.priority), // Min-heap by priority
|
|
43
|
+
keyExtractor: task => task.id, // Identity by id
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Insert items
|
|
47
|
+
pq.insert({ id: 1, priority: 10, name: 'Low priority task' });
|
|
48
|
+
pq.insert({ id: 2, priority: 1, name: 'High priority task' });
|
|
49
|
+
pq.insert({ id: 3, priority: 5, name: 'Medium priority task' });
|
|
50
|
+
|
|
51
|
+
// Get highest priority item (lowest priority value in min-heap)
|
|
52
|
+
console.log(pq.front()); // { id: 2, priority: 1, name: 'High priority task' }
|
|
53
|
+
|
|
54
|
+
// Update priority of existing item
|
|
55
|
+
pq.increasePriority({ id: 1, priority: 0, name: 'Now urgent!' });
|
|
56
|
+
console.log(pq.front()); // { id: 1, priority: 0, name: 'Now urgent!' }
|
|
57
|
+
|
|
58
|
+
// Remove items in priority order
|
|
59
|
+
while (!pq.isEmpty()) {
|
|
60
|
+
console.log(pq.pop());
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## API Reference
|
|
65
|
+
|
|
66
|
+
### Constructor
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
new PriorityQueue<T, K>(options: PriorityQueueOptions<T, K>)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Options:
|
|
73
|
+
- `d`: Number of children per node (arity). Default: 2. Must be >= 1.
|
|
74
|
+
- `comparator`: Function `(a: T, b: T) => boolean` returning true if `a` has higher priority.
|
|
75
|
+
- `keyExtractor`: Function `(item: T) => K` extracting the identity key from an item.
|
|
76
|
+
- `initialCapacity`: Optional hint for pre-allocation.
|
|
77
|
+
|
|
78
|
+
### Static Factory Methods
|
|
79
|
+
|
|
80
|
+
| Method | Description |
|
|
81
|
+
|--------|-------------|
|
|
82
|
+
| `PriorityQueue.withFirst(options, item)` | Create queue with initial item |
|
|
83
|
+
|
|
84
|
+
### Query Methods
|
|
85
|
+
|
|
86
|
+
| Method | Description | Time |
|
|
87
|
+
|--------|-------------|------|
|
|
88
|
+
| `len()` | Number of items | O(1) |
|
|
89
|
+
| `size` | Property alias for `len()` | O(1) |
|
|
90
|
+
| `isEmpty()` / `is_empty()` | Check if empty | O(1) |
|
|
91
|
+
| `d()` | Get arity | O(1) |
|
|
92
|
+
| `contains(item)` | Check if item exists (by key) | O(1) |
|
|
93
|
+
| `containsKey(key)` | Check if key exists | O(1) |
|
|
94
|
+
| `getPosition(item)` | Get item's index in heap | O(1) |
|
|
95
|
+
| `getPositionByKey(key)` | Get index by key | O(1) |
|
|
96
|
+
| `front()` | Get highest-priority item (throws if empty) | O(1) |
|
|
97
|
+
| `peek()` | Get highest-priority item (returns undefined if empty) | O(1) |
|
|
98
|
+
|
|
99
|
+
### Modification Methods
|
|
100
|
+
|
|
101
|
+
| Method | Description | Time |
|
|
102
|
+
|--------|-------------|------|
|
|
103
|
+
| `insert(item)` | Add new item | O(log_d n) |
|
|
104
|
+
| `pop()` | Remove and return highest-priority item | O(d · log_d n) |
|
|
105
|
+
| `increasePriority(item)` / `increase_priority(item)` | Update item to higher priority | O(log_d n) |
|
|
106
|
+
| `increasePriorityByIndex(i)` / `increase_priority_by_index(i)` | Update by index | O(log_d n) |
|
|
107
|
+
| `decreasePriority(item)` / `decrease_priority(item)` | Update item to lower priority | O(d · log_d n) |
|
|
108
|
+
| `clear(newD?)` | Remove all items, optionally change arity | O(1) |
|
|
109
|
+
|
|
110
|
+
### Utility Methods
|
|
111
|
+
|
|
112
|
+
| Method | Description |
|
|
113
|
+
|--------|-------------|
|
|
114
|
+
| `toString()` / `to_string()` | String representation |
|
|
115
|
+
| `toArray()` | Copy of internal array |
|
|
116
|
+
| `[Symbol.iterator]()` | Iterate over items in heap order |
|
|
117
|
+
|
|
118
|
+
## Comparator Helpers
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { minBy, maxBy, minNumber, maxNumber, reverse, chain } from 'd-ary-heap';
|
|
122
|
+
|
|
123
|
+
// Min-heap by extracted key
|
|
124
|
+
const minByCost = minBy<Item, number>(item => item.cost);
|
|
125
|
+
|
|
126
|
+
// Max-heap by extracted key
|
|
127
|
+
const maxByCost = maxBy<Item, number>(item => item.cost);
|
|
128
|
+
|
|
129
|
+
// For primitive numbers
|
|
130
|
+
const minHeap = minNumber; // (a, b) => a < b
|
|
131
|
+
const maxHeap = maxNumber; // (a, b) => a > b
|
|
132
|
+
|
|
133
|
+
// Reverse any comparator
|
|
134
|
+
const reversed = reverse(minByCost);
|
|
135
|
+
|
|
136
|
+
// Chain comparators (tiebreaker)
|
|
137
|
+
const byPriorityThenTime = chain(
|
|
138
|
+
minBy<Task, number>(t => t.priority),
|
|
139
|
+
minBy<Task, number>(t => t.timestamp)
|
|
140
|
+
);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Priority Update Semantics
|
|
144
|
+
|
|
145
|
+
The `increasePriority()` and `decreasePriority()` methods have an intentionally **asymmetric design**:
|
|
146
|
+
|
|
147
|
+
- **`increasePriority()`**: Make an item **more important** (moves toward heap root). Only moves up for O(log_d n) performance.
|
|
148
|
+
- **`decreasePriority()`**: Make an item **less important** (moves toward leaves). Checks both directions for robustness.
|
|
149
|
+
|
|
150
|
+
**Heap Context:**
|
|
151
|
+
- **Min-heap**: Lower priority values = higher importance
|
|
152
|
+
- **Max-heap**: Higher priority values = higher importance
|
|
153
|
+
|
|
154
|
+
## Performance
|
|
155
|
+
|
|
156
|
+
### Benchmark Results (Node.js v20, typical hardware)
|
|
157
|
+
|
|
158
|
+
| Operation | d=2 | d=4 | d=8 |
|
|
159
|
+
|-----------|-----|-----|-----|
|
|
160
|
+
| 100k inserts | ~21ms | ~13ms | ~11ms |
|
|
161
|
+
| 100k pops | ~187ms | ~136ms | ~140ms |
|
|
162
|
+
| Throughput (insert) | ~2.2M ops/sec | | |
|
|
163
|
+
| Throughput (pop+insert) | ~900k ops/sec | | |
|
|
164
|
+
|
|
165
|
+
### Performance Tips
|
|
166
|
+
|
|
167
|
+
1. **Choose arity wisely**: d=4 is often optimal, balancing tree height vs comparison work per level.
|
|
168
|
+
|
|
169
|
+
2. **Use inline comparators**: `(a, b) => a.cost < b.cost` is faster than `minBy(x => x.cost)` in hot paths.
|
|
170
|
+
|
|
171
|
+
3. **Pre-allocate**: Use `initialCapacity` if you know the approximate size.
|
|
172
|
+
|
|
173
|
+
4. **Simple key extractors**: Keep `keyExtractor` simple—`(item) => item.id` is ideal.
|
|
174
|
+
|
|
175
|
+
5. **Avoid unnecessary updates**: Check if priority actually changed before calling update methods.
|
|
176
|
+
|
|
177
|
+
### Running Benchmarks
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# Full benchmark suite
|
|
181
|
+
npx tsx benchmarks/run-benchmarks.ts
|
|
182
|
+
|
|
183
|
+
# Compare with naive implementation
|
|
184
|
+
npx tsx benchmarks/compare-implementations.ts
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## What is a d-Heap?
|
|
188
|
+
|
|
189
|
+
A [d-ary heap](https://en.wikipedia.org/wiki/D-ary_heap) is a tree where:
|
|
190
|
+
- Each node has up to d children
|
|
191
|
+
- The root holds the highest priority
|
|
192
|
+
- Children are unordered
|
|
193
|
+
- Priorities decrease along any root-to-leaf path
|
|
194
|
+
|
|
195
|
+
Time complexities over n items:
|
|
196
|
+
- O(1): access top
|
|
197
|
+
- O(d · log_d n): delete-top
|
|
198
|
+
- O(log_d n): insert and upward update
|
|
199
|
+
|
|
200
|
+
## Development
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Install dependencies
|
|
204
|
+
npm install
|
|
205
|
+
|
|
206
|
+
# Run tests
|
|
207
|
+
npm test
|
|
208
|
+
|
|
209
|
+
# Build
|
|
210
|
+
npm run build
|
|
211
|
+
|
|
212
|
+
# Type check
|
|
213
|
+
npm run typecheck
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Reference
|
|
217
|
+
|
|
218
|
+
Section A.3, [d-Heaps](https://en.wikipedia.org/wiki/D-ary_heap), pp. 773–778 of Ravindra Ahuja, Thomas Magnanti & James Orlin, **Network Flows** (Prentice Hall, 1993).
|