nv-dag-using-str-nd 1.0.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/TEST/perf.js ADDED
@@ -0,0 +1,45 @@
1
+
2
+ const { creat } = require("../index"); // 假设 dag.js 导出 creat
3
+
4
+ function benchmark() {
5
+ const g = creat();
6
+ const nodeCount = 50000;
7
+ const edgeCount = 2000000;
8
+
9
+ // 生成节点名
10
+ const nodes = [];
11
+ for (let i = 0; i < nodeCount; i++) {
12
+ nodes.push(`N${i}`);
13
+ }
14
+
15
+ // 记录开始时间
16
+ const t1 = performance.now();
17
+
18
+ // 添加节点
19
+ for (const node of nodes) {
20
+ g.add_nd(node);
21
+ }
22
+
23
+ // 随机添加边(确保不重复、不形成自环)
24
+ let attempts = 0;
25
+ let edgesAdded = 0;
26
+ while (attempts < edgeCount) {
27
+ const src = nodes[Math.floor(Math.random() * 10 )];
28
+ let dst = nodes[Math.floor(Math.random() * nodeCount)];
29
+ if (src !== dst) {
30
+ const [ok] = g.conn(src, dst);
31
+ if (ok) edgesAdded++;
32
+ }
33
+ attempts++;
34
+ }
35
+
36
+ const t2 = performance.now();
37
+
38
+ console.log(`节点数: ${nodeCount}`);
39
+ console.log(`边数: ${edgesAdded}`);
40
+ console.log(`耗时: ${(t2 - t1).toFixed(2)} ms`);
41
+ ////
42
+ //// 删除所有 孤立节点: 出度和入度均为0
43
+ }
44
+
45
+ benchmark();
@@ -0,0 +1,86 @@
1
+ // dag.test.js
2
+ const assert = require('assert');
3
+ const { creat } = require('../index'); // 假设 dag.js 在同一目录
4
+
5
+ function runTests() {
6
+ console.log('开始测试 DAG 工具...');
7
+
8
+ // 创建空图
9
+ let g = creat();
10
+
11
+ // ========== 测试 add_nd / has_nd / del_nd ==========
12
+ assert.strictEqual(g.has_nd('A'), false, '初始无节点 A');
13
+ assert.deepStrictEqual(g.add_nd('A'), [true, 'A'], '添加节点 A 成功');
14
+ assert.strictEqual(g.has_nd('A'), true, '节点 A 存在');
15
+ assert.deepStrictEqual(g.add_nd('A'), [false, 'A already exist'], '重复添加 A 失败');
16
+ assert.deepStrictEqual(g.del_nd('A'), [true, 'A'], '删除节点 A 成功');
17
+ assert.strictEqual(g.has_nd('A'), false, '节点 A 已不存在');
18
+ assert.deepStrictEqual(g.del_nd('A'), [false, 'A NOT exist'], '删除不存在的节点失败');
19
+
20
+ // ========== 测试 conn / is_reachable_fr_to ==========
21
+ g.add_nd('X');
22
+ g.add_nd('Y');
23
+ g.add_nd('Z');
24
+ assert.deepStrictEqual(g.conn('X', 'Y'), [true, 'X', 'Y'], '连接 X -> Y 成功');
25
+ assert.deepStrictEqual(g.conn('Y', 'Z'), [true, 'Y', 'Z'], '连接 Y -> Z 成功');
26
+ assert.strictEqual(g.is_reachable_fr_to('X', 'Z'), true, 'X 可达 Z');
27
+ assert.strictEqual(g.is_reachable_fr_to('Z', 'X'), false, 'Z 不可达 X');
28
+ assert.strictEqual(g.is_reachable_fr_to('X', 'X'), true, '节点自身可达');
29
+
30
+ // 测试循环依赖
31
+ assert.deepStrictEqual(g.conn('Z', 'X'), [false, 'circular X->...->Z'], '检测循环依赖 Z -> X 失败');
32
+
33
+ // 测试重复连接
34
+ assert.deepStrictEqual(g.conn('X', 'Y'), [false, 'X->Y already exist'], '重复连接 X -> Y 失败');
35
+
36
+ // ========== 测试 get_reachable_fr / get_reachable_to ==========
37
+ let reachableFromX = g.get_reachable_fr('X');
38
+ assert.deepStrictEqual(reachableFromX.has('Y') && reachableFromX.has('Z'), true, '从 X 可达 Y 和 Z');
39
+ let reachableToZ = g.get_reachable_to('Z');
40
+ assert.deepStrictEqual(reachableToZ.has('X') && reachableToZ.has('Y'), true, '到 Z 的可达节点包含 X 和 Y');
41
+
42
+ // ========== 测试 add (单参数 & 双参数) ==========
43
+ g2 = creat();
44
+ assert.deepStrictEqual(g2.add('P'), [true, 'P'], 'add 单参数添加 P 成功');
45
+ assert.deepStrictEqual(g2.add('Q'), [true, 'Q'], 'add 单参数添加 Q 成功');
46
+ assert.deepStrictEqual(g2.add('P', 'Q'), [true, 'P', 'Q'], 'add 双参数连接 P -> Q 成功');
47
+ assert.deepStrictEqual(g2.add('P', 'Q'), [false, 'P->Q already exist'], 'add 重复连接 P -> Q 失败');
48
+
49
+ // ========== 测试 has (单参数 & 双参数) ==========
50
+ assert.strictEqual(g2.has('P'), true, 'has 单参数检查 P 存在');
51
+ assert.strictEqual(g2.has('R'), false, 'has 单参数检查 R 不存在');
52
+ assert.strictEqual(g2.has('P', 'Q'), true, 'has 双参数检查边 P -> Q 存在');
53
+ assert.strictEqual(g2.has('Q', 'P'), false, 'has 双参数检查边 Q -> P 不存在');
54
+
55
+ // ========== 测试 del (单参数 & 双参数) ==========
56
+ assert.deepStrictEqual(g2.del('P', 'Q'), [true, 'P', 'Q'], 'del 双参数删除边 P -> Q 成功');
57
+ assert.deepStrictEqual(g2.del('P', 'Q'), [false, 'edge P->Q NOT exist'], 'del 删除不存在的边失败');
58
+ assert.deepStrictEqual(g2.del('R'), [false, 'R NOT exist'], 'del 单参数删除不存在的节点失败');
59
+
60
+ // ========== 测试 in0nds / ot0nds ==========
61
+ g3 = creat();
62
+ g3.add_nd('A');
63
+ g3.add_nd('B');
64
+ g3.add_nd('C');
65
+ g3.conn('A', 'B');
66
+ g3.conn('A', 'C');
67
+ let in0 = g3.in0nds;
68
+ let ot0 = g3.ot0nds;
69
+ assert.strictEqual(in0.has('A'), true, 'A 入度为 0');
70
+ assert.strictEqual(ot0.has('B') && ot0.has('C'), true, 'B 和 C 出度为 0');
71
+
72
+ // ========== 测试 clone ==========
73
+ let g3clone = g3.clone();
74
+ assert.deepStrictEqual(g3clone.nds, g3.nds, '克隆后节点集合一致');
75
+ assert.deepStrictEqual(g3clone.dns, g3.dns, '克隆后 dns 一致');
76
+ assert.deepStrictEqual(g3clone.ups, g3.ups, '克隆后 ups 一致');
77
+ // 修改原图不影响克隆
78
+ g3.add_nd('D');
79
+ assert.strictEqual(g3clone.has_nd('D'), false, '克隆不受原图修改影响');
80
+
81
+ console.log('✅ 所有测试通过!');
82
+ }
83
+
84
+ runTests();
85
+
86
+
@@ -0,0 +1,103 @@
1
+ const assert = require('assert');
2
+ const {
3
+ creat,
4
+ sync_d2u_iter,
5
+ async_d2u_iter,
6
+ sync_u2d_iter,
7
+ async_u2d_iter,
8
+ } = require("../index");
9
+
10
+
11
+ // ========== 测试 4 个 iterator 方法 ==========
12
+
13
+ console.log('\n--- 测试 sync_d2u_iter ---');
14
+ {
15
+ let g = creat();
16
+ // 构造图: A -> B -> D, A -> C -> D
17
+ g.add('A', 'B');
18
+ g.add('A', 'C');
19
+ g.add('B', 'D');
20
+ g.add('C', 'D');
21
+
22
+ let order = [];
23
+ sync_d2u_iter(g, (nds, readonly_g) => {
24
+ // nds 是当前出度为 0 的节点集合
25
+ let arr = Array.from(nds).sort();
26
+ order.push(arr);
27
+ console.log('sync_d2u_iter step:', arr);
28
+ });
29
+
30
+ // 预期顺序:先 [D](叶子),再 [B, C](B、C 变成叶子),最后 [A](根)
31
+ assert.deepStrictEqual(order, [['D'], ['B', 'C'], ['A']], 'sync_d2u_iter 顺序正确');
32
+ console.log('✅ sync_d2u_iter 测试通过');
33
+ }
34
+
35
+ console.log('\n--- 测试 sync_u2d_iter ---');
36
+ {
37
+ let g = creat();
38
+ g.add('A', 'B');
39
+ g.add('A', 'C');
40
+ g.add('B', 'D');
41
+ g.add('C', 'D');
42
+
43
+ let order = [];
44
+ sync_u2d_iter(g, (nds, readonly_g) => {
45
+ let arr = Array.from(nds).sort();
46
+ order.push(arr);
47
+ console.log('sync_u2d_iter step:', arr);
48
+ });
49
+
50
+ // 预期顺序:先 [A](根),再 [B, C],最后 [D](叶子)
51
+ assert.deepStrictEqual(order, [['A'], ['B', 'C'], ['D']], 'sync_u2d_iter 顺序正确');
52
+ console.log('✅ sync_u2d_iter 测试通过');
53
+ }
54
+
55
+ console.log('\n--- 测试 async_d2u_iter ---');
56
+ var p
57
+ {
58
+ let g = creat();
59
+ g.add('A', 'B');
60
+ g.add('A', 'C');
61
+ g.add('B', 'D');
62
+ g.add('C', 'D');
63
+
64
+ let order = [];
65
+ async function testAsyncD2U() {
66
+ await async_d2u_iter(g, async (nds, readonly_g) => {
67
+ let arr = Array.from(nds).sort();
68
+ order.push(arr);
69
+ console.log('async_d2u_iter step:', arr);
70
+ // 模拟异步操作
71
+ await new Promise(resolve => setTimeout(resolve, 10));
72
+ });
73
+ assert.deepStrictEqual(order, [['D'], ['B', 'C'], ['A']], 'async_d2u_iter 顺序正确');
74
+ console.log('✅ async_d2u_iter 测试通过');
75
+ }
76
+ p= testAsyncD2U();
77
+ }
78
+
79
+ p.then(r=>{
80
+ console.log('\n--- 测试 async_u2d_iter ---');
81
+ {
82
+ let g = creat();
83
+ g.add('A', 'B');
84
+ g.add('A', 'C');
85
+ g.add('B', 'D');
86
+ g.add('C', 'D');
87
+
88
+ let order = [];
89
+ async function testAsyncU2D() {
90
+ await async_u2d_iter(g, async (nds, readonly_g) => {
91
+ let arr = Array.from(nds).sort();
92
+ order.push(arr);
93
+ console.log('async_u2d_iter step:', arr);
94
+ await new Promise(resolve => setTimeout(resolve, 10));
95
+ });
96
+ assert.deepStrictEqual(order, [['A'], ['B', 'C'], ['D']], 'async_u2d_iter 顺序正确');
97
+ console.log('✅ async_u2d_iter 测试通过');
98
+ }
99
+ testAsyncU2D();
100
+ }
101
+ });
102
+
103
+
@@ -0,0 +1,13 @@
1
+ const {creat} = require("../index")
2
+
3
+ // 测试 show 方法
4
+ let gShow = creat();
5
+ gShow.add_nd('A');
6
+ gShow.add_nd('B');
7
+ gShow.add_nd('C');
8
+ gShow.add_nd('D');
9
+ gShow.conn('A', 'B');
10
+ gShow.conn('A', 'C');
11
+ gShow.conn('B', 'D');
12
+ gShow.conn('C', 'D');
13
+ gShow.show();
package/index.js ADDED
@@ -0,0 +1,517 @@
1
+ //src depends on dst
2
+
3
+ // 例如我们有 4个节点 A B C D
4
+ // 关系 A->B A->C B->D C->D
5
+ // nds :{A,B,C,D}
6
+ // dns: {
7
+ // A:{B,C}
8
+ // B:{D}
9
+ // C:{D}
10
+ // D:{}
11
+ // }
12
+ //
13
+ // ups {
14
+ // A:{}
15
+ // B:{A}
16
+ // C:{A}
17
+ // D:{BMC}
18
+ // }
19
+ //
20
+ //------我们适当增加一些冗余数据结构(空间换时间)
21
+ //------让我们探测环路 和 算必经点 容易一些
22
+ //
23
+
24
+
25
+ class G {
26
+ nds = new Set();
27
+ dns = {}; //直连下游
28
+ ups = {}; //直连上游
29
+ ////
30
+ has_nd(p) { return this.nds.has(p); }
31
+ indeg(p) {
32
+ if(this.has_nd(p)) {
33
+ return this.ups[p].size();
34
+ } else {
35
+ return -1;
36
+ }
37
+ }
38
+ otdeg(p) {
39
+ if(this.has_nd(p)) {
40
+ return this.dns[p].size();
41
+ } else {
42
+ return -1;
43
+ }
44
+ }
45
+ ////
46
+ _add_nd(p) {
47
+ this.nds.add(p);
48
+ this.dns[p] = new Set(); // p 直接连向的节点
49
+ this.ups[p] = new Set(); // 直接连向p 的节点
50
+ }
51
+ add_nd(p) {
52
+ if(this.has_nd(p)) {
53
+ return [false, `${p} already exist`]
54
+ } else {
55
+ this._add_nd(p);
56
+ return [true,p]
57
+ }
58
+ }
59
+ _del_nd(p) {
60
+ this.nds.delete(p); //先从节点集删除
61
+ var ups = this.ups[p]; delete this.ups[p];
62
+ for(var u of ups) {
63
+ //所有p 的直连上游节点u 删除 其下游节点p
64
+ delete this.dns[u].delete(p);
65
+ }
66
+ var dns = this.dns[p]; delete this.dns[p];
67
+ for(var d of dns) {
68
+ //所有p 的直连下游节点d 删除 其上游节点p
69
+ delete this.ups[d].delete(p);
70
+ }
71
+ }
72
+ del_nd(p) {
73
+ if(!this.has_nd(p)) {
74
+ return [false, `${p} NOT exist`]
75
+ } else {
76
+ this._del_nd(p);
77
+ return [true,p]
78
+ }
79
+ }
80
+ ////
81
+ _is_reachable_fr_to(src,dst) {
82
+ var unhandled = [];
83
+ for(var d of this.dns[src]) {
84
+ if(d=== dst) {
85
+ return true;
86
+ } else {
87
+ unhandled.push(d)
88
+ }
89
+ }
90
+ var next_unhandled = [];
91
+ while(unhandled.length>0) {
92
+ for(var s of unhandled) {
93
+ var dsts = this.dns[s];
94
+ for(var d of dsts) {
95
+ if(d=== dst) {
96
+ return true
97
+ } else {
98
+ next_unhandled.push(d)
99
+ }
100
+ }
101
+ }
102
+ unhandled = next_unhandled;
103
+ next_unhandled = [];
104
+ }
105
+ return false;
106
+ }
107
+ is_reachable_fr_to(src,dst) {
108
+ if(this.has_nd(src)) {
109
+ if(this.has_nd(dst)) {
110
+ if(src !== dst) {
111
+ return this. _is_reachable_fr_to(src,dst);
112
+ } else {
113
+ return true;
114
+ }
115
+ } else {
116
+ return false;
117
+ }
118
+ } else {
119
+ return false;
120
+ }
121
+ }
122
+ ////
123
+ _conn(src,dst) {
124
+ this.dns[src].add(dst);
125
+ this.ups[dst].add(src);
126
+ }
127
+ conn(src,dst) {
128
+ if(this.has_nd(src)) {
129
+ if(this.has_nd(dst)) {
130
+ if(this._is_reachable_fr_to(dst,src)) {
131
+ return [false,`circular ${dst}->...->${src}`];
132
+ } else if(src === dst) {
133
+ return [false, `${src} MUST be different from${dst}`]
134
+ } else if(this.dns[src].has(dst)) {
135
+ return [false, `${src}->${dst} already exist`]
136
+ } else {
137
+ this._conn(src,dst);
138
+ return [true,src,dst]
139
+ }
140
+ } else {
141
+ return [false,`first add_nd(${dst})`];
142
+ }
143
+ } else {
144
+ return [false,`fisrt add_nd(${src})`];
145
+ }
146
+ }
147
+ ////
148
+ get_reachable_fr(src) {
149
+ var st = new Set();
150
+ var unhandled = [];
151
+ for(var d of this.dns[src]) {
152
+ st.add(d);
153
+ unhandled.push(d)
154
+ }
155
+ var next_unhandled = [];
156
+ while(unhandled.length>0) {
157
+ for(var s of unhandled) {
158
+ var dsts = this.dns[s];
159
+ for(var d of dsts) {
160
+ st.add(d);
161
+ next_unhandled.push(d)
162
+ }
163
+ }
164
+ unhandled = next_unhandled;
165
+ unhandled = [];
166
+ }
167
+ return st;
168
+ }
169
+ get_reachable_to(dst) {
170
+ var st = new Set();
171
+ var unhandled = [];
172
+ for(var s of this.ups[dst]) {
173
+ st.add(s);
174
+ unhandled.push(s)
175
+ }
176
+ var next_unhandled = [];
177
+ while(unhandled.length>0) {
178
+ for(var d of unhandled) {
179
+ var srcs = this.ups[d];
180
+ for(var s of srcs) {
181
+ st.add(s);
182
+ next_unhandled.push(s)
183
+ }
184
+ }
185
+ unhandled = next_unhandled;
186
+ unhandled = [];
187
+ }
188
+ return st;
189
+ }
190
+ ////--------------------------------------------------------------------------------
191
+ add(...args) {
192
+ if(args.length ===0) {
193
+ return [false,`args.length MUST be 1 OR 2`];
194
+ } else if(args.length === 1) {
195
+ return this.add_nd(args[0]);
196
+ } else if(args.length === 2) {
197
+ var src = args[0];
198
+ var dst = args[1];
199
+ if(this.has_nd(src)) {} else {this.add_nd(src)}
200
+ if(this.has_nd(dst)) {} else {this.add_nd(dst)}
201
+ if(this._is_reachable_fr_to(dst,src)) {
202
+ return [false,`circular ${dst}->...->${src}`];
203
+ } else if(src === dst) {
204
+ return [false, `${src} MUST be different from${dst}`]
205
+ } else if(this.dns[src].has(dst)) {
206
+ return [false, `${src}->${dst} already exist`]
207
+ } else {
208
+ this.conn(src,dst);
209
+ return [true,src,dst]
210
+ }
211
+ } else {
212
+ return [false,`args.length MUST be 1 OR 2`];
213
+ }
214
+ }
215
+ has(...args) {
216
+ if(args.length ===0) {
217
+ return false;
218
+ } else if(args.length === 1) {
219
+ return this.has_nd(args[0]);
220
+ } else if(args.length === 2) {
221
+ var src = args[0];
222
+ var dst = args[1];
223
+ if(this.has_nd(src)) {
224
+ if(this.has_nd(dst)) {
225
+ return this.dns[src].has(dst);
226
+ } else {
227
+ return false;
228
+ }
229
+ } else {
230
+ return false;
231
+ }
232
+ } else {
233
+ return false;
234
+ }
235
+ }
236
+ del(...args) {
237
+ if(args.length ===0) {
238
+ return [false,`args.length MUST be 1 OR 2`];
239
+ } else if(args.length === 1) {
240
+ return this.del_nd(args[0]);
241
+ } else if(args.length === 2) {
242
+ var src = args[0];
243
+ var dst = args[1];
244
+ if(this.has_nd(src)) {
245
+ if(this.has_nd(dst)) {
246
+ if(this.dns[src].has(dst)) {
247
+ this.dns[src].delete(dst);
248
+ this.ups[dst].delete(src);
249
+ return [true,src,dst];
250
+ } else {
251
+ return [false,`edge ${src}->${dst} NOT exist`]
252
+ }
253
+ } else {
254
+ return [false,`${dst} NOT exist`];
255
+ }
256
+ } else {
257
+ return [false,`${src} NOT exist`];
258
+ }
259
+ } else {
260
+ return [false,`args.length MUST be 1 OR 2`];
261
+ }
262
+ }
263
+ ////--------------------------------------------------------------------
264
+ get in0nds() {
265
+ //入度为0的节点
266
+ var st = new Set();
267
+ for(var dst in this.ups) {
268
+ var ups = this.ups[dst];
269
+ if(ups.size === 0) {
270
+ st.add(dst);
271
+ }
272
+ }
273
+ return st;
274
+ }
275
+ get ot0nds() {
276
+ //出度为0的节点
277
+ var st = new Set();
278
+ for(var src in this.dns) {
279
+ var dns = this.dns[src];
280
+ if(dns.size === 0) {
281
+ st.add(src);
282
+ }
283
+ }
284
+ return st;
285
+ }
286
+ ////
287
+ clone() {
288
+ var ng = new G();
289
+ for(var nd of this.nds) {ng.nds.add(nd)}
290
+ for(var src in this.dns) {
291
+ var st = new Set();
292
+ ng.dns[src] = st;
293
+ for(var dst of this.dns[src]) {
294
+ st.add(dst);
295
+ }
296
+ }
297
+ for(var dst in this.ups) {
298
+ var st = new Set();
299
+ ng.ups[dst] = st;
300
+ for(var src of this.ups[dst]) {
301
+ st.add(src);
302
+ }
303
+ }
304
+ return ng;
305
+ }
306
+ ////
307
+ show() {
308
+ if (this.nds.size === 0) {
309
+ console.log("(empty graph)");
310
+ return;
311
+ }
312
+ // 1. 打印所有节点
313
+ console.log("Nodes:");
314
+ for (let nd of this.nds) {
315
+ console.log(` ${nd}`);
316
+ }
317
+
318
+ // 2. 打印所有边,按源节点分组
319
+ console.log("Edges:");
320
+ // 收集所有源节点并按字母顺序排序
321
+ let sources = Array.from(this.nds).filter(nd => this.dns[nd] && this.dns[nd].size > 0);
322
+ sources.sort();
323
+
324
+ for (let src of sources) {
325
+ let dsts = Array.from(this.dns[src]).sort();
326
+ for (let dst of dsts) {
327
+ console.log(` ${src} -> ${dst}`);
328
+ }
329
+ }
330
+
331
+ // 3. 打印入度为 0 的节点(根)
332
+ let roots = Array.from(this.in0nds).sort();
333
+ console.log("Root nodes (in-degree 0):", roots.join(", ") || "(none)");
334
+
335
+ // 4. 打印出度为 0 的节点(叶子)
336
+ let leaves = Array.from(this.ot0nds).sort();
337
+ console.log("Leaf nodes (out-degree 0):", leaves.join(", ") || "(none)");
338
+ }
339
+ ////
340
+ get_all_paths_fr(src) {
341
+ if(this.has_nd(src)) {
342
+ // 求出所有 从 src 可达 的路径
343
+ // 返回 Array<Array>
344
+ var paths = [];
345
+ var unhandled = [];
346
+ for(var d of this.dns[src]) {
347
+ paths.push([src,d]);
348
+ unhandled.push([src,d])
349
+ }
350
+ var next_unhandled = [];
351
+ while(unhandled.length>0) {
352
+ for(var pl of unhandled) {
353
+ var lst = pl[pl.length-1];
354
+ for(var d of this.dns[lst]) {
355
+ var npl = pl.slice(0);
356
+ npl.push(d);
357
+ paths.push(npl);
358
+ next_unhandled.push(npl.slice(0))
359
+ }
360
+ }
361
+ unhandled = next_unhandled;
362
+ next_unhandled = [];
363
+
364
+ }
365
+ return paths
366
+ } else {
367
+ return [];
368
+ }
369
+ }
370
+ get_all_paths_to(dst) {
371
+ if (this.has_nd(dst)) {
372
+ // 求出所有 能到达 dst 的路径(从某个起点到 dst)
373
+ // 返回 Array<Array>
374
+ var paths = [];
375
+ var unhandled = [];
376
+ // 从 dst 的直连上游开始
377
+ for (var u of this.ups[dst]) {
378
+ paths.push([u, dst]);
379
+ unhandled.push([u, dst]);
380
+ }
381
+ var next_unhandled = [];
382
+ while (unhandled.length > 0) {
383
+ for (var pl of unhandled) {
384
+ var lst = pl[0]; // 路径的第一个节点(最上游端)
385
+ for (var s of this.ups[lst]) { // 找 lst 的上游
386
+ var npl = [s, ...pl]; // 在路径前端插入 s
387
+ paths.push(npl);
388
+ next_unhandled.push(npl.slice(0));
389
+ }
390
+ }
391
+ unhandled = next_unhandled;
392
+ next_unhandled = [];
393
+ }
394
+ return paths;
395
+ } else {
396
+ return [];
397
+ }
398
+ }
399
+ ////
400
+ get_all_paths_fr_to(src,dst) {
401
+ if (!this.has_nd(src) || !this.has_nd(dst)) return [];
402
+ if (src === dst) return [[src]];
403
+
404
+ var paths = [];
405
+ var unhandled = [];
406
+ // 初始化:从 src 的直连下游开始
407
+ for (var d of this.dns[src]) {
408
+ if (d === dst) {
409
+ paths.push([src, dst]);
410
+ } else {
411
+ unhandled.push([src, d]);
412
+ }
413
+ }
414
+
415
+ var next_unhandled = [];
416
+ while (unhandled.length > 0) {
417
+ for (var pl of unhandled) {
418
+ var last = pl[pl.length - 1];
419
+ for (var d of this.dns[last]) {
420
+ if (d === dst) {
421
+ // 到达终点,保存路径
422
+ paths.push([...pl, d]);
423
+ } else {
424
+ // 未到终点,继续扩展
425
+ next_unhandled.push([...pl, d]);
426
+ }
427
+ }
428
+ }
429
+ unhandled = next_unhandled;
430
+ next_unhandled = [];
431
+ }
432
+
433
+ return paths;
434
+ }
435
+ ////
436
+ del_all_iso_nds() {
437
+ //删除所有出度和入度均为0的节点
438
+ // 先收集所有孤立节点
439
+ const isolated = [];
440
+ for (const nd of this.nds) {
441
+ if (this.dns[nd].size === 0 && this.ups[nd].size === 0) {
442
+ isolated.push(nd);
443
+ }
444
+ }
445
+ // 再删除
446
+ for (const nd of isolated) {
447
+ this.del_nd(nd);
448
+ }
449
+ // 返回删除了多少个节点
450
+ return isolated.length;
451
+ }
452
+ ////
453
+ }
454
+
455
+
456
+ const creat = ()=>new G();
457
+ const sync_d2u_iter = (g,handle=(ndst,readonly_g)=>{}) =>{
458
+ var og = g;
459
+ g = g.clone();
460
+ while(true) {
461
+ var nds = g.ot0nds;
462
+ if(nds.size !== 0) {
463
+ handle(nds,og);
464
+ for(var nd of nds) {g.del_nd(nd)}
465
+ } else {
466
+ break;
467
+ }
468
+ }
469
+ }
470
+ const async_d2u_iter = async (g,handle=async(ndst,readonly_g)=>{}) => {
471
+ var og = g;
472
+ g = g.clone();
473
+ while(true) {
474
+ var nds = g.ot0nds;
475
+ if(nds.size !== 0) {
476
+ await handle(nds,og);
477
+ for(var nd of nds) {g.del_nd(nd)}
478
+ } else {
479
+ break;
480
+ }
481
+ }
482
+ }
483
+
484
+ const sync_u2d_iter = (g,handle=(ndst,readonly_g)=>{})=> {
485
+ var og = g;
486
+ g = g.clone();
487
+ while(true) {
488
+ var nds = g.in0nds;
489
+ if(nds.size !== 0) {
490
+ handle(nds,og);
491
+ for(var nd of nds) {g.del_nd(nd)}
492
+ } else {
493
+ break;
494
+ }
495
+ }
496
+ }
497
+ const async_u2d_iter = async (g,handle=async(ndst,readonly_g)=>{})=> {
498
+ var og = g;
499
+ g = g.clone();
500
+ while(true) {
501
+ var nds = g.in0nds;
502
+ if(nds.size !== 0) {
503
+ await handle(nds,og);
504
+ for(var nd of nds) {g.del_nd(nd)}
505
+ } else {
506
+ break;
507
+ }
508
+ }
509
+ }
510
+
511
+ module.exports = {
512
+ creat,
513
+ sync_d2u_iter,
514
+ async_d2u_iter,
515
+ sync_u2d_iter,
516
+ async_u2d_iter,
517
+ }
package/package.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "nv-dag-using-str-nd",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1"
7
+ },
8
+ "author": "",
9
+ "license": "ISC",
10
+ "description": ""
11
+ }