@nebulaaa/linked-list 0.0.0-stage → 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.
@@ -0,0 +1,406 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * 泛型单向链表节点
5
+ */
6
+ class LinkedListNode {
7
+ constructor(value) {
8
+ /** 指向下一个节点,末尾为 null */
9
+ this.Next = null;
10
+ this.Value = value;
11
+ }
12
+ }
13
+ /**
14
+ * 泛型链表
15
+ *
16
+ * 主要方法:
17
+ * - Push(value) : 在链表尾部追加节点
18
+ * - Pop() : 移除并返回最后一个节点的值(O(n))
19
+ * - Shift() : 移除并返回头节点的值(O(1))
20
+ * - Unshift(value) : 在链表头部插入节点(O(1))
21
+ * - Find(cb) : 根据回调函数查找第一个满足条件的节点
22
+ * - Remove(cb) : 删除第一个满足条件的节点
23
+ * - Each(cb) : 遍历所有节点
24
+ * - ToArray() : 转换为普通数组
25
+ * - Size / IsEmpty : 获取长度或判断是否为空
26
+ */
27
+ class LinkedList {
28
+ constructor() {
29
+ this._head = null;
30
+ this._tail = null;
31
+ this._size = 0;
32
+ }
33
+ /** 链表当前长度 */
34
+ get Size() {
35
+ return this._size;
36
+ }
37
+ /** 链表是否为空 */
38
+ get IsEmpty() {
39
+ return this._size === 0;
40
+ }
41
+ /** 在尾部追加节点 */
42
+ Push(value) {
43
+ const node = new LinkedListNode(value);
44
+ if (!this._head) {
45
+ this._head = this._tail = node;
46
+ }
47
+ else {
48
+ this._tail.Next = node;
49
+ this._tail = node;
50
+ }
51
+ this._size++;
52
+ return this;
53
+ }
54
+ /** 在头部插入节点 */
55
+ Unshift(value) {
56
+ const node = new LinkedListNode(value);
57
+ if (!this._head) {
58
+ this._head = this._tail = node;
59
+ }
60
+ else {
61
+ node.Next = this._head;
62
+ this._head = node;
63
+ }
64
+ this._size++;
65
+ return this;
66
+ }
67
+ /** 移除并返回头节点的值 */
68
+ Shift() {
69
+ if (!this._head)
70
+ return undefined;
71
+ const value = this._head.Value;
72
+ this._head = this._head.Next;
73
+ if (!this._head)
74
+ this._tail = null; // 链表变空
75
+ this._size--;
76
+ return value;
77
+ }
78
+ /** 移除并返回尾节点的值(需要遍历) */
79
+ Pop() {
80
+ if (!this._head)
81
+ return undefined;
82
+ if (this._head === this._tail) {
83
+ // 只有一个节点
84
+ const value = this._head.Value;
85
+ this._head = this._tail = null;
86
+ this._size = 0;
87
+ return value;
88
+ }
89
+ // 找到倒数第二个节点
90
+ let prev = this._head;
91
+ while (prev.Next && prev.Next !== this._tail) {
92
+ prev = prev.Next;
93
+ }
94
+ const value = this._tail.Value;
95
+ prev.Next = null;
96
+ this._tail = prev;
97
+ this._size--;
98
+ return value;
99
+ }
100
+ /** 根据回调函数查找第一个满足条件的节点 */
101
+ Find(predicate) {
102
+ let current = this._head;
103
+ let idx = 0;
104
+ while (current) {
105
+ if (predicate(current.Value, idx))
106
+ return current;
107
+ current = current.Next;
108
+ idx++;
109
+ }
110
+ return null;
111
+ }
112
+ /** 删除第一个满足条件的节点,返回是否成功 */
113
+ Remove(predicate) {
114
+ if (!this._head)
115
+ return false;
116
+ // 删除头节点
117
+ if (predicate(this._head.Value, 0)) {
118
+ this.Shift();
119
+ return true;
120
+ }
121
+ let prev = this._head;
122
+ let current = this._head.Next;
123
+ let idx = 1;
124
+ while (current) {
125
+ if (predicate(current.Value, idx)) {
126
+ prev.Next = current.Next;
127
+ if (current === this._tail)
128
+ this._tail = prev;
129
+ this._size--;
130
+ return true;
131
+ }
132
+ prev = current;
133
+ current = current.Next;
134
+ idx++;
135
+ }
136
+ return false;
137
+ }
138
+ /** 遍历所有节点;回调中新追加的节点不参与本次遍历,删除当前节点后沿原链继续 */
139
+ Each(callback) {
140
+ // 锚定起始尾节点:回调中 Push 会让原尾节点的 Next 指向新节点,
141
+ // 仅靠保存后继无法拦截,必须以起始尾节点为终点防止无限遍历
142
+ const end = this._tail;
143
+ let current = this._head;
144
+ let idx = 0;
145
+ while (current) {
146
+ // 提前保存后继:Remove 当前节点(其后继被改写)后也能继续沿原链走完
147
+ const next = current.Next;
148
+ callback(current.Value, idx);
149
+ if (current === end)
150
+ break;
151
+ current = next;
152
+ idx++;
153
+ }
154
+ }
155
+ /** 转换为普通数组 */
156
+ ToArray() {
157
+ const arr = [];
158
+ this.Each((v) => arr.push(v));
159
+ return arr;
160
+ }
161
+ /** 返回链表的字符串表示(调试用) */
162
+ toString() {
163
+ return this.ToArray().join(' -> ');
164
+ }
165
+ }
166
+
167
+ /**
168
+ * 泛型双向链表节点
169
+ */
170
+ class DoublyLinkedListNode {
171
+ constructor(value) {
172
+ /** 前驱节点,链表头部为 null */
173
+ this.Prev = null;
174
+ /** 后继节点,链表尾部为 null */
175
+ this.Next = null;
176
+ this.Value = value;
177
+ }
178
+ }
179
+ /**
180
+ * 泛型双向链表
181
+ *
182
+ * 常用 API(时间复杂度已标注):
183
+ * - Push(value) : O(1) 在尾部追加
184
+ * - Unshift(value) : O(1) 在头部插入
185
+ * - Pop() : O(1) 移除并返回尾部节点
186
+ * - Shift() : O(1) 移除并返回头部节点
187
+ * - InsertAfter(node, value) : O(1) 在指定节点后插入
188
+ * - InsertBefore(node, value): O(1) 在指定节点前插入
189
+ * - Remove(node) : O(n) 删除任意节点(含归属校验)
190
+ * - Find(cb) : O(n) 查找第一个满足条件的节点
191
+ * - Each(cb) : O(n) 遍历
192
+ * - ToArray() : O(n) 转为普通数组
193
+ */
194
+ class DoublyLinkedList {
195
+ constructor() {
196
+ this._head = null;
197
+ this._tail = null;
198
+ this._size = 0;
199
+ }
200
+ /** 链表当前长度 */
201
+ get Size() {
202
+ return this._size;
203
+ }
204
+ /** 链表是否为空 */
205
+ get IsEmpty() {
206
+ return this._size === 0;
207
+ }
208
+ /** 头节点(getter 只读,但节点字段可写;外部改写指针会破坏链表结构) */
209
+ get First() {
210
+ return this._head;
211
+ }
212
+ /** 尾节点(getter 只读,但节点字段可写;外部改写指针会破坏链表结构) */
213
+ get Last() {
214
+ return this._tail;
215
+ }
216
+ /** 在尾部追加节点 */
217
+ Push(value) {
218
+ const node = new DoublyLinkedListNode(value);
219
+ if (!this._tail) {
220
+ // 空链表
221
+ this._head = this._tail = node;
222
+ }
223
+ else {
224
+ node.Prev = this._tail;
225
+ this._tail.Next = node;
226
+ this._tail = node;
227
+ }
228
+ this._size++;
229
+ return this;
230
+ }
231
+ /** 在头部插入节点 */
232
+ Unshift(value) {
233
+ const node = new DoublyLinkedListNode(value);
234
+ if (!this._head) {
235
+ this._head = this._tail = node;
236
+ }
237
+ else {
238
+ node.Next = this._head;
239
+ this._head.Prev = node;
240
+ this._head = node;
241
+ }
242
+ this._size++;
243
+ return this;
244
+ }
245
+ /** 移除并返回头节点的值 */
246
+ Shift() {
247
+ if (!this._head)
248
+ return undefined;
249
+ const value = this._head.Value;
250
+ if (this._head === this._tail) {
251
+ // 只有一个节点
252
+ this._head = this._tail = null;
253
+ }
254
+ else {
255
+ const next = this._head.Next;
256
+ // 清理出链节点指针,缩短其引用链生命周期(与 Remove 行为一致)
257
+ this._head.Next = null;
258
+ next.Prev = null;
259
+ this._head = next;
260
+ }
261
+ this._size--;
262
+ return value;
263
+ }
264
+ /** 移除并返回尾节点的值 */
265
+ Pop() {
266
+ if (!this._tail)
267
+ return undefined;
268
+ const value = this._tail.Value;
269
+ if (this._head === this._tail) {
270
+ this._head = this._tail = null;
271
+ }
272
+ else {
273
+ const prev = this._tail.Prev;
274
+ // 清理出链节点指针,缩短其引用链生命周期(与 Remove 行为一致)
275
+ this._tail.Prev = null;
276
+ prev.Next = null;
277
+ this._tail = prev;
278
+ }
279
+ this._size--;
280
+ return value;
281
+ }
282
+ /**
283
+ * 在指定节点后插入新节点,返回新节点
284
+ * @tip 节点须属于本链表且未被移除,否则返回 null 且链表不受影响
285
+ */
286
+ InsertAfter(node, value) {
287
+ if (!this.owns(node))
288
+ return null;
289
+ const newNode = new DoublyLinkedListNode(value);
290
+ newNode.Prev = node;
291
+ newNode.Next = node.Next;
292
+ if (node.Next) {
293
+ node.Next.Prev = newNode;
294
+ }
295
+ else {
296
+ // node 是尾节点
297
+ this._tail = newNode;
298
+ }
299
+ node.Next = newNode;
300
+ this._size++;
301
+ return newNode;
302
+ }
303
+ /**
304
+ * 在指定节点前插入新节点,返回新节点
305
+ * @tip 节点须属于本链表且未被移除,否则返回 null 且链表不受影响
306
+ */
307
+ InsertBefore(node, value) {
308
+ if (!this.owns(node))
309
+ return null;
310
+ const newNode = new DoublyLinkedListNode(value);
311
+ newNode.Next = node;
312
+ newNode.Prev = node.Prev;
313
+ if (node.Prev) {
314
+ node.Prev.Next = newNode;
315
+ }
316
+ else {
317
+ // node 是头节点
318
+ this._head = newNode;
319
+ }
320
+ node.Prev = newNode;
321
+ this._size++;
322
+ return newNode;
323
+ }
324
+ /** 节点是否仍在链上(归属校验,O(n)) */
325
+ owns(node) {
326
+ let current = this._head;
327
+ while (current) {
328
+ if (current === node)
329
+ return true;
330
+ current = current.Next;
331
+ }
332
+ return false;
333
+ }
334
+ /**
335
+ * 删除任意节点,返回是否成功
336
+ * @tip 节点须属于本链表且未被移除,否则返回 false 且链表不受影响
337
+ */
338
+ Remove(node) {
339
+ // 归属校验:拦截来自其他链表或已被移除的节点,避免静默改写指针、错记 size
340
+ if (!node || !this.owns(node))
341
+ return false;
342
+ if (node.Prev) {
343
+ node.Prev.Next = node.Next;
344
+ }
345
+ else {
346
+ // 删除的是头节点
347
+ this._head = node.Next;
348
+ }
349
+ if (node.Next) {
350
+ node.Next.Prev = node.Prev;
351
+ }
352
+ else {
353
+ // 删除的是尾节点
354
+ this._tail = node.Prev;
355
+ }
356
+ // 清理被删节点的残留指针
357
+ node.Prev = null;
358
+ node.Next = null;
359
+ this._size--;
360
+ return true;
361
+ }
362
+ /** 根据回调函数查找第一个满足条件的节点 */
363
+ Find(predicate) {
364
+ let cur = this._head;
365
+ let idx = 0;
366
+ while (cur) {
367
+ if (predicate(cur.Value, idx))
368
+ return cur;
369
+ cur = cur.Next;
370
+ idx++;
371
+ }
372
+ return null;
373
+ }
374
+ /** 遍历所有节点;回调中新追加的节点不参与本次遍历,删除当前节点后沿原链继续 */
375
+ Each(callback) {
376
+ // 锚定起始尾节点:回调中 Push 会让原尾节点的 Next 指向新节点,
377
+ // 仅靠保存后继无法拦截,必须以起始尾节点为终点防止无限遍历
378
+ const end = this._tail;
379
+ let cur = this._head;
380
+ let idx = 0;
381
+ while (cur) {
382
+ // 提前保存后继:Remove 当前节点(会清空其指针)后也能继续沿原链走完
383
+ const next = cur.Next;
384
+ callback(cur.Value, idx);
385
+ if (cur === end)
386
+ break;
387
+ cur = next;
388
+ idx++;
389
+ }
390
+ }
391
+ /** 转换为普通数组(调试/展示用) */
392
+ ToArray() {
393
+ const arr = [];
394
+ this.Each((v) => arr.push(v));
395
+ return arr;
396
+ }
397
+ /** 便于调试的字符串表示 */
398
+ toString() {
399
+ return this.ToArray().join(' <-> ');
400
+ }
401
+ }
402
+
403
+ exports.DoublyLinkedList = DoublyLinkedList;
404
+ exports.DoublyLinkedListNode = DoublyLinkedListNode;
405
+ exports.LinkedList = LinkedList;
406
+ exports.LinkedListNode = LinkedListNode;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * 泛型单向链表节点
3
+ */
4
+ declare class LinkedListNode<T> {
5
+ /** 节点保存的值 */
6
+ Value: T;
7
+ /** 指向下一个节点,末尾为 null */
8
+ Next: LinkedListNode<T> | null;
9
+ constructor(value: T);
10
+ }
11
+ /**
12
+ * 泛型链表
13
+ *
14
+ * 主要方法:
15
+ * - Push(value) : 在链表尾部追加节点
16
+ * - Pop() : 移除并返回最后一个节点的值(O(n))
17
+ * - Shift() : 移除并返回头节点的值(O(1))
18
+ * - Unshift(value) : 在链表头部插入节点(O(1))
19
+ * - Find(cb) : 根据回调函数查找第一个满足条件的节点
20
+ * - Remove(cb) : 删除第一个满足条件的节点
21
+ * - Each(cb) : 遍历所有节点
22
+ * - ToArray() : 转换为普通数组
23
+ * - Size / IsEmpty : 获取长度或判断是否为空
24
+ */
25
+ declare class LinkedList<T> {
26
+ private _head;
27
+ private _tail;
28
+ private _size;
29
+ /** 链表当前长度 */
30
+ get Size(): number;
31
+ /** 链表是否为空 */
32
+ get IsEmpty(): boolean;
33
+ /** 在尾部追加节点 */
34
+ Push(value: T): this;
35
+ /** 在头部插入节点 */
36
+ Unshift(value: T): this;
37
+ /** 移除并返回头节点的值 */
38
+ Shift(): T | undefined;
39
+ /** 移除并返回尾节点的值(需要遍历) */
40
+ Pop(): T | undefined;
41
+ /** 根据回调函数查找第一个满足条件的节点 */
42
+ Find(predicate: (value: T, index: number) => boolean): LinkedListNode<T> | null;
43
+ /** 删除第一个满足条件的节点,返回是否成功 */
44
+ Remove(predicate: (value: T, index: number) => boolean): boolean;
45
+ /** 遍历所有节点;回调中新追加的节点不参与本次遍历,删除当前节点后沿原链继续 */
46
+ Each(callback: (value: T, index: number) => void): void;
47
+ /** 转换为普通数组 */
48
+ ToArray(): T[];
49
+ /** 返回链表的字符串表示(调试用) */
50
+ toString(): string;
51
+ }
52
+
53
+ /**
54
+ * 泛型双向链表节点
55
+ */
56
+ declare class DoublyLinkedListNode<T> {
57
+ /** 保存的值 */
58
+ Value: T;
59
+ /** 前驱节点,链表头部为 null */
60
+ Prev: DoublyLinkedListNode<T> | null;
61
+ /** 后继节点,链表尾部为 null */
62
+ Next: DoublyLinkedListNode<T> | null;
63
+ constructor(value: T);
64
+ }
65
+ /**
66
+ * 泛型双向链表
67
+ *
68
+ * 常用 API(时间复杂度已标注):
69
+ * - Push(value) : O(1) 在尾部追加
70
+ * - Unshift(value) : O(1) 在头部插入
71
+ * - Pop() : O(1) 移除并返回尾部节点
72
+ * - Shift() : O(1) 移除并返回头部节点
73
+ * - InsertAfter(node, value) : O(1) 在指定节点后插入
74
+ * - InsertBefore(node, value): O(1) 在指定节点前插入
75
+ * - Remove(node) : O(n) 删除任意节点(含归属校验)
76
+ * - Find(cb) : O(n) 查找第一个满足条件的节点
77
+ * - Each(cb) : O(n) 遍历
78
+ * - ToArray() : O(n) 转为普通数组
79
+ */
80
+ declare class DoublyLinkedList<T> {
81
+ private _head;
82
+ private _tail;
83
+ private _size;
84
+ /** 链表当前长度 */
85
+ get Size(): number;
86
+ /** 链表是否为空 */
87
+ get IsEmpty(): boolean;
88
+ /** 头节点(getter 只读,但节点字段可写;外部改写指针会破坏链表结构) */
89
+ get First(): DoublyLinkedListNode<T> | null;
90
+ /** 尾节点(getter 只读,但节点字段可写;外部改写指针会破坏链表结构) */
91
+ get Last(): DoublyLinkedListNode<T> | null;
92
+ /** 在尾部追加节点 */
93
+ Push(value: T): this;
94
+ /** 在头部插入节点 */
95
+ Unshift(value: T): this;
96
+ /** 移除并返回头节点的值 */
97
+ Shift(): T | undefined;
98
+ /** 移除并返回尾节点的值 */
99
+ Pop(): T | undefined;
100
+ /**
101
+ * 在指定节点后插入新节点,返回新节点
102
+ * @tip 节点须属于本链表且未被移除,否则返回 null 且链表不受影响
103
+ */
104
+ InsertAfter(node: DoublyLinkedListNode<T>, value: T): DoublyLinkedListNode<T> | null;
105
+ /**
106
+ * 在指定节点前插入新节点,返回新节点
107
+ * @tip 节点须属于本链表且未被移除,否则返回 null 且链表不受影响
108
+ */
109
+ InsertBefore(node: DoublyLinkedListNode<T>, value: T): DoublyLinkedListNode<T> | null;
110
+ /** 节点是否仍在链上(归属校验,O(n)) */
111
+ private owns;
112
+ /**
113
+ * 删除任意节点,返回是否成功
114
+ * @tip 节点须属于本链表且未被移除,否则返回 false 且链表不受影响
115
+ */
116
+ Remove(node: DoublyLinkedListNode<T>): boolean;
117
+ /** 根据回调函数查找第一个满足条件的节点 */
118
+ Find(predicate: (value: T, index: number) => boolean): DoublyLinkedListNode<T> | null;
119
+ /** 遍历所有节点;回调中新追加的节点不参与本次遍历,删除当前节点后沿原链继续 */
120
+ Each(callback: (value: T, index: number) => void): void;
121
+ /** 转换为普通数组(调试/展示用) */
122
+ ToArray(): T[];
123
+ /** 便于调试的字符串表示 */
124
+ toString(): string;
125
+ }
126
+
127
+ export { DoublyLinkedList, DoublyLinkedListNode, LinkedList, LinkedListNode };
@@ -0,0 +1 @@
1
+ "use strict";class t{constructor(t){this.Next=null,this.Value=t}}class e{constructor(t){this.Prev=null,this.Next=null,this.Value=t}}exports.DoublyLinkedList=class{constructor(){this._head=null,this._tail=null,this._size=0}get Size(){return this._size}get IsEmpty(){return 0===this._size}get First(){return this._head}get Last(){return this._tail}Push(t){const i=new e(t);return this._tail?(i.Prev=this._tail,this._tail.Next=i,this._tail=i):this._head=this._tail=i,this._size++,this}Unshift(t){const i=new e(t);return this._head?(i.Next=this._head,this._head.Prev=i,this._head=i):this._head=this._tail=i,this._size++,this}Shift(){if(!this._head)return;const t=this._head.Value;if(this._head===this._tail)this._head=this._tail=null;else{const t=this._head.Next;this._head.Next=null,t.Prev=null,this._head=t}return this._size--,t}Pop(){if(!this._tail)return;const t=this._tail.Value;if(this._head===this._tail)this._head=this._tail=null;else{const t=this._tail.Prev;this._tail.Prev=null,t.Next=null,this._tail=t}return this._size--,t}InsertAfter(t,i){if(!this.owns(t))return null;const s=new e(i);return s.Prev=t,s.Next=t.Next,t.Next?t.Next.Prev=s:this._tail=s,t.Next=s,this._size++,s}InsertBefore(t,i){if(!this.owns(t))return null;const s=new e(i);return s.Next=t,s.Prev=t.Prev,t.Prev?t.Prev.Next=s:this._head=s,t.Prev=s,this._size++,s}owns(t){let e=this._head;for(;e;){if(e===t)return!0;e=e.Next}return!1}Remove(t){return!(!t||!this.owns(t))&&(t.Prev?t.Prev.Next=t.Next:this._head=t.Next,t.Next?t.Next.Prev=t.Prev:this._tail=t.Prev,t.Prev=null,t.Next=null,this._size--,!0)}Find(t){let e=this._head,i=0;for(;e;){if(t(e.Value,i))return e;e=e.Next,i++}return null}Each(t){const e=this._tail;let i=this._head,s=0;for(;i;){const h=i.Next;if(t(i.Value,s),i===e)break;i=h,s++}}ToArray(){const t=[];return this.Each(e=>t.push(e)),t}toString(){return this.ToArray().join(" <-> ")}},exports.DoublyLinkedListNode=e,exports.LinkedList=class{constructor(){this._head=null,this._tail=null,this._size=0}get Size(){return this._size}get IsEmpty(){return 0===this._size}Push(e){const i=new t(e);return this._head?(this._tail.Next=i,this._tail=i):this._head=this._tail=i,this._size++,this}Unshift(e){const i=new t(e);return this._head?(i.Next=this._head,this._head=i):this._head=this._tail=i,this._size++,this}Shift(){if(!this._head)return;const t=this._head.Value;return this._head=this._head.Next,this._head||(this._tail=null),this._size--,t}Pop(){if(!this._head)return;if(this._head===this._tail){const t=this._head.Value;return this._head=this._tail=null,this._size=0,t}let t=this._head;for(;t.Next&&t.Next!==this._tail;)t=t.Next;const e=this._tail.Value;return t.Next=null,this._tail=t,this._size--,e}Find(t){let e=this._head,i=0;for(;e;){if(t(e.Value,i))return e;e=e.Next,i++}return null}Remove(t){if(!this._head)return!1;if(t(this._head.Value,0))return this.Shift(),!0;let e=this._head,i=this._head.Next,s=1;for(;i;){if(t(i.Value,s))return e.Next=i.Next,i===this._tail&&(this._tail=e),this._size--,!0;e=i,i=i.Next,s++}return!1}Each(t){const e=this._tail;let i=this._head,s=0;for(;i;){const h=i.Next;if(t(i.Value,s),i===e)break;i=h,s++}}ToArray(){const t=[];return this.Each(e=>t.push(e)),t}toString(){return this.ToArray().join(" -> ")}},exports.LinkedListNode=t;
@@ -0,0 +1 @@
1
+ class t{constructor(t){this.Next=null,this.Value=t}}class e{constructor(){this._head=null,this._tail=null,this._size=0}get Size(){return this._size}get IsEmpty(){return 0===this._size}Push(e){const i=new t(e);return this._head?(this._tail.Next=i,this._tail=i):this._head=this._tail=i,this._size++,this}Unshift(e){const i=new t(e);return this._head?(i.Next=this._head,this._head=i):this._head=this._tail=i,this._size++,this}Shift(){if(!this._head)return;const t=this._head.Value;return this._head=this._head.Next,this._head||(this._tail=null),this._size--,t}Pop(){if(!this._head)return;if(this._head===this._tail){const t=this._head.Value;return this._head=this._tail=null,this._size=0,t}let t=this._head;for(;t.Next&&t.Next!==this._tail;)t=t.Next;const e=this._tail.Value;return t.Next=null,this._tail=t,this._size--,e}Find(t){let e=this._head,i=0;for(;e;){if(t(e.Value,i))return e;e=e.Next,i++}return null}Remove(t){if(!this._head)return!1;if(t(this._head.Value,0))return this.Shift(),!0;let e=this._head,i=this._head.Next,s=1;for(;i;){if(t(i.Value,s))return e.Next=i.Next,i===this._tail&&(this._tail=e),this._size--,!0;e=i,i=i.Next,s++}return!1}Each(t){const e=this._tail;let i=this._head,s=0;for(;i;){const h=i.Next;if(t(i.Value,s),i===e)break;i=h,s++}}ToArray(){const t=[];return this.Each(e=>t.push(e)),t}toString(){return this.ToArray().join(" -> ")}}class i{constructor(t){this.Prev=null,this.Next=null,this.Value=t}}class s{constructor(){this._head=null,this._tail=null,this._size=0}get Size(){return this._size}get IsEmpty(){return 0===this._size}get First(){return this._head}get Last(){return this._tail}Push(t){const e=new i(t);return this._tail?(e.Prev=this._tail,this._tail.Next=e,this._tail=e):this._head=this._tail=e,this._size++,this}Unshift(t){const e=new i(t);return this._head?(e.Next=this._head,this._head.Prev=e,this._head=e):this._head=this._tail=e,this._size++,this}Shift(){if(!this._head)return;const t=this._head.Value;if(this._head===this._tail)this._head=this._tail=null;else{const t=this._head.Next;this._head.Next=null,t.Prev=null,this._head=t}return this._size--,t}Pop(){if(!this._tail)return;const t=this._tail.Value;if(this._head===this._tail)this._head=this._tail=null;else{const t=this._tail.Prev;this._tail.Prev=null,t.Next=null,this._tail=t}return this._size--,t}InsertAfter(t,e){if(!this.owns(t))return null;const s=new i(e);return s.Prev=t,s.Next=t.Next,t.Next?t.Next.Prev=s:this._tail=s,t.Next=s,this._size++,s}InsertBefore(t,e){if(!this.owns(t))return null;const s=new i(e);return s.Next=t,s.Prev=t.Prev,t.Prev?t.Prev.Next=s:this._head=s,t.Prev=s,this._size++,s}owns(t){let e=this._head;for(;e;){if(e===t)return!0;e=e.Next}return!1}Remove(t){return!(!t||!this.owns(t))&&(t.Prev?t.Prev.Next=t.Next:this._head=t.Next,t.Next?t.Next.Prev=t.Prev:this._tail=t.Prev,t.Prev=null,t.Next=null,this._size--,!0)}Find(t){let e=this._head,i=0;for(;e;){if(t(e.Value,i))return e;e=e.Next,i++}return null}Each(t){const e=this._tail;let i=this._head,s=0;for(;i;){const h=i.Next;if(t(i.Value,s),i===e)break;i=h,s++}}ToArray(){const t=[];return this.Each(e=>t.push(e)),t}toString(){return this.ToArray().join(" <-> ")}}export{s as DoublyLinkedList,i as DoublyLinkedListNode,e as LinkedList,t as LinkedListNode};
@@ -0,0 +1,401 @@
1
+ /**
2
+ * 泛型单向链表节点
3
+ */
4
+ class LinkedListNode {
5
+ constructor(value) {
6
+ /** 指向下一个节点,末尾为 null */
7
+ this.Next = null;
8
+ this.Value = value;
9
+ }
10
+ }
11
+ /**
12
+ * 泛型链表
13
+ *
14
+ * 主要方法:
15
+ * - Push(value) : 在链表尾部追加节点
16
+ * - Pop() : 移除并返回最后一个节点的值(O(n))
17
+ * - Shift() : 移除并返回头节点的值(O(1))
18
+ * - Unshift(value) : 在链表头部插入节点(O(1))
19
+ * - Find(cb) : 根据回调函数查找第一个满足条件的节点
20
+ * - Remove(cb) : 删除第一个满足条件的节点
21
+ * - Each(cb) : 遍历所有节点
22
+ * - ToArray() : 转换为普通数组
23
+ * - Size / IsEmpty : 获取长度或判断是否为空
24
+ */
25
+ class LinkedList {
26
+ constructor() {
27
+ this._head = null;
28
+ this._tail = null;
29
+ this._size = 0;
30
+ }
31
+ /** 链表当前长度 */
32
+ get Size() {
33
+ return this._size;
34
+ }
35
+ /** 链表是否为空 */
36
+ get IsEmpty() {
37
+ return this._size === 0;
38
+ }
39
+ /** 在尾部追加节点 */
40
+ Push(value) {
41
+ const node = new LinkedListNode(value);
42
+ if (!this._head) {
43
+ this._head = this._tail = node;
44
+ }
45
+ else {
46
+ this._tail.Next = node;
47
+ this._tail = node;
48
+ }
49
+ this._size++;
50
+ return this;
51
+ }
52
+ /** 在头部插入节点 */
53
+ Unshift(value) {
54
+ const node = new LinkedListNode(value);
55
+ if (!this._head) {
56
+ this._head = this._tail = node;
57
+ }
58
+ else {
59
+ node.Next = this._head;
60
+ this._head = node;
61
+ }
62
+ this._size++;
63
+ return this;
64
+ }
65
+ /** 移除并返回头节点的值 */
66
+ Shift() {
67
+ if (!this._head)
68
+ return undefined;
69
+ const value = this._head.Value;
70
+ this._head = this._head.Next;
71
+ if (!this._head)
72
+ this._tail = null; // 链表变空
73
+ this._size--;
74
+ return value;
75
+ }
76
+ /** 移除并返回尾节点的值(需要遍历) */
77
+ Pop() {
78
+ if (!this._head)
79
+ return undefined;
80
+ if (this._head === this._tail) {
81
+ // 只有一个节点
82
+ const value = this._head.Value;
83
+ this._head = this._tail = null;
84
+ this._size = 0;
85
+ return value;
86
+ }
87
+ // 找到倒数第二个节点
88
+ let prev = this._head;
89
+ while (prev.Next && prev.Next !== this._tail) {
90
+ prev = prev.Next;
91
+ }
92
+ const value = this._tail.Value;
93
+ prev.Next = null;
94
+ this._tail = prev;
95
+ this._size--;
96
+ return value;
97
+ }
98
+ /** 根据回调函数查找第一个满足条件的节点 */
99
+ Find(predicate) {
100
+ let current = this._head;
101
+ let idx = 0;
102
+ while (current) {
103
+ if (predicate(current.Value, idx))
104
+ return current;
105
+ current = current.Next;
106
+ idx++;
107
+ }
108
+ return null;
109
+ }
110
+ /** 删除第一个满足条件的节点,返回是否成功 */
111
+ Remove(predicate) {
112
+ if (!this._head)
113
+ return false;
114
+ // 删除头节点
115
+ if (predicate(this._head.Value, 0)) {
116
+ this.Shift();
117
+ return true;
118
+ }
119
+ let prev = this._head;
120
+ let current = this._head.Next;
121
+ let idx = 1;
122
+ while (current) {
123
+ if (predicate(current.Value, idx)) {
124
+ prev.Next = current.Next;
125
+ if (current === this._tail)
126
+ this._tail = prev;
127
+ this._size--;
128
+ return true;
129
+ }
130
+ prev = current;
131
+ current = current.Next;
132
+ idx++;
133
+ }
134
+ return false;
135
+ }
136
+ /** 遍历所有节点;回调中新追加的节点不参与本次遍历,删除当前节点后沿原链继续 */
137
+ Each(callback) {
138
+ // 锚定起始尾节点:回调中 Push 会让原尾节点的 Next 指向新节点,
139
+ // 仅靠保存后继无法拦截,必须以起始尾节点为终点防止无限遍历
140
+ const end = this._tail;
141
+ let current = this._head;
142
+ let idx = 0;
143
+ while (current) {
144
+ // 提前保存后继:Remove 当前节点(其后继被改写)后也能继续沿原链走完
145
+ const next = current.Next;
146
+ callback(current.Value, idx);
147
+ if (current === end)
148
+ break;
149
+ current = next;
150
+ idx++;
151
+ }
152
+ }
153
+ /** 转换为普通数组 */
154
+ ToArray() {
155
+ const arr = [];
156
+ this.Each((v) => arr.push(v));
157
+ return arr;
158
+ }
159
+ /** 返回链表的字符串表示(调试用) */
160
+ toString() {
161
+ return this.ToArray().join(' -> ');
162
+ }
163
+ }
164
+
165
+ /**
166
+ * 泛型双向链表节点
167
+ */
168
+ class DoublyLinkedListNode {
169
+ constructor(value) {
170
+ /** 前驱节点,链表头部为 null */
171
+ this.Prev = null;
172
+ /** 后继节点,链表尾部为 null */
173
+ this.Next = null;
174
+ this.Value = value;
175
+ }
176
+ }
177
+ /**
178
+ * 泛型双向链表
179
+ *
180
+ * 常用 API(时间复杂度已标注):
181
+ * - Push(value) : O(1) 在尾部追加
182
+ * - Unshift(value) : O(1) 在头部插入
183
+ * - Pop() : O(1) 移除并返回尾部节点
184
+ * - Shift() : O(1) 移除并返回头部节点
185
+ * - InsertAfter(node, value) : O(1) 在指定节点后插入
186
+ * - InsertBefore(node, value): O(1) 在指定节点前插入
187
+ * - Remove(node) : O(n) 删除任意节点(含归属校验)
188
+ * - Find(cb) : O(n) 查找第一个满足条件的节点
189
+ * - Each(cb) : O(n) 遍历
190
+ * - ToArray() : O(n) 转为普通数组
191
+ */
192
+ class DoublyLinkedList {
193
+ constructor() {
194
+ this._head = null;
195
+ this._tail = null;
196
+ this._size = 0;
197
+ }
198
+ /** 链表当前长度 */
199
+ get Size() {
200
+ return this._size;
201
+ }
202
+ /** 链表是否为空 */
203
+ get IsEmpty() {
204
+ return this._size === 0;
205
+ }
206
+ /** 头节点(getter 只读,但节点字段可写;外部改写指针会破坏链表结构) */
207
+ get First() {
208
+ return this._head;
209
+ }
210
+ /** 尾节点(getter 只读,但节点字段可写;外部改写指针会破坏链表结构) */
211
+ get Last() {
212
+ return this._tail;
213
+ }
214
+ /** 在尾部追加节点 */
215
+ Push(value) {
216
+ const node = new DoublyLinkedListNode(value);
217
+ if (!this._tail) {
218
+ // 空链表
219
+ this._head = this._tail = node;
220
+ }
221
+ else {
222
+ node.Prev = this._tail;
223
+ this._tail.Next = node;
224
+ this._tail = node;
225
+ }
226
+ this._size++;
227
+ return this;
228
+ }
229
+ /** 在头部插入节点 */
230
+ Unshift(value) {
231
+ const node = new DoublyLinkedListNode(value);
232
+ if (!this._head) {
233
+ this._head = this._tail = node;
234
+ }
235
+ else {
236
+ node.Next = this._head;
237
+ this._head.Prev = node;
238
+ this._head = node;
239
+ }
240
+ this._size++;
241
+ return this;
242
+ }
243
+ /** 移除并返回头节点的值 */
244
+ Shift() {
245
+ if (!this._head)
246
+ return undefined;
247
+ const value = this._head.Value;
248
+ if (this._head === this._tail) {
249
+ // 只有一个节点
250
+ this._head = this._tail = null;
251
+ }
252
+ else {
253
+ const next = this._head.Next;
254
+ // 清理出链节点指针,缩短其引用链生命周期(与 Remove 行为一致)
255
+ this._head.Next = null;
256
+ next.Prev = null;
257
+ this._head = next;
258
+ }
259
+ this._size--;
260
+ return value;
261
+ }
262
+ /** 移除并返回尾节点的值 */
263
+ Pop() {
264
+ if (!this._tail)
265
+ return undefined;
266
+ const value = this._tail.Value;
267
+ if (this._head === this._tail) {
268
+ this._head = this._tail = null;
269
+ }
270
+ else {
271
+ const prev = this._tail.Prev;
272
+ // 清理出链节点指针,缩短其引用链生命周期(与 Remove 行为一致)
273
+ this._tail.Prev = null;
274
+ prev.Next = null;
275
+ this._tail = prev;
276
+ }
277
+ this._size--;
278
+ return value;
279
+ }
280
+ /**
281
+ * 在指定节点后插入新节点,返回新节点
282
+ * @tip 节点须属于本链表且未被移除,否则返回 null 且链表不受影响
283
+ */
284
+ InsertAfter(node, value) {
285
+ if (!this.owns(node))
286
+ return null;
287
+ const newNode = new DoublyLinkedListNode(value);
288
+ newNode.Prev = node;
289
+ newNode.Next = node.Next;
290
+ if (node.Next) {
291
+ node.Next.Prev = newNode;
292
+ }
293
+ else {
294
+ // node 是尾节点
295
+ this._tail = newNode;
296
+ }
297
+ node.Next = newNode;
298
+ this._size++;
299
+ return newNode;
300
+ }
301
+ /**
302
+ * 在指定节点前插入新节点,返回新节点
303
+ * @tip 节点须属于本链表且未被移除,否则返回 null 且链表不受影响
304
+ */
305
+ InsertBefore(node, value) {
306
+ if (!this.owns(node))
307
+ return null;
308
+ const newNode = new DoublyLinkedListNode(value);
309
+ newNode.Next = node;
310
+ newNode.Prev = node.Prev;
311
+ if (node.Prev) {
312
+ node.Prev.Next = newNode;
313
+ }
314
+ else {
315
+ // node 是头节点
316
+ this._head = newNode;
317
+ }
318
+ node.Prev = newNode;
319
+ this._size++;
320
+ return newNode;
321
+ }
322
+ /** 节点是否仍在链上(归属校验,O(n)) */
323
+ owns(node) {
324
+ let current = this._head;
325
+ while (current) {
326
+ if (current === node)
327
+ return true;
328
+ current = current.Next;
329
+ }
330
+ return false;
331
+ }
332
+ /**
333
+ * 删除任意节点,返回是否成功
334
+ * @tip 节点须属于本链表且未被移除,否则返回 false 且链表不受影响
335
+ */
336
+ Remove(node) {
337
+ // 归属校验:拦截来自其他链表或已被移除的节点,避免静默改写指针、错记 size
338
+ if (!node || !this.owns(node))
339
+ return false;
340
+ if (node.Prev) {
341
+ node.Prev.Next = node.Next;
342
+ }
343
+ else {
344
+ // 删除的是头节点
345
+ this._head = node.Next;
346
+ }
347
+ if (node.Next) {
348
+ node.Next.Prev = node.Prev;
349
+ }
350
+ else {
351
+ // 删除的是尾节点
352
+ this._tail = node.Prev;
353
+ }
354
+ // 清理被删节点的残留指针
355
+ node.Prev = null;
356
+ node.Next = null;
357
+ this._size--;
358
+ return true;
359
+ }
360
+ /** 根据回调函数查找第一个满足条件的节点 */
361
+ Find(predicate) {
362
+ let cur = this._head;
363
+ let idx = 0;
364
+ while (cur) {
365
+ if (predicate(cur.Value, idx))
366
+ return cur;
367
+ cur = cur.Next;
368
+ idx++;
369
+ }
370
+ return null;
371
+ }
372
+ /** 遍历所有节点;回调中新追加的节点不参与本次遍历,删除当前节点后沿原链继续 */
373
+ Each(callback) {
374
+ // 锚定起始尾节点:回调中 Push 会让原尾节点的 Next 指向新节点,
375
+ // 仅靠保存后继无法拦截,必须以起始尾节点为终点防止无限遍历
376
+ const end = this._tail;
377
+ let cur = this._head;
378
+ let idx = 0;
379
+ while (cur) {
380
+ // 提前保存后继:Remove 当前节点(会清空其指针)后也能继续沿原链走完
381
+ const next = cur.Next;
382
+ callback(cur.Value, idx);
383
+ if (cur === end)
384
+ break;
385
+ cur = next;
386
+ idx++;
387
+ }
388
+ }
389
+ /** 转换为普通数组(调试/展示用) */
390
+ ToArray() {
391
+ const arr = [];
392
+ this.Each((v) => arr.push(v));
393
+ return arr;
394
+ }
395
+ /** 便于调试的字符串表示 */
396
+ toString() {
397
+ return this.ToArray().join(' <-> ');
398
+ }
399
+ }
400
+
401
+ export { DoublyLinkedList, DoublyLinkedListNode, LinkedList, LinkedListNode };
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@nebulaaa/linked-list",
3
+ "version": "1.0.0",
4
+ "description": "链表扩展包",
5
+ "author": "doooreyn",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "linked-list",
9
+ "nebulaaa"
10
+ ],
11
+ "engines": {
12
+ "node": ">=16.0.0",
13
+ "npm": ">=10.0.0"
14
+ },
15
+ "main": "./linked-list.cjs",
16
+ "module": "./linked-list.mjs",
17
+ "types": "./linked-list.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./linked-list.d.ts",
21
+ "require": "./linked-list.cjs",
22
+ "import": "./linked-list.mjs",
23
+ "default": "./linked-list.cjs"
24
+ },
25
+ "./min": {
26
+ "require": "./linked-list.min.cjs",
27
+ "import": "./linked-list.min.mjs"
28
+ }
29
+ },
30
+ "publishConfig": {
31
+ "registry": "https://registry.npmjs.org/",
32
+ "access": "public"
33
+ }
34
+ }
package/package.json CHANGED
@@ -1,6 +1,66 @@
1
- {
2
- "name": "@nebulaaa/linked-list",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
1
+ {
2
+ "name": "@nebulaaa/linked-list",
3
+ "version": "1.0.0",
4
+ "description": "链表扩展包",
5
+ "main": "./dist/linked-list.cjs",
6
+ "module": "./dist/linked-list.mjs",
7
+ "types": "./dist/linked-list.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "require": "./dist/linked-list.cjs",
11
+ "import": "./dist/linked-list.mjs",
12
+ "types": "./dist/linked-list.d.ts",
13
+ "default": "./dist/linked-list.cjs"
14
+ },
15
+ "./min": {
16
+ "require": "./dist/linked-list.min.cjs",
17
+ "import": "./dist/linked-list.min.mjs"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "scripts": {
24
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
25
+ "build": "npm run clean && rollup -c rollup.config.mjs && node ../scripts/write-dist-package.mjs linked-list",
26
+ "test": "jest"
27
+ },
28
+ "keywords": [
29
+ "linked-list",
30
+ "nebulaaa"
31
+ ],
32
+ "author": "doooreyn",
33
+ "license": "MIT",
34
+ "devEngines": {
35
+ "packageManager": {
36
+ "name": "npm",
37
+ "version": ">=10.0.0",
38
+ "onFail": "warn"
39
+ }
40
+ },
41
+ "engines": {
42
+ "node": ">=16.0.0",
43
+ "npm": ">=10.0.0"
44
+ },
45
+ "publishConfig": {
46
+ "registry": "https://registry.npmjs.org/",
47
+ "access": "public"
48
+ },
49
+ "allowScripts": {
50
+ "unrs-resolver@1.12.2": true,
51
+ "@parcel/watcher@2.6.0": true
52
+ },
53
+ "devDependencies": {
54
+ "@rollup/plugin-terser": "^1.0.2",
55
+ "@rollup/plugin-typescript": "^12.1.2",
56
+ "@types/jest": "^30.0.0",
57
+ "@types/node": "^22.10.2",
58
+ "jest": "^30.5.2",
59
+ "rollup": "^4.28.1",
60
+ "rollup-plugin-dts": "^6.1.1",
61
+ "ts-jest": "^29.1.2",
62
+ "ts-node": "^10.9.2",
63
+ "typescript": "5.7.3"
64
+ },
65
+ "dependencies": {}
66
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.