@no-k/hermes 0.1.0
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/LICENSE +15 -0
- package/README.md +88 -0
- package/bin/hermes.mjs +159 -0
- package/catalog.json +1084 -0
- package/lib/catalog.mjs +51 -0
- package/package.json +28 -0
- package/templates/algorithm/geometry/ccw/ccw.ts +25 -0
- package/templates/algorithm/geometry/ccw/index.ts +1 -0
- package/templates/algorithm/geometry/convex-hull/convex-hull.ts +43 -0
- package/templates/algorithm/geometry/convex-hull/index.ts +1 -0
- package/templates/algorithm/geometry/polygon-area/index.ts +1 -0
- package/templates/algorithm/geometry/polygon-area/polygon-area.ts +13 -0
- package/templates/algorithm/graph/bellman-ford/bellman-ford.ts +46 -0
- package/templates/algorithm/graph/bellman-ford/index.ts +1 -0
- package/templates/algorithm/graph/bfs/bfs.ts +21 -0
- package/templates/algorithm/graph/bfs/index.ts +1 -0
- package/templates/algorithm/graph/dfs/dfs.ts +22 -0
- package/templates/algorithm/graph/dfs/index.ts +1 -0
- package/templates/algorithm/graph/dijkstra/dijkstra.ts +37 -0
- package/templates/algorithm/graph/dijkstra/index.ts +1 -0
- package/templates/algorithm/graph/floyd-warshall/floyd-warshall.ts +17 -0
- package/templates/algorithm/graph/floyd-warshall/index.ts +1 -0
- package/templates/algorithm/graph/kruskal/index.ts +1 -0
- package/templates/algorithm/graph/kruskal/kruskal.ts +29 -0
- package/templates/algorithm/graph/prim/index.ts +1 -0
- package/templates/algorithm/graph/prim/prim.ts +44 -0
- package/templates/algorithm/graph/topology-sort/index.ts +1 -0
- package/templates/algorithm/graph/topology-sort/topology-sort.ts +31 -0
- package/templates/algorithm/search/binary-search/binary-search.ts +74 -0
- package/templates/algorithm/search/binary-search/index.ts +1 -0
- package/templates/algorithm/string/kmp/index.ts +1 -0
- package/templates/algorithm/string/kmp/kmp.ts +46 -0
- package/templates/algorithm/string/z-algorithm/index.ts +1 -0
- package/templates/algorithm/string/z-algorithm/z-algorithm.ts +41 -0
- package/templates/algorithm/tree/avl-tree/avl-tree.ts +197 -0
- package/templates/algorithm/tree/avl-tree/index.ts +1 -0
- package/templates/algorithm/tree/binary-search-tree/binary-search-tree.ts +144 -0
- package/templates/algorithm/tree/binary-search-tree/index.ts +1 -0
- package/templates/algorithm/tree/lca/index.ts +1 -0
- package/templates/algorithm/tree/lca/lca.ts +119 -0
- package/templates/array/chunk/chunk.ts +13 -0
- package/templates/array/chunk/index.ts +1 -0
- package/templates/array/compact/compact.ts +8 -0
- package/templates/array/compact/index.ts +1 -0
- package/templates/array/count-by/count-by.ts +12 -0
- package/templates/array/count-by/index.ts +1 -0
- package/templates/array/difference/difference.ts +6 -0
- package/templates/array/difference/index.ts +1 -0
- package/templates/array/drop/drop.ts +7 -0
- package/templates/array/drop/index.ts +1 -0
- package/templates/array/group-by/group-by.ts +18 -0
- package/templates/array/group-by/index.ts +1 -0
- package/templates/array/intersection/index.ts +1 -0
- package/templates/array/intersection/intersection.ts +15 -0
- package/templates/array/key-by/index.ts +1 -0
- package/templates/array/key-by/key-by.ts +11 -0
- package/templates/array/max-by/index.ts +1 -0
- package/templates/array/max-by/max-by.ts +20 -0
- package/templates/array/min-by/index.ts +1 -0
- package/templates/array/min-by/min-by.ts +20 -0
- package/templates/array/partition/index.ts +1 -0
- package/templates/array/partition/partition.ts +16 -0
- package/templates/array/range/index.ts +1 -0
- package/templates/array/range/range.ts +22 -0
- package/templates/array/sample/index.ts +1 -0
- package/templates/array/sample/sample.ts +10 -0
- package/templates/array/shuffle/index.ts +1 -0
- package/templates/array/shuffle/shuffle.ts +15 -0
- package/templates/array/sort-by/index.ts +1 -0
- package/templates/array/sort-by/sort-by.ts +32 -0
- package/templates/array/sum-by/index.ts +1 -0
- package/templates/array/sum-by/sum-by.ts +9 -0
- package/templates/array/take/index.ts +1 -0
- package/templates/array/take/take.ts +7 -0
- package/templates/array/union/index.ts +1 -0
- package/templates/array/union/union.ts +5 -0
- package/templates/array/unique/index.ts +1 -0
- package/templates/array/unique/unique.ts +5 -0
- package/templates/array/unique-by/index.ts +1 -0
- package/templates/array/unique-by/unique-by.ts +19 -0
- package/templates/array/windowed/index.ts +1 -0
- package/templates/array/windowed/windowed.ts +15 -0
- package/templates/array/zip/index.ts +1 -0
- package/templates/array/zip/zip.ts +11 -0
- package/templates/boolean/parse-boolean/index.ts +1 -0
- package/templates/boolean/parse-boolean/parse-boolean.ts +17 -0
- package/templates/boolean/toggle/index.ts +1 -0
- package/templates/boolean/toggle/toggle.ts +2 -0
- package/templates/boolean/xor/index.ts +1 -0
- package/templates/boolean/xor/xor.ts +2 -0
- package/templates/data-structure/deque/deque.ts +79 -0
- package/templates/data-structure/deque/index.ts +1 -0
- package/templates/data-structure/fenwick-tree/fenwick-tree.ts +76 -0
- package/templates/data-structure/fenwick-tree/index.ts +1 -0
- package/templates/data-structure/heap/heap.ts +88 -0
- package/templates/data-structure/heap/index.ts +3 -0
- package/templates/data-structure/heap/max-heap.ts +7 -0
- package/templates/data-structure/heap/min-heap.ts +7 -0
- package/templates/data-structure/priority-queue/index.ts +1 -0
- package/templates/data-structure/priority-queue/priority-queue.ts +44 -0
- package/templates/data-structure/queue/index.ts +1 -0
- package/templates/data-structure/queue/queue.ts +66 -0
- package/templates/data-structure/segment-tree/index.ts +1 -0
- package/templates/data-structure/segment-tree/segment-tree.ts +79 -0
- package/templates/data-structure/trie/index.ts +1 -0
- package/templates/data-structure/trie/trie.ts +104 -0
- package/templates/data-structure/union-find/index.ts +1 -0
- package/templates/data-structure/union-find/union-find.ts +79 -0
- package/templates/dom/attribute/attribute.ts +24 -0
- package/templates/dom/attribute/index.ts +1 -0
- package/templates/dom/class-name/class-name.ts +48 -0
- package/templates/dom/class-name/index.ts +1 -0
- package/templates/dom/is-element/index.ts +1 -0
- package/templates/dom/is-element/is-element.ts +20 -0
- package/templates/dom/query/index.ts +1 -0
- package/templates/dom/query/query.ts +7 -0
- package/templates/http/content-type/content-type.ts +130 -0
- package/templates/http/content-type/index.ts +1 -0
- package/templates/http/header/header.ts +56 -0
- package/templates/http/header/index.ts +1 -0
- package/templates/http/method/index.ts +1 -0
- package/templates/http/method/method.ts +25 -0
- package/templates/http/status/index.ts +1 -0
- package/templates/http/status/status.ts +191 -0
- package/templates/number/clamp/clamp.ts +11 -0
- package/templates/number/clamp/index.ts +1 -0
- package/templates/number/factorial/factorial.ts +15 -0
- package/templates/number/factorial/index.ts +1 -0
- package/templates/number/format-thousands/format-thousands.ts +24 -0
- package/templates/number/format-thousands/index.ts +1 -0
- package/templates/number/gcd/gcd.ts +13 -0
- package/templates/number/gcd/index.ts +1 -0
- package/templates/number/in-range/in-range.ts +3 -0
- package/templates/number/in-range/index.ts +1 -0
- package/templates/number/is-even/index.ts +1 -0
- package/templates/number/is-even/is-even.ts +5 -0
- package/templates/number/is-odd/index.ts +1 -0
- package/templates/number/is-odd/is-odd.ts +7 -0
- package/templates/number/lcm/index.ts +1 -0
- package/templates/number/lcm/lcm.ts +12 -0
- package/templates/number/lerp/index.ts +1 -0
- package/templates/number/lerp/lerp.ts +3 -0
- package/templates/number/percentage/index.ts +1 -0
- package/templates/number/percentage/percentage.ts +7 -0
- package/templates/number/random/index.ts +1 -0
- package/templates/number/random/random.ts +9 -0
- package/templates/number/random-int/index.ts +1 -0
- package/templates/number/random-int/random-int.ts +9 -0
- package/templates/number/round/index.ts +1 -0
- package/templates/number/round/round.ts +22 -0
- package/templates/number/safe-parse-number/index.ts +1 -0
- package/templates/number/safe-parse-number/safe-parse-number.ts +29 -0
- package/templates/number/to-ordinal/index.ts +1 -0
- package/templates/number/to-ordinal/to-ordinal.ts +28 -0
- package/templates/object/deep-clone/deep-clone.ts +47 -0
- package/templates/object/deep-clone/index.ts +1 -0
- package/templates/object/deep-equal/deep-equal.ts +66 -0
- package/templates/object/deep-equal/index.ts +1 -0
- package/templates/object/deep-merge/deep-merge.ts +49 -0
- package/templates/object/deep-merge/index.ts +1 -0
- package/templates/object/defaults/defaults.ts +14 -0
- package/templates/object/defaults/index.ts +1 -0
- package/templates/object/filter-object/filter-object.ts +11 -0
- package/templates/object/filter-object/index.ts +1 -0
- package/templates/object/get/get.ts +75 -0
- package/templates/object/get/index.ts +1 -0
- package/templates/object/has/has.ts +60 -0
- package/templates/object/has/index.ts +1 -0
- package/templates/object/invert/index.ts +1 -0
- package/templates/object/invert/invert.ts +13 -0
- package/templates/object/map-keys/index.ts +1 -0
- package/templates/object/map-keys/map-keys.ts +16 -0
- package/templates/object/map-values/index.ts +1 -0
- package/templates/object/map-values/map-values.ts +14 -0
- package/templates/object/omit/index.ts +1 -0
- package/templates/object/omit/omit.ts +15 -0
- package/templates/object/omit-by/index.ts +1 -0
- package/templates/object/omit-by/omit-by.ts +19 -0
- package/templates/object/pick/index.ts +1 -0
- package/templates/object/pick/pick.ts +15 -0
- package/templates/object/pick-by/index.ts +1 -0
- package/templates/object/pick-by/pick-by.ts +19 -0
- package/templates/object/set/index.ts +1 -0
- package/templates/object/set/set.ts +118 -0
- package/templates/string/capitalize/capitalize.ts +4 -0
- package/templates/string/capitalize/index.ts +1 -0
- package/templates/string/change-case/change-case.ts +98 -0
- package/templates/string/change-case/index.ts +1 -0
- package/templates/string/char/char.ts +14 -0
- package/templates/string/char/index.ts +1 -0
- package/templates/string/count-occurrences/count-occurrences.ts +20 -0
- package/templates/string/count-occurrences/index.ts +1 -0
- package/templates/string/escape-html/escape-html.ts +16 -0
- package/templates/string/escape-html/index.ts +1 -0
- package/templates/string/get-decorated-string/get-decorated-string.ts +7 -0
- package/templates/string/get-decorated-string/index.ts +1 -0
- package/templates/string/is-blank/index.ts +1 -0
- package/templates/string/is-blank/is-blank.ts +6 -0
- package/templates/string/mask/index.ts +1 -0
- package/templates/string/mask/mask.ts +30 -0
- package/templates/string/pad/index.ts +1 -0
- package/templates/string/pad/pad.ts +17 -0
- package/templates/string/random-string/index.ts +1 -0
- package/templates/string/random-string/random-string.ts +20 -0
- package/templates/string/reverse/index.ts +1 -0
- package/templates/string/reverse/reverse.ts +6 -0
- package/templates/string/slugify/index.ts +1 -0
- package/templates/string/slugify/slugify.ts +11 -0
- package/templates/string/strip-tags/index.ts +1 -0
- package/templates/string/strip-tags/strip-tags.ts +8 -0
- package/templates/string/template/index.ts +1 -0
- package/templates/string/template/template.ts +14 -0
- package/templates/string/truncate/index.ts +1 -0
- package/templates/string/truncate/truncate.ts +17 -0
- package/templates/string/truncate-middle/index.ts +1 -0
- package/templates/string/truncate-middle/truncate-middle.ts +25 -0
- package/templates/string/uuid/index.ts +1 -0
- package/templates/string/uuid/uuid.ts +9 -0
- package/templates/tailwindcss/class-variant/class-variant.ts +35 -0
- package/templates/tailwindcss/class-variant/index.ts +1 -0
- package/templates/tailwindcss/cn/cn.ts +29 -0
- package/templates/tailwindcss/cn/index.ts +1 -0
- package/templates/tailwindcss/create-tailwind-css/create-tailwind-css.ts +16 -0
- package/templates/tailwindcss/create-tailwind-css/index.ts +1 -0
- package/templates/tailwindcss/tw-merge-config/index.ts +1 -0
- package/templates/tailwindcss/tw-merge-config/tw-merge-config.ts +14 -0
- package/templates/type/get-type/get-type.ts +27 -0
- package/templates/type/get-type/index.ts +1 -0
- package/templates/type/is-type-of/index.ts +1 -0
- package/templates/type/is-type-of/is-type-of.ts +26 -0
- package/templates/util/mutex/index.ts +1 -0
- package/templates/util/mutex/mutex.ts +109 -0
- package/templates/util/url-parser/index.ts +1 -0
- package/templates/util/url-parser/url-parser.ts +44 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
type Compare<T> = (a: T, b: T) => number;
|
|
2
|
+
|
|
3
|
+
type AvlTreeNode<T> = {
|
|
4
|
+
value: T;
|
|
5
|
+
height: number;
|
|
6
|
+
left: AvlTreeNode<T> | null;
|
|
7
|
+
right: AvlTreeNode<T> | null;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const defaultCompare = <T>(a: T, b: T) => {
|
|
11
|
+
if (a < b) {
|
|
12
|
+
return -1;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (a > b) {
|
|
16
|
+
return 1;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return 0;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const createNode = <T>(value: T): AvlTreeNode<T> => ({
|
|
23
|
+
value,
|
|
24
|
+
height: 1,
|
|
25
|
+
left: null,
|
|
26
|
+
right: null,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export class AvlTree<T> {
|
|
30
|
+
private root: AvlTreeNode<T> | null = null;
|
|
31
|
+
private length = 0;
|
|
32
|
+
private compare: Compare<T>;
|
|
33
|
+
|
|
34
|
+
constructor(options: { compare?: Compare<T> } = {}) {
|
|
35
|
+
this.compare = options.compare ?? defaultCompare;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
insert(value: T): boolean {
|
|
39
|
+
const result = this.insertNode(this.root, value);
|
|
40
|
+
this.root = result.node;
|
|
41
|
+
|
|
42
|
+
if (result.inserted) {
|
|
43
|
+
this.length += 1;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return result.inserted;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
has(value: T): boolean {
|
|
50
|
+
let current = this.root;
|
|
51
|
+
|
|
52
|
+
while (current !== null) {
|
|
53
|
+
const compared = this.compare(value, current.value);
|
|
54
|
+
|
|
55
|
+
if (compared === 0) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
current = compared < 0 ? current.left : current.right;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
inOrder(): T[] {
|
|
66
|
+
const values: T[] = [];
|
|
67
|
+
const stack: Array<AvlTreeNode<T>> = [];
|
|
68
|
+
let current = this.root;
|
|
69
|
+
|
|
70
|
+
while (current !== null || stack.length > 0) {
|
|
71
|
+
while (current !== null) {
|
|
72
|
+
stack.push(current);
|
|
73
|
+
current = current.left;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const node = stack.pop() as AvlTreeNode<T>;
|
|
77
|
+
values.push(node.value);
|
|
78
|
+
current = node.right;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return values;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
height() {
|
|
85
|
+
return this.getHeight(this.root);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
size() {
|
|
89
|
+
return this.length;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
isEmpty() {
|
|
93
|
+
return this.length === 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private insertNode(node: AvlTreeNode<T> | null, value: T): { node: AvlTreeNode<T>; inserted: boolean } {
|
|
97
|
+
if (node === null) {
|
|
98
|
+
return {
|
|
99
|
+
node: createNode(value),
|
|
100
|
+
inserted: true,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const compared = this.compare(value, node.value);
|
|
105
|
+
|
|
106
|
+
if (compared === 0) {
|
|
107
|
+
return {
|
|
108
|
+
node,
|
|
109
|
+
inserted: false,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (compared < 0) {
|
|
114
|
+
const result = this.insertNode(node.left, value);
|
|
115
|
+
node.left = result.node;
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
node: this.balance(node),
|
|
119
|
+
inserted: result.inserted,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const result = this.insertNode(node.right, value);
|
|
124
|
+
node.right = result.node;
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
node: this.balance(node),
|
|
128
|
+
inserted: result.inserted,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private balance(node: AvlTreeNode<T>) {
|
|
133
|
+
this.updateHeight(node);
|
|
134
|
+
|
|
135
|
+
const balanceFactor = this.getBalanceFactor(node);
|
|
136
|
+
|
|
137
|
+
if (balanceFactor > 1) {
|
|
138
|
+
if (this.getBalanceFactor(node.left) < 0) {
|
|
139
|
+
node.left = this.rotateLeft(node.left as AvlTreeNode<T>);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return this.rotateRight(node);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (balanceFactor < -1) {
|
|
146
|
+
if (this.getBalanceFactor(node.right) > 0) {
|
|
147
|
+
node.right = this.rotateRight(node.right as AvlTreeNode<T>);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return this.rotateLeft(node);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return node;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private rotateLeft(node: AvlTreeNode<T>) {
|
|
157
|
+
const pivot = node.right as AvlTreeNode<T>;
|
|
158
|
+
const pivotLeft = pivot.left;
|
|
159
|
+
|
|
160
|
+
pivot.left = node;
|
|
161
|
+
node.right = pivotLeft;
|
|
162
|
+
|
|
163
|
+
this.updateHeight(node);
|
|
164
|
+
this.updateHeight(pivot);
|
|
165
|
+
|
|
166
|
+
return pivot;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private rotateRight(node: AvlTreeNode<T>) {
|
|
170
|
+
const pivot = node.left as AvlTreeNode<T>;
|
|
171
|
+
const pivotRight = pivot.right;
|
|
172
|
+
|
|
173
|
+
pivot.right = node;
|
|
174
|
+
node.left = pivotRight;
|
|
175
|
+
|
|
176
|
+
this.updateHeight(node);
|
|
177
|
+
this.updateHeight(pivot);
|
|
178
|
+
|
|
179
|
+
return pivot;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private updateHeight(node: AvlTreeNode<T>) {
|
|
183
|
+
node.height = Math.max(this.getHeight(node.left), this.getHeight(node.right)) + 1;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private getBalanceFactor(node: AvlTreeNode<T> | null) {
|
|
187
|
+
if (node === null) {
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return this.getHeight(node.left) - this.getHeight(node.right);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private getHeight(node: AvlTreeNode<T> | null) {
|
|
195
|
+
return node?.height ?? 0;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./avl-tree";
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
type Compare<T> = (a: T, b: T) => number;
|
|
2
|
+
|
|
3
|
+
type BinarySearchTreeNode<T> = {
|
|
4
|
+
value: T;
|
|
5
|
+
left: BinarySearchTreeNode<T> | null;
|
|
6
|
+
right: BinarySearchTreeNode<T> | null;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const defaultCompare = <T>(a: T, b: T) => {
|
|
10
|
+
if (a < b) {
|
|
11
|
+
return -1;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (a > b) {
|
|
15
|
+
return 1;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return 0;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const createNode = <T>(value: T): BinarySearchTreeNode<T> => ({
|
|
22
|
+
value,
|
|
23
|
+
left: null,
|
|
24
|
+
right: null,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export class BinarySearchTree<T> {
|
|
28
|
+
private root: BinarySearchTreeNode<T> | null = null;
|
|
29
|
+
private length = 0;
|
|
30
|
+
private compare: Compare<T>;
|
|
31
|
+
|
|
32
|
+
constructor(options: { compare?: Compare<T> } = {}) {
|
|
33
|
+
this.compare = options.compare ?? defaultCompare;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
insert(value: T): boolean {
|
|
37
|
+
if (this.root === null) {
|
|
38
|
+
this.root = createNode(value);
|
|
39
|
+
this.length += 1;
|
|
40
|
+
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let current = this.root;
|
|
45
|
+
|
|
46
|
+
while (true) {
|
|
47
|
+
const compared = this.compare(value, current.value);
|
|
48
|
+
|
|
49
|
+
if (compared === 0) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (compared < 0) {
|
|
54
|
+
if (current.left === null) {
|
|
55
|
+
current.left = createNode(value);
|
|
56
|
+
this.length += 1;
|
|
57
|
+
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
current = current.left;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (current.right === null) {
|
|
66
|
+
current.right = createNode(value);
|
|
67
|
+
this.length += 1;
|
|
68
|
+
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
current = current.right;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
has(value: T): boolean {
|
|
77
|
+
let current = this.root;
|
|
78
|
+
|
|
79
|
+
while (current !== null) {
|
|
80
|
+
const compared = this.compare(value, current.value);
|
|
81
|
+
|
|
82
|
+
if (compared === 0) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
current = compared < 0 ? current.left : current.right;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
inOrder(): T[] {
|
|
93
|
+
const values: T[] = [];
|
|
94
|
+
const stack: Array<BinarySearchTreeNode<T>> = [];
|
|
95
|
+
let current = this.root;
|
|
96
|
+
|
|
97
|
+
while (current !== null || stack.length > 0) {
|
|
98
|
+
while (current !== null) {
|
|
99
|
+
stack.push(current);
|
|
100
|
+
current = current.left;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const node = stack.pop() as BinarySearchTreeNode<T>;
|
|
104
|
+
values.push(node.value);
|
|
105
|
+
current = node.right;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return values;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
min(): T | null {
|
|
112
|
+
let current = this.root;
|
|
113
|
+
if (current === null) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
while (current.left !== null) {
|
|
118
|
+
current = current.left;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return current.value;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
max(): T | null {
|
|
125
|
+
let current = this.root;
|
|
126
|
+
if (current === null) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
while (current.right !== null) {
|
|
131
|
+
current = current.right;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return current.value;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
size() {
|
|
138
|
+
return this.length;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
isEmpty() {
|
|
142
|
+
return this.length === 0;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./binary-search-tree";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./lca";
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
export class LowestCommonAncestor {
|
|
2
|
+
private parents: number[][];
|
|
3
|
+
private depths: number[];
|
|
4
|
+
|
|
5
|
+
constructor(graph: number[][], root = 0) {
|
|
6
|
+
if (graph.length === 0) {
|
|
7
|
+
throw new RangeError("graph must contain at least one node");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
this.assertNode(root, graph.length);
|
|
11
|
+
|
|
12
|
+
const maxLevel = Math.max(1, Math.ceil(Math.log2(graph.length)) + 1);
|
|
13
|
+
this.parents = Array.from({ length: maxLevel }, () => Array(graph.length).fill(-1));
|
|
14
|
+
this.depths = Array(graph.length).fill(-1);
|
|
15
|
+
|
|
16
|
+
this.build(graph, root);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
lca(a: number, b: number) {
|
|
20
|
+
this.assertConnectedNode(a);
|
|
21
|
+
this.assertConnectedNode(b);
|
|
22
|
+
|
|
23
|
+
let left = a;
|
|
24
|
+
let right = b;
|
|
25
|
+
|
|
26
|
+
if (this.depths[left] < this.depths[right]) {
|
|
27
|
+
[left, right] = [right, left];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
left = this.lift(left, this.depths[left] - this.depths[right]);
|
|
31
|
+
|
|
32
|
+
if (left === right) {
|
|
33
|
+
return left;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (let level = this.parents.length - 1; level >= 0; level -= 1) {
|
|
37
|
+
if (this.parents[level][left] !== this.parents[level][right]) {
|
|
38
|
+
left = this.parents[level][left];
|
|
39
|
+
right = this.parents[level][right];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return this.parents[0][left];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
distance(a: number, b: number) {
|
|
47
|
+
const ancestor = this.lca(a, b);
|
|
48
|
+
|
|
49
|
+
return this.depths[a] + this.depths[b] - this.depths[ancestor] * 2;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
depth(node: number) {
|
|
53
|
+
this.assertConnectedNode(node);
|
|
54
|
+
|
|
55
|
+
return this.depths[node];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private build(graph: number[][], root: number) {
|
|
59
|
+
const stack = [root];
|
|
60
|
+
this.parents[0][root] = root;
|
|
61
|
+
this.depths[root] = 0;
|
|
62
|
+
|
|
63
|
+
while (stack.length > 0) {
|
|
64
|
+
const node = stack.pop() as number;
|
|
65
|
+
|
|
66
|
+
for (const next of graph[node]) {
|
|
67
|
+
this.assertNode(next, graph.length);
|
|
68
|
+
|
|
69
|
+
if (next === this.parents[0][node]) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (this.depths[next] !== -1) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.parents[0][next] = node;
|
|
78
|
+
this.depths[next] = this.depths[node] + 1;
|
|
79
|
+
stack.push(next);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for (let level = 1; level < this.parents.length; level += 1) {
|
|
84
|
+
for (let node = 0; node < graph.length; node += 1) {
|
|
85
|
+
const parent = this.parents[level - 1][node];
|
|
86
|
+
this.parents[level][node] = parent === -1 ? -1 : this.parents[level - 1][parent];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private lift(node: number, distance: number) {
|
|
92
|
+
let current = node;
|
|
93
|
+
let remainingDistance = distance;
|
|
94
|
+
|
|
95
|
+
for (let level = 0; remainingDistance > 0; level += 1) {
|
|
96
|
+
if (remainingDistance % 2 === 1) {
|
|
97
|
+
current = this.parents[level][current];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
remainingDistance = Math.floor(remainingDistance / 2);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return current;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private assertConnectedNode(node: number) {
|
|
107
|
+
this.assertNode(node, this.depths.length);
|
|
108
|
+
|
|
109
|
+
if (this.depths[node] === -1) {
|
|
110
|
+
throw new RangeError("node is not connected to the root");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private assertNode(node: number, nodeCount: number) {
|
|
115
|
+
if (!Number.isInteger(node) || node < 0 || node >= nodeCount) {
|
|
116
|
+
throw new RangeError("node is out of range");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const chunk = <T>(array: T[], size: number): T[][] => {
|
|
2
|
+
if (size <= 0) {
|
|
3
|
+
return [];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const result: T[][] = [];
|
|
7
|
+
|
|
8
|
+
for (let index = 0; index < array.length; index += size) {
|
|
9
|
+
result.push(array.slice(index, index + size));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return result;
|
|
13
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./chunk";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type Falsy = false | null | undefined | 0 | "";
|
|
2
|
+
|
|
3
|
+
// NaN은 number 타입이라 위 Falsy 리터럴 유니온으로는 표현할 수 없다.
|
|
4
|
+
// 런타임에서는 Boolean(NaN) === false라 함께 걸러지고, 타입 위에서는
|
|
5
|
+
// 입력의 number 부분이 그대로 T로 남는다(예: compact([1, 0, 2]) -> number[]).
|
|
6
|
+
export const compact = <T>(array: ReadonlyArray<T | Falsy>): T[] => {
|
|
7
|
+
return array.filter((value): value is T => Boolean(value));
|
|
8
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./compact";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// 배열 항목을 keyFn이 돌려주는 키로 묶어 키별 개수를 센다. 입력 배열은
|
|
2
|
+
// 변형하지 않고 { 키: 개수 } 객체를 새로 반환한다.
|
|
3
|
+
export const countBy = <T, K extends PropertyKey>(array: readonly T[], keyFn: (item: T) => K): Record<K, number> => {
|
|
4
|
+
const result = {} as Record<K, number>;
|
|
5
|
+
|
|
6
|
+
for (const item of array) {
|
|
7
|
+
const key = keyFn(item);
|
|
8
|
+
result[key] = (result[key] ?? 0) + 1;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return result;
|
|
12
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./count-by";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./difference";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// 배열 앞에서 n개를 제외한 나머지를 새 배열로 반환한다. 입력 배열은 변형하지 않는다.
|
|
2
|
+
// n이 0 이하면 전체 복사본, n이 길이 이상이면 빈 배열을 돌려준다.
|
|
3
|
+
// slice의 음수 인덱스 해석을 피하려고 n을 [0, length]로 먼저 클램프한다.
|
|
4
|
+
export const drop = <T>(array: readonly T[], n: number): T[] => {
|
|
5
|
+
const count = Math.min(Math.max(n, 0), array.length);
|
|
6
|
+
return array.slice(count);
|
|
7
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./drop";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// 배열 항목을 keyFn이 돌려주는 키로 묶는다. 같은 키의 항목들은 등장 순서대로
|
|
2
|
+
// 한 배열에 모인다. 입력 배열은 변형하지 않고 새 객체를 반환한다.
|
|
3
|
+
export const groupBy = <T, K extends PropertyKey>(array: readonly T[], keyFn: (item: T) => K): Record<K, T[]> => {
|
|
4
|
+
const result = {} as Record<K, T[]>;
|
|
5
|
+
|
|
6
|
+
for (const item of array) {
|
|
7
|
+
const key = keyFn(item);
|
|
8
|
+
const bucket = result[key];
|
|
9
|
+
|
|
10
|
+
if (bucket === undefined) {
|
|
11
|
+
result[key] = [item];
|
|
12
|
+
} else {
|
|
13
|
+
bucket.push(item);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return result;
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./group-by";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./intersection";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const intersection = <T>(a: T[], b: T[]): T[] => {
|
|
2
|
+
// b는 포함 검사용, seen은 중복 제거용. 순서는 a를 한 번 훑으며 결정한다.
|
|
3
|
+
const include = new Set(b);
|
|
4
|
+
const seen = new Set<T>();
|
|
5
|
+
const result: T[] = [];
|
|
6
|
+
|
|
7
|
+
for (const value of a) {
|
|
8
|
+
if (include.has(value) && !seen.has(value)) {
|
|
9
|
+
seen.add(value);
|
|
10
|
+
result.push(value);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return result;
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./key-by";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// 배열을 keyFn이 돌려주는 키로 인덱싱해 { 키: 항목 } 객체를 만든다.
|
|
2
|
+
// 같은 키가 여러 번 나오면 마지막 항목이 우선한다. 입력 배열은 변형하지 않는다.
|
|
3
|
+
export const keyBy = <T, K extends PropertyKey>(array: readonly T[], keyFn: (item: T) => K): Record<K, T> => {
|
|
4
|
+
const result = {} as Record<K, T>;
|
|
5
|
+
|
|
6
|
+
for (const item of array) {
|
|
7
|
+
result[keyFn(item)] = item;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return result;
|
|
11
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./max-by";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const maxBy = <T>(array: T[], valueFn: (item: T) => number): T | null => {
|
|
2
|
+
if (array.length === 0) {
|
|
3
|
+
return null;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
let best = array[0];
|
|
7
|
+
let bestValue = valueFn(best);
|
|
8
|
+
|
|
9
|
+
for (let index = 1; index < array.length; index += 1) {
|
|
10
|
+
const item = array[index];
|
|
11
|
+
const value = valueFn(item);
|
|
12
|
+
|
|
13
|
+
if (value > bestValue) {
|
|
14
|
+
best = item;
|
|
15
|
+
bestValue = value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return best;
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./min-by";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const minBy = <T>(array: T[], valueFn: (item: T) => number): T | null => {
|
|
2
|
+
if (array.length === 0) {
|
|
3
|
+
return null;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
let best = array[0];
|
|
7
|
+
let bestValue = valueFn(best);
|
|
8
|
+
|
|
9
|
+
for (let index = 1; index < array.length; index += 1) {
|
|
10
|
+
const item = array[index];
|
|
11
|
+
const value = valueFn(item);
|
|
12
|
+
|
|
13
|
+
if (value < bestValue) {
|
|
14
|
+
best = item;
|
|
15
|
+
bestValue = value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return best;
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./partition";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// 배열을 predicate 통과 여부로 두 그룹으로 나눈다. [통과한 항목, 실패한 항목]
|
|
2
|
+
// 튜플을 반환하며 각 그룹은 등장 순서를 보존한다. 입력 배열은 변형하지 않는다.
|
|
3
|
+
export const partition = <T>(array: readonly T[], predicate: (item: T) => boolean): [T[], T[]] => {
|
|
4
|
+
const passed: T[] = [];
|
|
5
|
+
const failed: T[] = [];
|
|
6
|
+
|
|
7
|
+
for (const item of array) {
|
|
8
|
+
if (predicate(item)) {
|
|
9
|
+
passed.push(item);
|
|
10
|
+
} else {
|
|
11
|
+
failed.push(item);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return [passed, failed];
|
|
16
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./range";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// [start, end) 구간을 step 간격으로 채운 숫자 배열을 반환한다. step 기본값은 1.
|
|
2
|
+
// step이 0이면(전진하지 않으므로) 빈 배열을 돌려준다.
|
|
3
|
+
// 음수 step도 지원한다: start > end 일 때 감소 수열을 만든다.
|
|
4
|
+
// 방향(step 부호)과 구간 방향이 어긋나면(예: start < end 인데 step < 0) 빈 배열이 된다.
|
|
5
|
+
export const range = (start: number, end: number, step = 1): number[] => {
|
|
6
|
+
if (step === 0) {
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const result: number[] = [];
|
|
11
|
+
if (step > 0) {
|
|
12
|
+
for (let value = start; value < end; value += step) {
|
|
13
|
+
result.push(value);
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
16
|
+
for (let value = start; value > end; value += step) {
|
|
17
|
+
result.push(value);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return result;
|
|
22
|
+
};
|