data-structure-typed 2.2.2 → 2.2.4
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/CHANGELOG.md +3 -1
- package/README.md +355 -1672
- package/README_CN.md +509 -0
- package/SECURITY.md +962 -11
- package/SECURITY.zh-CN.md +966 -0
- package/SPECIFICATION.md +689 -30
- package/SPECIFICATION.zh-CN.md +715 -0
- package/SPONSOR.zh-CN.md +62 -0
- package/SPONSOR_POLISHED.md +62 -0
- package/benchmark/report.html +1 -1
- package/benchmark/report.json +215 -172
- package/dist/cjs/index.cjs +245 -72
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +246 -72
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +245 -72
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +246 -72
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/data-structures/binary-tree/avl-tree-counter.d.ts +2 -2
- package/dist/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +5 -5
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +98 -5
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +103 -7
- package/dist/types/data-structures/binary-tree/bst.d.ts +202 -39
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +86 -37
- package/dist/types/data-structures/binary-tree/tree-counter.d.ts +4 -5
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +7 -7
- package/dist/types/data-structures/graph/directed-graph.d.ts +126 -1
- package/dist/types/data-structures/graph/undirected-graph.d.ts +160 -1
- package/dist/types/data-structures/hash/hash-map.d.ts +110 -27
- package/dist/types/data-structures/heap/heap.d.ts +107 -58
- package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +72 -404
- package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +121 -5
- package/dist/types/data-structures/queue/deque.d.ts +95 -67
- package/dist/types/data-structures/queue/queue.d.ts +90 -34
- package/dist/types/data-structures/stack/stack.d.ts +58 -40
- package/dist/types/data-structures/trie/trie.d.ts +109 -47
- package/dist/types/interfaces/binary-tree.d.ts +1 -0
- package/dist/types/types/data-structures/binary-tree/bst.d.ts +5 -5
- package/dist/umd/data-structure-typed.js +246 -72
- package/dist/umd/data-structure-typed.js.map +1 -1
- package/dist/umd/data-structure-typed.min.js +3 -3
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +3 -2
- package/src/data-structures/binary-tree/avl-tree-counter.ts +1 -2
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +7 -8
- package/src/data-structures/binary-tree/avl-tree.ts +100 -7
- package/src/data-structures/binary-tree/binary-tree.ts +117 -7
- package/src/data-structures/binary-tree/bst.ts +431 -93
- package/src/data-structures/binary-tree/red-black-tree.ts +85 -37
- package/src/data-structures/binary-tree/tree-counter.ts +5 -7
- package/src/data-structures/binary-tree/tree-multi-map.ts +9 -10
- package/src/data-structures/graph/directed-graph.ts +126 -1
- package/src/data-structures/graph/undirected-graph.ts +160 -1
- package/src/data-structures/hash/hash-map.ts +110 -27
- package/src/data-structures/heap/heap.ts +107 -58
- package/src/data-structures/linked-list/doubly-linked-list.ts +72 -404
- package/src/data-structures/linked-list/singly-linked-list.ts +121 -5
- package/src/data-structures/queue/deque.ts +95 -67
- package/src/data-structures/queue/queue.ts +90 -34
- package/src/data-structures/stack/stack.ts +58 -40
- package/src/data-structures/trie/trie.ts +109 -47
- package/src/interfaces/binary-tree.ts +2 -0
- package/src/types/data-structures/binary-tree/bst.ts +5 -5
- package/test/performance/benchmark-runner.ts +14 -11
- package/test/performance/data-structures/binary-tree/avl-tree.test.ts +8 -8
- package/test/performance/data-structures/binary-tree/binary-tree-overall.test.ts +8 -8
- package/test/performance/data-structures/binary-tree/binary-tree.test.ts +6 -6
- package/test/performance/data-structures/binary-tree/bst.test.ts +5 -5
- package/test/performance/data-structures/binary-tree/red-black-tree.test.ts +10 -10
- package/test/performance/reportor.ts +2 -1
- package/test/performance/single-suite-runner.ts +7 -4
- package/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts +2 -2
- package/test/unit/data-structures/binary-tree/avl-tree.test.ts +117 -0
- package/test/unit/data-structures/binary-tree/binary-tree.test.ts +166 -0
- package/test/unit/data-structures/binary-tree/bst.test.ts +771 -16
- package/test/unit/data-structures/binary-tree/overall.test.ts +2 -2
- package/test/unit/data-structures/binary-tree/red-black-tree.test.ts +90 -38
- package/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +2 -2
- package/test/unit/data-structures/graph/directed-graph.test.ts +133 -0
- package/test/unit/data-structures/graph/undirected-graph.test.ts +167 -0
- package/test/unit/data-structures/hash/hash-map.test.ts +149 -3
- package/test/unit/data-structures/heap/heap.test.ts +182 -47
- package/test/unit/data-structures/linked-list/doubly-linked-list.test.ts +118 -14
- package/test/unit/data-structures/linked-list/singly-linked-list.test.ts +121 -0
- package/test/unit/data-structures/queue/deque.test.ts +98 -67
- package/test/unit/data-structures/queue/queue.test.ts +85 -51
- package/test/unit/data-structures/stack/stack.test.ts +142 -33
- package/test/unit/data-structures/trie/trie.test.ts +135 -39
- package/tsup.leetcode.config.js +99 -0
- package/typedoc.json +2 -1
- package/POSTS_zh-CN.md +0 -54
- package/README_zh-CN.md +0 -1208
- package/SPECIFICATION_zh-CN.md +0 -81
|
@@ -452,18 +452,106 @@ describe('FibonacciHeap Stress Test', () => {
|
|
|
452
452
|
});
|
|
453
453
|
|
|
454
454
|
describe('classic use', () => {
|
|
455
|
-
it('@example
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
455
|
+
it('@example basic Heap creation and add operation', () => {
|
|
456
|
+
// Create a min heap (default)
|
|
457
|
+
const minHeap = new Heap([5, 3, 7, 1, 9, 2]);
|
|
458
|
+
|
|
459
|
+
// Verify size
|
|
460
|
+
expect(minHeap.size).toBe(6);
|
|
461
|
+
|
|
462
|
+
// Add new element
|
|
463
|
+
minHeap.add(4);
|
|
464
|
+
expect(minHeap.size).toBe(7);
|
|
465
|
+
|
|
466
|
+
// Min heap property: smallest element at root
|
|
467
|
+
const min = minHeap.peek();
|
|
468
|
+
expect(min).toBe(1);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it('@example Heap with custom comparator (MaxHeap behavior)', () => {
|
|
472
|
+
interface Task {
|
|
473
|
+
id: number;
|
|
474
|
+
priority: number;
|
|
475
|
+
name: string;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Custom comparator for max heap behavior (higher priority first)
|
|
479
|
+
const tasks: Task[] = [
|
|
480
|
+
{ id: 1, priority: 5, name: 'Email' },
|
|
481
|
+
{ id: 2, priority: 3, name: 'Chat' },
|
|
482
|
+
{ id: 3, priority: 8, name: 'Alert' }
|
|
483
|
+
];
|
|
484
|
+
|
|
485
|
+
const maxHeap = new Heap(tasks, {
|
|
486
|
+
comparator: (a: Task, b: Task) => b.priority - a.priority
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
expect(maxHeap.size).toBe(3);
|
|
490
|
+
|
|
491
|
+
// Peek returns highest priority task
|
|
492
|
+
const topTask = maxHeap.peek();
|
|
493
|
+
expect(topTask?.priority).toBe(8);
|
|
494
|
+
expect(topTask?.name).toBe('Alert');
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it('@example Heap for event processing with priority', () => {
|
|
498
|
+
interface Event {
|
|
499
|
+
id: number;
|
|
500
|
+
type: 'critical' | 'warning' | 'info';
|
|
501
|
+
timestamp: number;
|
|
502
|
+
message: string;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Custom priority: critical > warning > info
|
|
506
|
+
const priorityMap = { critical: 3, warning: 2, info: 1 };
|
|
507
|
+
|
|
508
|
+
const eventHeap = new Heap<Event>([], {
|
|
509
|
+
comparator: (a: Event, b: Event) => {
|
|
510
|
+
const priorityA = priorityMap[a.type];
|
|
511
|
+
const priorityB = priorityMap[b.type];
|
|
512
|
+
return priorityB - priorityA; // Higher priority first
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
// Add events in random order
|
|
517
|
+
eventHeap.add({ id: 1, type: 'info', timestamp: 100, message: 'User logged in' });
|
|
518
|
+
eventHeap.add({ id: 2, type: 'critical', timestamp: 101, message: 'Server down' });
|
|
519
|
+
eventHeap.add({ id: 3, type: 'warning', timestamp: 102, message: 'High memory' });
|
|
520
|
+
eventHeap.add({ id: 4, type: 'info', timestamp: 103, message: 'Cache cleared' });
|
|
521
|
+
eventHeap.add({ id: 5, type: 'critical', timestamp: 104, message: 'Database error' });
|
|
522
|
+
|
|
523
|
+
expect(eventHeap.size).toBe(5);
|
|
524
|
+
|
|
525
|
+
// Process events by priority (critical first)
|
|
526
|
+
const processedOrder: Event[] = [];
|
|
527
|
+
while (eventHeap.size > 0) {
|
|
528
|
+
const event = eventHeap.poll();
|
|
529
|
+
if (event) {
|
|
530
|
+
processedOrder.push(event);
|
|
461
531
|
}
|
|
462
|
-
return sorted;
|
|
463
532
|
}
|
|
464
533
|
|
|
465
|
-
|
|
466
|
-
expect(
|
|
534
|
+
// Verify critical events came first
|
|
535
|
+
expect(processedOrder[0].type).toBe('critical');
|
|
536
|
+
expect(processedOrder[1].type).toBe('critical');
|
|
537
|
+
expect(processedOrder[2].type).toBe('warning');
|
|
538
|
+
expect(processedOrder[3].type).toBe('info');
|
|
539
|
+
expect(processedOrder[4].type).toBe('info');
|
|
540
|
+
|
|
541
|
+
// Verify O(log n) operations
|
|
542
|
+
const newHeap = new Heap<number>([5, 3, 7, 1]);
|
|
543
|
+
|
|
544
|
+
// Add - O(log n)
|
|
545
|
+
newHeap.add(2);
|
|
546
|
+
expect(newHeap.size).toBe(5);
|
|
547
|
+
|
|
548
|
+
// Poll - O(log n)
|
|
549
|
+
const removed = newHeap.poll();
|
|
550
|
+
expect(removed).toBe(1);
|
|
551
|
+
|
|
552
|
+
// Peek - O(1)
|
|
553
|
+
const top = newHeap.peek();
|
|
554
|
+
expect(top).toBe(2);
|
|
467
555
|
});
|
|
468
556
|
|
|
469
557
|
it('@example Use Heap to solve top k problems', () => {
|
|
@@ -480,44 +568,6 @@ describe('classic use', () => {
|
|
|
480
568
|
expect(topKElements(numbers, 3)).toEqual([15, 10, 5]);
|
|
481
569
|
});
|
|
482
570
|
|
|
483
|
-
it('@example Use Heap to merge sorted sequences', () => {
|
|
484
|
-
function mergeSortedSequences(sequences: number[][]): number[] {
|
|
485
|
-
const heap = new Heap<{ value: number; seqIndex: number; itemIndex: number }>([], {
|
|
486
|
-
comparator: (a, b) => a.value - b.value // Min heap
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
// Initialize heap
|
|
490
|
-
sequences.forEach((seq, seqIndex) => {
|
|
491
|
-
if (seq.length) {
|
|
492
|
-
heap.add({ value: seq[0], seqIndex, itemIndex: 0 });
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
const merged: number[] = [];
|
|
497
|
-
while (!heap.isEmpty()) {
|
|
498
|
-
const { value, seqIndex, itemIndex } = heap.poll()!;
|
|
499
|
-
merged.push(value);
|
|
500
|
-
|
|
501
|
-
if (itemIndex + 1 < sequences[seqIndex].length) {
|
|
502
|
-
heap.add({
|
|
503
|
-
value: sequences[seqIndex][itemIndex + 1],
|
|
504
|
-
seqIndex,
|
|
505
|
-
itemIndex: itemIndex + 1
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
return merged;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
const sequences = [
|
|
514
|
-
[1, 4, 7],
|
|
515
|
-
[2, 5, 8],
|
|
516
|
-
[3, 6, 9]
|
|
517
|
-
];
|
|
518
|
-
expect(mergeSortedSequences(sequences)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
|
519
|
-
});
|
|
520
|
-
|
|
521
571
|
it('@example Use Heap to dynamically maintain the median', () => {
|
|
522
572
|
class MedianFinder {
|
|
523
573
|
private low: MaxHeap<number>; // Max heap, stores the smaller half
|
|
@@ -622,4 +672,89 @@ describe('classic use', () => {
|
|
|
622
672
|
]);
|
|
623
673
|
expect(scheduleTasks(tasks, 2)).toEqual(expectedMap);
|
|
624
674
|
});
|
|
675
|
+
|
|
676
|
+
it('Use Heap to sort an array', () => {
|
|
677
|
+
function heapSort(arr: number[]): number[] {
|
|
678
|
+
const heap = new Heap<number>(arr, { comparator: (a, b) => a - b });
|
|
679
|
+
const sorted: number[] = [];
|
|
680
|
+
while (!heap.isEmpty()) {
|
|
681
|
+
sorted.push(heap.poll()!); // Poll minimum element
|
|
682
|
+
}
|
|
683
|
+
return sorted;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const array = [5, 3, 8, 4, 1, 2];
|
|
687
|
+
expect(heapSort(array)).toEqual([1, 2, 3, 4, 5, 8]);
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
it('Heap getHeight and structure', () => {
|
|
691
|
+
const heap = new Heap<number>([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
|
|
692
|
+
|
|
693
|
+
// Complete binary tree (heap property)
|
|
694
|
+
expect(heap.size).toBe(15);
|
|
695
|
+
|
|
696
|
+
// Verify min heap property (parent <= children)
|
|
697
|
+
const min = heap.peek();
|
|
698
|
+
expect(min).toBe(1);
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
it('Heap poll and priority extraction', () => {
|
|
702
|
+
// Create max heap (higher numbers first)
|
|
703
|
+
const heap = new Heap([3, 7, 1, 9, 5], {
|
|
704
|
+
comparator: (a: number, b: number) => b - a
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
expect(heap.size).toBe(5);
|
|
708
|
+
|
|
709
|
+
// Poll removes and returns the highest priority element
|
|
710
|
+
const first = heap.poll();
|
|
711
|
+
expect(first).toBe(9);
|
|
712
|
+
expect(heap.size).toBe(4);
|
|
713
|
+
|
|
714
|
+
const second = heap.poll();
|
|
715
|
+
expect(second).toBe(7);
|
|
716
|
+
expect(heap.size).toBe(3);
|
|
717
|
+
|
|
718
|
+
// Remaining elements are still in heap order
|
|
719
|
+
const third = heap.peek();
|
|
720
|
+
expect(third).toBe(5);
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
it('Use Heap to merge sorted sequences', () => {
|
|
724
|
+
function mergeSortedSequences(sequences: number[][]): number[] {
|
|
725
|
+
const heap = new Heap<{ value: number; seqIndex: number; itemIndex: number }>([], {
|
|
726
|
+
comparator: (a, b) => a.value - b.value // Min heap
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
// Initialize heap
|
|
730
|
+
sequences.forEach((seq, seqIndex) => {
|
|
731
|
+
if (seq.length) {
|
|
732
|
+
heap.add({ value: seq[0], seqIndex, itemIndex: 0 });
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
const merged: number[] = [];
|
|
737
|
+
while (!heap.isEmpty()) {
|
|
738
|
+
const { value, seqIndex, itemIndex } = heap.poll()!;
|
|
739
|
+
merged.push(value);
|
|
740
|
+
|
|
741
|
+
if (itemIndex + 1 < sequences[seqIndex].length) {
|
|
742
|
+
heap.add({
|
|
743
|
+
value: sequences[seqIndex][itemIndex + 1],
|
|
744
|
+
seqIndex,
|
|
745
|
+
itemIndex: itemIndex + 1
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
return merged;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const sequences = [
|
|
754
|
+
[1, 4, 7],
|
|
755
|
+
[2, 5, 8],
|
|
756
|
+
[3, 6, 9]
|
|
757
|
+
];
|
|
758
|
+
expect(mergeSortedSequences(sequences)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
|
759
|
+
});
|
|
625
760
|
});
|
|
@@ -612,17 +612,51 @@ describe('iterable methods', () => {
|
|
|
612
612
|
});
|
|
613
613
|
|
|
614
614
|
describe('classic use', () => {
|
|
615
|
-
it('@example
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
{ type: 'insert', content: 'second line of text' },
|
|
619
|
-
{ type: 'delete', content: 'delete the first line' }
|
|
620
|
-
];
|
|
621
|
-
const editorHistory = new DoublyLinkedList<{ type: string; content: string }>(actions);
|
|
615
|
+
it('@example basic DoublyLinkedList creation and push operation', () => {
|
|
616
|
+
// Create a simple DoublyLinkedList with initial values
|
|
617
|
+
const list = new DoublyLinkedList([1, 2, 3, 4, 5]);
|
|
622
618
|
|
|
623
|
-
|
|
624
|
-
expect(
|
|
625
|
-
|
|
619
|
+
// Verify the list maintains insertion order
|
|
620
|
+
expect([...list]).toEqual([1, 2, 3, 4, 5]);
|
|
621
|
+
|
|
622
|
+
// Check length
|
|
623
|
+
expect(list.length).toBe(5);
|
|
624
|
+
|
|
625
|
+
// Push a new element to the end
|
|
626
|
+
list.push(6);
|
|
627
|
+
expect(list.length).toBe(6);
|
|
628
|
+
expect([...list]).toEqual([1, 2, 3, 4, 5, 6]);
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
it('@example DoublyLinkedList pop and shift operations', () => {
|
|
632
|
+
const list = new DoublyLinkedList<number>([10, 20, 30, 40, 50]);
|
|
633
|
+
|
|
634
|
+
// Pop removes from the end
|
|
635
|
+
const last = list.pop();
|
|
636
|
+
expect(last).toBe(50);
|
|
637
|
+
|
|
638
|
+
// Shift removes from the beginning
|
|
639
|
+
const first = list.shift();
|
|
640
|
+
expect(first).toBe(10);
|
|
641
|
+
|
|
642
|
+
// Verify remaining elements
|
|
643
|
+
expect([...list]).toEqual([20, 30, 40]);
|
|
644
|
+
expect(list.length).toBe(3);
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
it('@example DoublyLinkedList for...of iteration and map operation', () => {
|
|
648
|
+
const list = new DoublyLinkedList<number>([1, 2, 3, 4, 5]);
|
|
649
|
+
|
|
650
|
+
// Iterate through list
|
|
651
|
+
const doubled = list.map(value => value * 2);
|
|
652
|
+
expect(doubled.length).toBe(5);
|
|
653
|
+
|
|
654
|
+
// Use for...of loop
|
|
655
|
+
const result: number[] = [];
|
|
656
|
+
for (const item of list) {
|
|
657
|
+
result.push(item);
|
|
658
|
+
}
|
|
659
|
+
expect(result).toEqual([1, 2, 3, 4, 5]);
|
|
626
660
|
});
|
|
627
661
|
|
|
628
662
|
it('@example Browser history', () => {
|
|
@@ -637,7 +671,77 @@ describe('classic use', () => {
|
|
|
637
671
|
expect(browserHistory.last).toBe('search page');
|
|
638
672
|
});
|
|
639
673
|
|
|
640
|
-
it('@example
|
|
674
|
+
it('@example DoublyLinkedList for LRU cache implementation', () => {
|
|
675
|
+
interface CacheEntry {
|
|
676
|
+
key: string;
|
|
677
|
+
value: string;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Simulate LRU cache using DoublyLinkedList
|
|
681
|
+
// DoublyLinkedList is perfect because:
|
|
682
|
+
// - O(1) delete from any position
|
|
683
|
+
// - O(1) push to end
|
|
684
|
+
// - Bidirectional traversal for LRU policy
|
|
685
|
+
|
|
686
|
+
const cacheList = new DoublyLinkedList<CacheEntry>();
|
|
687
|
+
const maxSize = 3;
|
|
688
|
+
|
|
689
|
+
// Add cache entries
|
|
690
|
+
cacheList.push({ key: 'user:1', value: 'Alice' });
|
|
691
|
+
cacheList.push({ key: 'user:2', value: 'Bob' });
|
|
692
|
+
cacheList.push({ key: 'user:3', value: 'Charlie' });
|
|
693
|
+
|
|
694
|
+
// Try to add a new entry when cache is full
|
|
695
|
+
if (cacheList.length >= maxSize) {
|
|
696
|
+
// Remove the oldest (first) entry
|
|
697
|
+
const evicted = cacheList.shift();
|
|
698
|
+
expect(evicted?.key).toBe('user:1');
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Add new entry
|
|
702
|
+
cacheList.push({ key: 'user:4', value: 'Diana' });
|
|
703
|
+
|
|
704
|
+
// Verify current cache state
|
|
705
|
+
expect(cacheList.length).toBe(3);
|
|
706
|
+
const cachedKeys = [...cacheList].map(entry => entry.key);
|
|
707
|
+
expect(cachedKeys).toEqual(['user:2', 'user:3', 'user:4']);
|
|
708
|
+
|
|
709
|
+
// Access entry (in real LRU, this would move it to end)
|
|
710
|
+
const foundEntry = [...cacheList].find(entry => entry.key === 'user:2');
|
|
711
|
+
expect(foundEntry?.value).toBe('Bob');
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it('DoublyLinkedList unshift and bidirectional traversal', () => {
|
|
715
|
+
const list = new DoublyLinkedList<number>([20, 30, 40]);
|
|
716
|
+
|
|
717
|
+
// Unshift adds to the beginning
|
|
718
|
+
list.unshift(10);
|
|
719
|
+
expect([...list]).toEqual([10, 20, 30, 40]);
|
|
720
|
+
|
|
721
|
+
// Access elements by index (bidirectional advantage)
|
|
722
|
+
const second = list.at(1);
|
|
723
|
+
expect(second).toBe(20);
|
|
724
|
+
|
|
725
|
+
const last = list.at(list.length - 1);
|
|
726
|
+
expect(last).toBe(40);
|
|
727
|
+
|
|
728
|
+
expect(list.length).toBe(4);
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
it('text editor operation history', () => {
|
|
732
|
+
const actions = [
|
|
733
|
+
{ type: 'insert', content: 'first line of text' },
|
|
734
|
+
{ type: 'insert', content: 'second line of text' },
|
|
735
|
+
{ type: 'delete', content: 'delete the first line' }
|
|
736
|
+
];
|
|
737
|
+
const editorHistory = new DoublyLinkedList<{ type: string; content: string }>(actions);
|
|
738
|
+
|
|
739
|
+
expect(editorHistory.last?.type).toBe('delete');
|
|
740
|
+
expect(editorHistory.pop()?.content).toBe('delete the first line');
|
|
741
|
+
expect(editorHistory.last?.type).toBe('insert');
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
it('Use DoublyLinkedList to implement music player', () => {
|
|
641
745
|
// Define the Song interface
|
|
642
746
|
interface Song {
|
|
643
747
|
title: string;
|
|
@@ -763,7 +867,7 @@ describe('classic use', () => {
|
|
|
763
867
|
]);
|
|
764
868
|
});
|
|
765
869
|
|
|
766
|
-
it('
|
|
870
|
+
it('Use DoublyLinkedList to implement LRU cache', () => {
|
|
767
871
|
interface CacheEntry<K, V> {
|
|
768
872
|
key: K;
|
|
769
873
|
value: V;
|
|
@@ -926,7 +1030,7 @@ describe('classic use', () => {
|
|
|
926
1030
|
expect(cache.isEmpty).toBe(true);
|
|
927
1031
|
});
|
|
928
1032
|
|
|
929
|
-
it('
|
|
1033
|
+
it('Finding lyrics by timestamp in Coldplay\'s "Fix You"', () => {
|
|
930
1034
|
// Create a DoublyLinkedList to store song lyrics with timestamps
|
|
931
1035
|
const lyricsList = new DoublyLinkedList<{ time: number; text: string }>();
|
|
932
1036
|
|
|
@@ -967,7 +1071,7 @@ describe('classic use', () => {
|
|
|
967
1071
|
expect(lateTimeLyric?.text).toBe('And I will try to fix you');
|
|
968
1072
|
});
|
|
969
1073
|
|
|
970
|
-
it('
|
|
1074
|
+
it('Cpu process schedules', () => {
|
|
971
1075
|
class Process {
|
|
972
1076
|
constructor(
|
|
973
1077
|
public id: number,
|
|
@@ -646,6 +646,127 @@ describe('iterable methods', () => {
|
|
|
646
646
|
});
|
|
647
647
|
|
|
648
648
|
describe('classic uses', () => {
|
|
649
|
+
it('@example basic SinglyLinkedList creation and push operation', () => {
|
|
650
|
+
// Create a simple SinglyLinkedList with initial values
|
|
651
|
+
const list = new SinglyLinkedList([1, 2, 3, 4, 5]);
|
|
652
|
+
|
|
653
|
+
// Verify the list maintains insertion order
|
|
654
|
+
expect([...list]).toEqual([1, 2, 3, 4, 5]);
|
|
655
|
+
|
|
656
|
+
// Check length
|
|
657
|
+
expect(list.length).toBe(5);
|
|
658
|
+
|
|
659
|
+
// Push a new element to the end
|
|
660
|
+
list.push(6);
|
|
661
|
+
expect(list.length).toBe(6);
|
|
662
|
+
expect([...list]).toEqual([1, 2, 3, 4, 5, 6]);
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it('@example SinglyLinkedList pop and shift operations', () => {
|
|
666
|
+
const list = new SinglyLinkedList<number>([10, 20, 30, 40, 50]);
|
|
667
|
+
|
|
668
|
+
// Pop removes from the end
|
|
669
|
+
const last = list.pop();
|
|
670
|
+
expect(last).toBe(50);
|
|
671
|
+
|
|
672
|
+
// Shift removes from the beginning
|
|
673
|
+
const first = list.shift();
|
|
674
|
+
expect(first).toBe(10);
|
|
675
|
+
|
|
676
|
+
// Verify remaining elements
|
|
677
|
+
expect([...list]).toEqual([20, 30, 40]);
|
|
678
|
+
expect(list.length).toBe(3);
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
it('@example SinglyLinkedList unshift and forward traversal', () => {
|
|
682
|
+
const list = new SinglyLinkedList<number>([20, 30, 40]);
|
|
683
|
+
|
|
684
|
+
// Unshift adds to the beginning
|
|
685
|
+
list.unshift(10);
|
|
686
|
+
expect([...list]).toEqual([10, 20, 30, 40]);
|
|
687
|
+
|
|
688
|
+
// Access elements (forward traversal only for singly linked)
|
|
689
|
+
const second = list.at(1);
|
|
690
|
+
expect(second).toBe(20);
|
|
691
|
+
|
|
692
|
+
// SinglyLinkedList allows forward iteration only
|
|
693
|
+
const elements: number[] = [];
|
|
694
|
+
for (const item of list) {
|
|
695
|
+
elements.push(item);
|
|
696
|
+
}
|
|
697
|
+
expect(elements).toEqual([10, 20, 30, 40]);
|
|
698
|
+
|
|
699
|
+
expect(list.length).toBe(4);
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
it('@example SinglyLinkedList filter and map operations', () => {
|
|
703
|
+
const list = new SinglyLinkedList<number>([1, 2, 3, 4, 5]);
|
|
704
|
+
|
|
705
|
+
// Filter even numbers
|
|
706
|
+
const filtered = list.filter(value => value % 2 === 0);
|
|
707
|
+
expect(filtered.length).toBe(2);
|
|
708
|
+
|
|
709
|
+
// Map to double values
|
|
710
|
+
const doubled = list.map(value => value * 2);
|
|
711
|
+
expect(doubled.length).toBe(5);
|
|
712
|
+
|
|
713
|
+
// Use reduce to sum
|
|
714
|
+
const sum = list.reduce((acc, value) => acc + value, 0);
|
|
715
|
+
expect(sum).toBe(15);
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
it('@example SinglyLinkedList for sequentially processed data stream', () => {
|
|
719
|
+
interface LogEntry {
|
|
720
|
+
timestamp: number;
|
|
721
|
+
level: 'INFO' | 'WARN' | 'ERROR';
|
|
722
|
+
message: string;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// SinglyLinkedList is ideal for sequential processing where you only need forward iteration
|
|
726
|
+
// O(1) insertion/deletion at head, O(n) for tail operations
|
|
727
|
+
const logStream = new SinglyLinkedList<LogEntry>();
|
|
728
|
+
|
|
729
|
+
// Simulate incoming log entries
|
|
730
|
+
const entries: LogEntry[] = [
|
|
731
|
+
{ timestamp: 1000, level: 'INFO', message: 'Server started' },
|
|
732
|
+
{ timestamp: 1100, level: 'WARN', message: 'Memory usage high' },
|
|
733
|
+
{ timestamp: 1200, level: 'ERROR', message: 'Connection failed' },
|
|
734
|
+
{ timestamp: 1300, level: 'INFO', message: 'Connection restored' }
|
|
735
|
+
];
|
|
736
|
+
|
|
737
|
+
// Add entries to the stream
|
|
738
|
+
for (const entry of entries) {
|
|
739
|
+
logStream.push(entry);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
expect(logStream.length).toBe(4);
|
|
743
|
+
|
|
744
|
+
// Process logs sequentially (only forward iteration needed)
|
|
745
|
+
const processedLogs: string[] = [];
|
|
746
|
+
for (const log of logStream) {
|
|
747
|
+
processedLogs.push(`[${log.level}] ${log.message}`);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
expect(processedLogs).toEqual([
|
|
751
|
+
'[INFO] Server started',
|
|
752
|
+
'[WARN] Memory usage high',
|
|
753
|
+
'[ERROR] Connection failed',
|
|
754
|
+
'[INFO] Connection restored'
|
|
755
|
+
]);
|
|
756
|
+
|
|
757
|
+
// Get first log (O(1) - direct head access)
|
|
758
|
+
const firstLog = logStream.at(0);
|
|
759
|
+
expect(firstLog?.message).toBe('Server started');
|
|
760
|
+
|
|
761
|
+
// Remove oldest log (O(1) operation at head)
|
|
762
|
+
const removed = logStream.shift();
|
|
763
|
+
expect(removed?.message).toBe('Server started');
|
|
764
|
+
expect(logStream.length).toBe(3);
|
|
765
|
+
|
|
766
|
+
// Remaining logs still maintain order for sequential processing
|
|
767
|
+
expect(logStream.length).toBe(3);
|
|
768
|
+
});
|
|
769
|
+
|
|
649
770
|
it('@example implementation of a basic text editor', () => {
|
|
650
771
|
class TextEditor {
|
|
651
772
|
private content: SinglyLinkedList<string>;
|