undirected-graph-typed
Version:
Undirected Graph
5,113 lines (497 loc) • 45 kB
TypeScript
/**
* data-structure-typed
*
* @author Pablo Zeng
* @copyright Copyright (c) 2022 Pablo Zeng
* @license MIT License
*/
import type { Comparator, TreeMultiMapOptions } from '../../types';
import { Range } from '../../common';
import { RedBlackTreeNode } from './red-black-tree';
/**
* TreeMultiMap (ordered MultiMap) — key → bucket (Array of values).
*
* Semantics (RFC):
* - Bucketed design: each key appears once; duplicates live in the bucket.
* - `get(key)` returns a **live** bucket reference.
* - Default iteration yields bucket entries: `[K, V[]]`.
* - Navigable operations (`first/last/ceiling/...`) return entry tuples like TreeMap.
* @example
* // Morris traversal (O(1) space)
* const tmm = new TreeMultiMap<number>([5, 3, 7]);
* const result = tmm.morris(n => n.key, 'IN');
* console.log(result.length); // > 0;
*/
export declare class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]]> {
#private;
/**
* Creates a new TreeMultiMap.
* @param keysNodesEntriesOrRaws - Initial entries, or raw elements if `toEntryFn` is provided.
* @param options - Configuration options including optional `toEntryFn` to transform raw elements.
* @remarks Time O(m log m), Space O(m) where m is the number of initial entries
* @example
* // Standard usage with entries
* const mmap = new TreeMultiMap([['a', ['x', 'y']], ['b', ['z']]]);
*
* // Using toEntryFn to transform raw objects
* const players = [{ score: 100, items: ['sword'] }, { score: 200, items: ['shield', 'bow'] }];
* const mmap = new TreeMultiMap(players, { toEntryFn: p => [p.score, p.items] });
*/
constructor(keysNodesEntriesOrRaws?: Iterable<K | [K | null | undefined, V[] | undefined] | null | undefined | R>, options?: TreeMultiMapOptions<K, V[], R>);
/**
* Validates the key against the default comparator rules.
* @remarks Time O(1), Space O(1)
*/
private _validateKey;
/**
* Number of distinct keys.
* @remarks Time O(1), Space O(1)
*/
get size(): number;
/**
* Whether the map is empty.
* @remarks Time O(1), Space O(1)
* @example
* // Check if empty
* console.log(new TreeMultiMap().isEmpty()); // true;
*/
isEmpty(): boolean;
/**
* Removes all entries from the map.
* @remarks Time O(1), Space O(1)
* @example
* // Remove all entries
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.clear();
* console.log(mm.isEmpty()); // true;
*/
clear(): void;
/**
* Bucket length for a key (missing => 0).
* @remarks Time O(log n), Space O(1)
* @example
* // Count values for key
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(1, 'b');
* console.log(mm.count(1)); // 2;
*/
count(key: K): number;
/**
* Total number of values across all buckets (Σ bucket.length).
* @remarks Time O(n), Space O(1)
* @example
* // Total number of values
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(1, 'b');
* mm.add(2, 'c');
* console.log(mm.totalSize); // 3;
*/
get totalSize(): number;
/**
* Whether the map contains the given key.
* @remarks Time O(log n), Space O(1)
* @example
* // Check key existence
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* console.log(mm.has(1)); // true;
* console.log(mm.has(2)); // false;
*/
has(key: K): boolean;
/**
* Live bucket reference (do not auto-delete key if bucket becomes empty via mutation).
* @remarks Time O(log n), Space O(1)
* @example
* // Get values for key
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(1, 'b');
* console.log(mm.get(1)); // ['a', 'b'];
*/
get(key: K): V[] | undefined;
/**
* Append a single value.
* @remarks Time O(log n), Space O(1)
* @example
* // Add key-value pair
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(1, 'b');
* mm.add(2, 'c');
* console.log(mm.get(1)); // ['a', 'b'];
*/
add(key: K, value: V): boolean;
/**
* Alias for compatibility with existing TreeMultiMap semantics.
* @remarks Time O(log n), Space O(1) for single value; O(log n + m) for bucket append
* @example
* // Set values for key
* const mm = new TreeMultiMap<number, string>();
* mm.set(1, 'a');
* mm.set(1, 'b');
* console.log(mm.get(1)); // ['a', 'b'];
*/
set(entry: [K | null | undefined, V[] | undefined] | K | null | undefined, value?: V): boolean;
set(key: K, value: V): boolean;
/**
* Deletes a key and its entire bucket.
* @remarks Time O(log n), Space O(1)
* @example
* // Remove key
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(2, 'b');
* mm.delete(1);
* console.log(mm.has(1)); // false;
*/
delete(key: K): boolean;
/**
* Check if a specific value exists in a key's bucket.
* @remarks Time O(log n + m), Space O(1) where m is bucket size
* @example
* // Check specific key-value
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* console.log(mm.hasEntry(1, 'a')); // true;
* console.log(mm.hasEntry(1, 'z')); // false;
*/
hasEntry(key: K, value: V, eq?: (a: V, b: V) => boolean): boolean;
/**
* Delete a single occurrence of a value from a key's bucket.
* @remarks Time O(log n + m), Space O(1) where m is bucket size
* @example
* // Delete specific value
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(1, 'b');
* mm.deleteValue(1, 'a');
* console.log(mm.get(1)); // ['b'];
*/
deleteValue(key: K, value: V, eq?: (a: V, b: V) => boolean): boolean;
/**
* Delete all occurrences of a value from a key's bucket.
* @remarks Time O(log n + m), Space O(1) where m is bucket size
* @example
* // Delete all matching values
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(1, 'a');
* mm.add(1, 'b');
* const count = mm.deleteValues(1, 'a');
* console.log(count); // 2;
*/
deleteValues(key: K, value: V, eq?: (a: V, b: V) => boolean): number;
/**
* Iterates over all entries as [key, bucket] pairs.
* @remarks Time O(n), Space O(1)
*/
[Symbol.iterator](): Iterator<[K, V[]]>;
/**
* Iterates over all keys.
* @remarks Time O(n), Space O(1)
* @example
* // Iterate keys
* const mm = new TreeMultiMap<number, string>();
* mm.add(3, 'c');
* mm.add(1, 'a');
* console.log([...mm.keys()]); // [1, 3];
*/
keys(): IterableIterator<K>;
/**
* Iterates over all buckets.
* @remarks Time O(n), Space O(1)
* @example
* // Iterate value arrays
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(1, 'b');
* console.log([...mm.values()]); // [['a', 'b']];
*/
values(): IterableIterator<V[]>;
/**
* Iterate over all `[key, values[]]` entries (Map-compatible).
* @remarks Time O(n), Space O(1) per step.
* @example
* // Iterate over entries
* const mm = new TreeMultiMap<number, string>();
* mm.set(1, 'a');
* mm.set(1, 'b');
* mm.set(2, 'c');
* console.log([...mm.entries()]); // [
* // [1, ['a', 'b']],
* // [2, ['c']]
* // ];
*/
entries(): IterableIterator<[K, V[]]>;
/**
* Iterates over all entries for a specific key.
* @remarks Time O(log n + m), Space O(1) where m is bucket size
* @example
* // Get entries for key
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(1, 'b');
* console.log([...mm.entriesOf(1)]); // [[1, 'a'], [1, 'b']];
*/
entriesOf(key: K): IterableIterator<[K, V]>;
/**
* Iterates over all values for a specific key.
* @remarks Time O(log n + m), Space O(1) where m is bucket size
* @example
* // Get flat values for key
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(1, 'b');
* console.log([...mm.valuesOf(1)]); // ['a', 'b'];
*/
valuesOf(key: K): IterableIterator<V>;
/**
* Iterates over all [key, value] pairs (flattened from buckets).
* @remarks Time O(T), Space O(1) where T is totalSize
* @example
* // All key-value pairs flattened
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(1, 'b');
* mm.add(2, 'c');
* console.log([...mm.flatEntries()]); // [[1, 'a'], [1, 'b'], [2, 'c']];
*/
flatEntries(): IterableIterator<[K, V]>;
/**
* Returns the entry with the smallest key.
* @remarks Time O(log n), Space O(1)
* @example
* // First entry
* const mm = new TreeMultiMap<number, string>();
* mm.add(3, 'c');
* mm.add(1, 'a');
* console.log(mm.first()?.[0]); // 1;
*/
first(): [K, V[]] | undefined;
/**
* Returns the entry with the largest key.
* @remarks Time O(log n), Space O(1)
* @example
* // Last entry
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(3, 'c');
* console.log(mm.last()?.[0]); // 3;
*/
last(): [K, V[]] | undefined;
/**
* Removes and returns the entry with the smallest key.
* @remarks Time O(log n), Space O(1)
* @example
* // Remove and return first
* const mm = new TreeMultiMap<number, string>();
* mm.add(2, 'b');
* mm.add(1, 'a');
* const first = mm.pollFirst();
* console.log(first?.[0]); // 1;
* console.log(mm.has(1)); // false;
*/
pollFirst(): [K, V[]] | undefined;
/**
* Removes and returns the entry with the largest key.
* @remarks Time O(log n), Space O(1)
* @example
* // Remove and return last
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(3, 'c');
* const last = mm.pollLast();
* console.log(last?.[0]); // 3;
*/
pollLast(): [K, V[]] | undefined;
/**
* Returns the entry with the smallest key >= given key.
* @remarks Time O(log n), Space O(1)
* @example
* // Least key ≥ target
* const mm = new TreeMultiMap<number, string>();
* mm.add(10, 'a');
* mm.add(20, 'b');
* mm.add(30, 'c');
* console.log(mm.ceiling(15)?.[0]); // 20;
*/
ceiling(key: K): [K, V[]] | undefined;
/**
* Returns the entry with the largest key <= given key.
* @remarks Time O(log n), Space O(1)
* @example
* // Greatest key ≤ target
* const mm = new TreeMultiMap<number, string>();
* mm.add(10, 'a');
* mm.add(20, 'b');
* mm.add(30, 'c');
* console.log(mm.floor(25)?.[0]); // 20;
*/
floor(key: K): [K, V[]] | undefined;
/**
* Returns the entry with the smallest key > given key.
* @remarks Time O(log n), Space O(1)
* @example
* // Least key > target
* const mm = new TreeMultiMap<number, string>();
* mm.add(10, 'a');
* mm.add(20, 'b');
* console.log(mm.higher(10)?.[0]); // 20;
*/
higher(key: K): [K, V[]] | undefined;
/**
* Returns the entry with the largest key < given key.
* @remarks Time O(log n), Space O(1)
* @example
* // Greatest key < target
* const mm = new TreeMultiMap<number, string>();
* mm.add(10, 'a');
* mm.add(20, 'b');
* console.log(mm.lower(20)?.[0]); // 10;
*/
lower(key: K): [K, V[]] | undefined;
/**
* Prints the internal tree structure (for debugging).
* @remarks Time O(n), Space O(n)
* @example
* // Display tree
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* expect(() => mm.print()).not.toThrow();
*/
print(): void;
/**
* Executes a callback for each entry.
* @remarks Time O(n), Space O(1)
* @example
* // Iterate entries
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(2, 'b');
* const keys: number[] = [];
* mm.forEach((v, k) => keys.push(k));
* console.log(keys); // [1, 2];
*/
forEach(callback: (value: V[], key: K, map: this) => void): void;
/**
* Creates a new map with entries that pass the predicate.
* @remarks Time O(n), Space O(n)
* @example
* // Filter entries
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* mm.add(2, 'b');
* mm.add(3, 'c');
* const filtered = mm.filter((v, k) => k > 1);
* console.log([...filtered.keys()]); // [2, 3];
*/
filter(predicate: (value: V[], key: K, map: this) => boolean): TreeMultiMap<K, V, R>;
/**
* Creates a new map by transforming each entry.
* @remarks Time O(n log n), Space O(n)
* @example
* // Transform values
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* const mapped = mm.map((v, k) => [k, v.map(s => s.toUpperCase())] as [number, string[]]);
* console.log(mapped.get(1)); // ['A'];
*/
map<V2>(mapper: (value: V[], key: K, map: this) => [K, V2[]]): TreeMultiMap<K, V2, R>;
/**
* Reduces all entries to a single value.
* @remarks Time O(n), Space O(1)
* @example
* // Aggregate
* const mm = new TreeMultiMap<number, number>();
* mm.add(1, 10);
* mm.add(2, 20);
* const sum = mm.reduce((acc, v) => acc + v.reduce((a, b) => a + b, 0), 0);
* console.log(sum); // 30;
*/
reduce<U>(callback: (accumulator: U, value: V[], key: K, map: this) => U, initialValue: U): U;
/**
* Sets multiple entries at once.
* @remarks Time O(m log n), Space O(m) where m is input size
* @example
* // Set multiple entries
* const mm = new TreeMultiMap<number, string>();
* mm.setMany([[1, ['a']], [2, ['b']]]);
* console.log(mm.size); // 2;
*/
setMany(keysNodesEntriesOrRaws: Iterable<K | [K | null | undefined, V[] | undefined]>): boolean[];
/**
* Searches for entries within a key range.
* @remarks Time O(log n + k), Space O(k) where k is result size
* @example
* // Find keys in range
* const mm = new TreeMultiMap<number, string>();
* mm.add(10, 'a');
* mm.add(20, 'b');
* mm.add(30, 'c');
* const result = mm.rangeSearch([15, 25]);
* console.log(result.length); // 1;
*/
rangeSearch<C extends (node: RedBlackTreeNode<K, V[]>) => unknown>(range: Range<K> | [K, K], callback?: C): ReturnType<C>[];
/**
* Creates a shallow clone of this map.
* @remarks Time O(n log n), Space O(n)
* @example
* // Order-statistic on BST
* const tree = new TreeMultiMap<number>([30, 10, 50, 20, 40], { enableOrderStatistic: true });
* console.log(tree.getByRank(0)); // 10;
* console.log(tree.getByRank(4)); // 50;
* console.log(tree.getRank(30)); // 2;
*/
getByRank(k: number): [K, V[]] | undefined;
/**
* Get the rank of a key in sorted order
* @example
* // Get the rank of a key in sorted order
* const tree = new TreeMultiMap<number>(
* [10, 20, 30, 40, 50],
* { enableOrderStatistic: true }
* );
* console.log(tree.getRank(10)); // 0; // smallest → rank 0
* console.log(tree.getRank(30)); // 2; // 2 elements before 30 in tree order
* console.log(tree.getRank(50)); // 4; // largest → rank 4
* console.log(tree.getRank(25)); // 2;
*/
getRank(key: K): number;
/**
* Get elements by rank range
* @example
* // Pagination by position in tree order
* const tree = new TreeMultiMap<number>(
* [10, 20, 30, 40, 50, 60, 70, 80, 90],
* { enableOrderStatistic: true }
* );
* const pageSize = 3;
*
* // Page 1
* console.log(tree.rangeByRank(0, pageSize - 1)); // [10, 20, 30];
* // Page 2
* console.log(tree.rangeByRank(pageSize, 2 * pageSize - 1)); // [40, 50, 60];
* // Page 3
* console.log(tree.rangeByRank(2 * pageSize, 3 * pageSize - 1)); // [70, 80, 90];
*/
rangeByRank(start: number, end: number): Array<[K, V[]]>;
/**
* Deep copy
* @example
* // Deep clone
* const mm = new TreeMultiMap<number, string>();
* mm.add(1, 'a');
* const copy = mm.clone();
* copy.delete(1);
* console.log(mm.has(1)); // true;
*/
clone(): TreeMultiMap<K, V, R>;
/**
* Expose comparator for advanced usage/testing (read-only).
* @remarks Time O(1), Space O(1)
*/
get comparator(): Comparator<K>;
}