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
|
@@ -980,7 +980,7 @@ describe('BST operations test recursively', () => {
|
|
|
980
980
|
|
|
981
981
|
if (isTestStackOverflow) {
|
|
982
982
|
it('should getLeftMost', () => {
|
|
983
|
-
const bst = new BST<number>([]
|
|
983
|
+
const bst = new BST<number>([]);
|
|
984
984
|
for (let i = 1; i <= SYSTEM_MAX_CALL_STACK; i++) bst.add(i);
|
|
985
985
|
|
|
986
986
|
expect(() => {
|
|
@@ -1015,7 +1015,7 @@ describe('BST isBST', function () {
|
|
|
1015
1015
|
|
|
1016
1016
|
it('isBST when variant is Max', () => {
|
|
1017
1017
|
const bst = new BST<number, number>([1, 2, 3, 9, 8, 5, 6, 7, 4], {
|
|
1018
|
-
|
|
1018
|
+
comparator: (a, b) => b - a,
|
|
1019
1019
|
});
|
|
1020
1020
|
bst.addMany([1, 2, 3, 9, 8, 5, 6, 7, 4]);
|
|
1021
1021
|
expect(bst.isBST()).toBe(true);
|
|
@@ -1537,19 +1537,16 @@ describe('BST iterative methods not map mode test', () => {
|
|
|
1537
1537
|
});
|
|
1538
1538
|
|
|
1539
1539
|
describe('BST constructor and comparator edge cases', () => {
|
|
1540
|
-
it('should support
|
|
1540
|
+
it('should support comparator', () => {
|
|
1541
1541
|
const bst = new BST<number>([], {
|
|
1542
|
-
|
|
1543
|
-
isReverse: true
|
|
1542
|
+
comparator: (a, b) => b - a,
|
|
1544
1543
|
});
|
|
1545
1544
|
bst.add(1);
|
|
1546
1545
|
bst.add(2);
|
|
1547
|
-
expect(bst.isReverse).toBe(true);
|
|
1548
|
-
expect(bst['_specifyComparable']).toBeDefined();
|
|
1549
1546
|
expect([...bst.keys()]).toEqual([2, 1]);
|
|
1550
1547
|
});
|
|
1551
1548
|
|
|
1552
|
-
it('should throw if compare object key without
|
|
1549
|
+
it('should throw if compare object key without comparator', () => {
|
|
1553
1550
|
const bst = new BST<any>();
|
|
1554
1551
|
expect(() => bst.comparator({ a: 1 }, { a: 2 })).toThrow();
|
|
1555
1552
|
});
|
|
@@ -1616,7 +1613,677 @@ describe('BST _keyValueNodeOrEntryToNodeAndValue edge', () => {
|
|
|
1616
1613
|
});
|
|
1617
1614
|
});
|
|
1618
1615
|
|
|
1616
|
+
describe('BST lowerBound and upperBound', () => {
|
|
1617
|
+
let bst: BST<number, string>;
|
|
1618
|
+
|
|
1619
|
+
beforeEach(() => {
|
|
1620
|
+
// Create a BST with keys: [10, 5, 15, 3, 7, 12, 20, 1, 4, 6, 8, 11, 13, 18, 25]
|
|
1621
|
+
bst = new BST<number, string>([10, 5, 15, 3, 7, 12, 20, 1, 4, 6, 8, 11, 13, 18, 25]);
|
|
1622
|
+
});
|
|
1623
|
+
|
|
1624
|
+
describe('lowerBound', () => {
|
|
1625
|
+
it('should return the node with exact key match', () => {
|
|
1626
|
+
// Test for key that exists in tree
|
|
1627
|
+
const node = bst.lowerBound(10);
|
|
1628
|
+
expect(node).toBeDefined();
|
|
1629
|
+
expect(node?.key).toBe(10);
|
|
1630
|
+
});
|
|
1631
|
+
|
|
1632
|
+
it('should return the smallest node >= key when exact match not found', () => {
|
|
1633
|
+
// Test for key 9 (doesn't exist, should return 10)
|
|
1634
|
+
const node = bst.lowerBound(9);
|
|
1635
|
+
expect(node).toBeDefined();
|
|
1636
|
+
expect(node?.key).toBe(10);
|
|
1637
|
+
});
|
|
1638
|
+
|
|
1639
|
+
it('should return node with key >= search key from multiple candidates', () => {
|
|
1640
|
+
// Test for key 6 (exists, should return 6)
|
|
1641
|
+
const node = bst.lowerBound(6);
|
|
1642
|
+
expect(node).toBeDefined();
|
|
1643
|
+
expect(node?.key).toBe(6);
|
|
1644
|
+
|
|
1645
|
+
// Test for key 7 (exists, should return 7)
|
|
1646
|
+
const node2 = bst.lowerBound(7);
|
|
1647
|
+
expect(node2).toBeDefined();
|
|
1648
|
+
expect(node2?.key).toBe(7);
|
|
1649
|
+
});
|
|
1650
|
+
|
|
1651
|
+
it('should return smallest key >= search key for non-existent key in middle range', () => {
|
|
1652
|
+
// Test for key 14 (doesn't exist, should return 15)
|
|
1653
|
+
const node = bst.lowerBound(14);
|
|
1654
|
+
expect(node).toBeDefined();
|
|
1655
|
+
expect(node?.key).toBe(15);
|
|
1656
|
+
|
|
1657
|
+
// Test for key 11.5 (doesn't exist, should return 12)
|
|
1658
|
+
const node2 = bst.lowerBound(11.5);
|
|
1659
|
+
expect(node2).toBeDefined();
|
|
1660
|
+
expect(node2?.key).toBe(12);
|
|
1661
|
+
});
|
|
1662
|
+
|
|
1663
|
+
it('should return smallest key when search key is less than minimum', () => {
|
|
1664
|
+
// Test for key 0 (should return 1, the minimum)
|
|
1665
|
+
const node = bst.lowerBound(0);
|
|
1666
|
+
expect(node).toBeDefined();
|
|
1667
|
+
expect(node?.key).toBe(1);
|
|
1668
|
+
|
|
1669
|
+
// Test for key -100 (should return 1, the minimum)
|
|
1670
|
+
const node2 = bst.lowerBound(-100);
|
|
1671
|
+
expect(node2).toBeDefined();
|
|
1672
|
+
expect(node2?.key).toBe(1);
|
|
1673
|
+
});
|
|
1674
|
+
|
|
1675
|
+
it('should return undefined when search key is greater than maximum', () => {
|
|
1676
|
+
// Test for key 100 (no key >= 100, should return undefined)
|
|
1677
|
+
const node = bst.lowerBound(100);
|
|
1678
|
+
expect(node).toBeUndefined();
|
|
1679
|
+
|
|
1680
|
+
// Test for key 26 (no key >= 26, should return undefined)
|
|
1681
|
+
const node2 = bst.lowerBound(26);
|
|
1682
|
+
expect(node2).toBeUndefined();
|
|
1683
|
+
});
|
|
1684
|
+
|
|
1685
|
+
it('should return correct node for edge cases', () => {
|
|
1686
|
+
// Test for key 25 (maximum, should return 25)
|
|
1687
|
+
const node = bst.lowerBound(25);
|
|
1688
|
+
expect(node).toBeDefined();
|
|
1689
|
+
expect(node?.key).toBe(25);
|
|
1690
|
+
|
|
1691
|
+
// Test for key 1 (minimum, should return 1)
|
|
1692
|
+
const node2 = bst.lowerBound(1);
|
|
1693
|
+
expect(node2).toBeDefined();
|
|
1694
|
+
expect(node2?.key).toBe(1);
|
|
1695
|
+
});
|
|
1696
|
+
});
|
|
1697
|
+
|
|
1698
|
+
describe('upperBound', () => {
|
|
1699
|
+
it('should return the smallest key > search key', () => {
|
|
1700
|
+
// Test for key 10 (exists, should return next key > 10, which is 11)
|
|
1701
|
+
const node = bst.upperBound(10);
|
|
1702
|
+
expect(node).toBeDefined();
|
|
1703
|
+
expect(node?.key).toBe(11);
|
|
1704
|
+
});
|
|
1705
|
+
|
|
1706
|
+
it('should return the smallest key > search key for non-existent key', () => {
|
|
1707
|
+
// Test for key 9 (doesn't exist, should return 10, the smallest key > 9)
|
|
1708
|
+
const node = bst.upperBound(9);
|
|
1709
|
+
expect(node).toBeDefined();
|
|
1710
|
+
expect(node?.key).toBe(10);
|
|
1711
|
+
|
|
1712
|
+
// Test for key 14 (doesn't exist, should return 15)
|
|
1713
|
+
const node2 = bst.upperBound(14);
|
|
1714
|
+
expect(node2).toBeDefined();
|
|
1715
|
+
expect(node2?.key).toBe(15);
|
|
1716
|
+
});
|
|
1717
|
+
|
|
1718
|
+
it('should skip equal keys and return the next larger key', () => {
|
|
1719
|
+
// Test for key 5 (exists, should return 6, the smallest key > 5)
|
|
1720
|
+
const node = bst.upperBound(5);
|
|
1721
|
+
expect(node).toBeDefined();
|
|
1722
|
+
expect(node?.key).toBe(6);
|
|
1723
|
+
|
|
1724
|
+
// Test for key 20 (exists, should return 25)
|
|
1725
|
+
const node2 = bst.upperBound(20);
|
|
1726
|
+
expect(node2).toBeDefined();
|
|
1727
|
+
expect(node2?.key).toBe(25);
|
|
1728
|
+
});
|
|
1729
|
+
|
|
1730
|
+
it('should return smallest key > search key for non-existent key in middle range', () => {
|
|
1731
|
+
// Test for key 11.5 (doesn't exist, should return 12)
|
|
1732
|
+
const node = bst.upperBound(11.5);
|
|
1733
|
+
expect(node).toBeDefined();
|
|
1734
|
+
expect(node?.key).toBe(12);
|
|
1735
|
+
|
|
1736
|
+
// Test for key 19 (doesn't exist, should return 20)
|
|
1737
|
+
const node2 = bst.upperBound(19);
|
|
1738
|
+
expect(node2).toBeDefined();
|
|
1739
|
+
expect(node2?.key).toBe(20);
|
|
1740
|
+
});
|
|
1741
|
+
|
|
1742
|
+
it('should return smallest key when search key is less than minimum', () => {
|
|
1743
|
+
// Test for key 0 (should return 1, the minimum)
|
|
1744
|
+
const node = bst.upperBound(0);
|
|
1745
|
+
expect(node).toBeDefined();
|
|
1746
|
+
expect(node?.key).toBe(1);
|
|
1747
|
+
|
|
1748
|
+
// Test for key -100 (should return 1, the minimum)
|
|
1749
|
+
const node2 = bst.upperBound(-100);
|
|
1750
|
+
expect(node2).toBeDefined();
|
|
1751
|
+
expect(node2?.key).toBe(1);
|
|
1752
|
+
});
|
|
1753
|
+
|
|
1754
|
+
it('should return undefined when search key is >= maximum', () => {
|
|
1755
|
+
// Test for key 100 (no key > 100, should return undefined)
|
|
1756
|
+
const node = bst.upperBound(100);
|
|
1757
|
+
expect(node).toBeUndefined();
|
|
1758
|
+
|
|
1759
|
+
// Test for key 25 (maximum, no key > 25, should return undefined)
|
|
1760
|
+
const node2 = bst.upperBound(25);
|
|
1761
|
+
expect(node2).toBeUndefined();
|
|
1762
|
+
|
|
1763
|
+
// Test for key 26 (no key > 26, should return undefined)
|
|
1764
|
+
const node3 = bst.upperBound(26);
|
|
1765
|
+
expect(node3).toBeUndefined();
|
|
1766
|
+
});
|
|
1767
|
+
|
|
1768
|
+
it('should return correct node for edge cases', () => {
|
|
1769
|
+
// Test for key 1 (minimum, should return next key 3)
|
|
1770
|
+
const node = bst.upperBound(1);
|
|
1771
|
+
expect(node).toBeDefined();
|
|
1772
|
+
expect(node?.key).toBe(3);
|
|
1773
|
+
|
|
1774
|
+
// Test for key 24 (doesn't exist, should return 25)
|
|
1775
|
+
const node2 = bst.upperBound(24);
|
|
1776
|
+
expect(node2).toBeDefined();
|
|
1777
|
+
expect(node2?.key).toBe(25);
|
|
1778
|
+
});
|
|
1779
|
+
});
|
|
1780
|
+
|
|
1781
|
+
describe('lowerBound vs upperBound comparison', () => {
|
|
1782
|
+
it('should demonstrate difference between lowerBound and upperBound', () => {
|
|
1783
|
+
const searchKey = 12;
|
|
1784
|
+
|
|
1785
|
+
// lowerBound(12) should return 12 (key >= 12)
|
|
1786
|
+
const lower = bst.lowerBound(searchKey);
|
|
1787
|
+
expect(lower?.key).toBe(12);
|
|
1788
|
+
|
|
1789
|
+
// upperBound(12) should return 13 (key > 12)
|
|
1790
|
+
const upper = bst.upperBound(searchKey);
|
|
1791
|
+
expect(upper?.key).toBe(13);
|
|
1792
|
+
});
|
|
1793
|
+
|
|
1794
|
+
it('should return same result for non-existent key in both methods', () => {
|
|
1795
|
+
const searchKey = 11.5;
|
|
1796
|
+
|
|
1797
|
+
// Both should return 12 (smallest key >= 11.5 for lowerBound, smallest key > 11.5 for upperBound)
|
|
1798
|
+
const lower = bst.lowerBound(searchKey);
|
|
1799
|
+
const upper = bst.upperBound(searchKey);
|
|
1800
|
+
expect(lower?.key).toBe(12);
|
|
1801
|
+
expect(upper?.key).toBe(12);
|
|
1802
|
+
});
|
|
1803
|
+
});
|
|
1804
|
+
|
|
1805
|
+
describe('BST Bound Methods Comprehensive Tests', () => {
|
|
1806
|
+
let bst: BST<number, string>;
|
|
1807
|
+
|
|
1808
|
+
// Helper: Generate random number array
|
|
1809
|
+
const generateRandomArray = (size: number, min: number, max: number) => {
|
|
1810
|
+
return Array.from({ length: size }, () => Math.floor(Math.random() * (max - min + 1)) + min);
|
|
1811
|
+
};
|
|
1812
|
+
|
|
1813
|
+
beforeEach(() => {
|
|
1814
|
+
// Construct a standard test tree
|
|
1815
|
+
// Structure:
|
|
1816
|
+
// 10
|
|
1817
|
+
// / \
|
|
1818
|
+
// 5 15
|
|
1819
|
+
// / \ / \
|
|
1820
|
+
// 2 8 12 20
|
|
1821
|
+
// / \
|
|
1822
|
+
// 6 9
|
|
1823
|
+
bst = new BST<number, string>();
|
|
1824
|
+
const keys = [10, 5, 15, 2, 8, 12, 20, 6, 9];
|
|
1825
|
+
keys.forEach(k => bst.add(k, `val-${k}`));
|
|
1826
|
+
});
|
|
1827
|
+
|
|
1828
|
+
describe('lowerBound (First node >= key)', () => {
|
|
1829
|
+
test('should return strict match if key exists', () => {
|
|
1830
|
+
// Case: Key exists
|
|
1831
|
+
expect(bst.lowerBound(10)?.key).toBe(10);
|
|
1832
|
+
expect(bst.lowerBound(6)?.key).toBe(6);
|
|
1833
|
+
expect(bst.lowerBound(20)?.key).toBe(20);
|
|
1834
|
+
});
|
|
1835
|
+
|
|
1836
|
+
test('should return next larger node if key does not exist', () => {
|
|
1837
|
+
// Case: Key doesn't exist, but falls within range
|
|
1838
|
+
expect(bst.lowerBound(7)?.key).toBe(8); // 7 -> 8
|
|
1839
|
+
expect(bst.lowerBound(11)?.key).toBe(12); // 11 -> 12
|
|
1840
|
+
expect(bst.lowerBound(3)?.key).toBe(5); // 3 -> 5
|
|
1841
|
+
});
|
|
1842
|
+
|
|
1843
|
+
test('should return smallest node if key is smaller than min', () => {
|
|
1844
|
+
// Case: Key is smaller than the minimum value in the tree
|
|
1845
|
+
expect(bst.lowerBound(0)?.key).toBe(2);
|
|
1846
|
+
expect(bst.lowerBound(-100)?.key).toBe(2);
|
|
1847
|
+
});
|
|
1848
|
+
|
|
1849
|
+
test('should return undefined if key is larger than max', () => {
|
|
1850
|
+
// Case: Key is larger than the maximum value in the tree
|
|
1851
|
+
expect(bst.lowerBound(21)).toBeUndefined();
|
|
1852
|
+
expect(bst.lowerBound(100)).toBeUndefined();
|
|
1853
|
+
});
|
|
1854
|
+
|
|
1855
|
+
test('should work with IterationType.RECURSIVE explicitly', () => {
|
|
1856
|
+
expect(bst.lowerBound(7, 'RECURSIVE')?.key).toBe(8);
|
|
1857
|
+
expect(bst.lowerBound(10, 'RECURSIVE')?.key).toBe(10);
|
|
1858
|
+
expect(bst.lowerBound(21, 'RECURSIVE')).toBeUndefined();
|
|
1859
|
+
});
|
|
1860
|
+
|
|
1861
|
+
test('should work with IterationType.ITERATIVE explicitly', () => {
|
|
1862
|
+
expect(bst.lowerBound(7, 'ITERATIVE')?.key).toBe(8);
|
|
1863
|
+
expect(bst.lowerBound(10, 'ITERATIVE')?.key).toBe(10);
|
|
1864
|
+
expect(bst.lowerBound(21, 'ITERATIVE')).toBeUndefined();
|
|
1865
|
+
});
|
|
1866
|
+
});
|
|
1867
|
+
|
|
1868
|
+
describe('upperBound (First node > key)', () => {
|
|
1869
|
+
test('should return next larger node even if key exists', () => {
|
|
1870
|
+
// Case: Key exists, but we want strictly greater
|
|
1871
|
+
expect(bst.upperBound(10)?.key).toBe(12); // > 10 is 12
|
|
1872
|
+
expect(bst.upperBound(6)?.key).toBe(8); // > 6 is 8
|
|
1873
|
+
expect(bst.upperBound(20)).toBeUndefined(); // > 20 is undefined
|
|
1874
|
+
});
|
|
1875
|
+
|
|
1876
|
+
test('should return next larger node if key does not exist', () => {
|
|
1877
|
+
// Case: Key doesn't exist
|
|
1878
|
+
expect(bst.upperBound(7)?.key).toBe(8);
|
|
1879
|
+
expect(bst.upperBound(11)?.key).toBe(12);
|
|
1880
|
+
});
|
|
1881
|
+
|
|
1882
|
+
test('should return undefined if key is larger than or equal to max', () => {
|
|
1883
|
+
expect(bst.upperBound(20)).toBeUndefined();
|
|
1884
|
+
expect(bst.upperBound(21)).toBeUndefined();
|
|
1885
|
+
});
|
|
1886
|
+
|
|
1887
|
+
test('should work with IterationType.RECURSIVE explicitly', () => {
|
|
1888
|
+
expect(bst.upperBound(10, 'RECURSIVE')?.key).toBe(12);
|
|
1889
|
+
expect(bst.upperBound(20, 'RECURSIVE')).toBeUndefined();
|
|
1890
|
+
});
|
|
1891
|
+
});
|
|
1892
|
+
|
|
1893
|
+
describe('Edge Cases', () => {
|
|
1894
|
+
test('should handle empty tree', () => {
|
|
1895
|
+
const emptyTree = new BST<number, string>();
|
|
1896
|
+
expect(emptyTree.lowerBound(10)).toBeUndefined();
|
|
1897
|
+
expect(emptyTree.upperBound(10)).toBeUndefined();
|
|
1898
|
+
});
|
|
1899
|
+
|
|
1900
|
+
test('should handle single node tree', () => {
|
|
1901
|
+
const singleNodeTree = new BST<number, string>();
|
|
1902
|
+
singleNodeTree.add(10);
|
|
1903
|
+
|
|
1904
|
+
// lowerBound
|
|
1905
|
+
expect(singleNodeTree.lowerBound(5)?.key).toBe(10);
|
|
1906
|
+
expect(singleNodeTree.lowerBound(10)?.key).toBe(10);
|
|
1907
|
+
expect(singleNodeTree.lowerBound(15)).toBeUndefined();
|
|
1908
|
+
|
|
1909
|
+
// upperBound
|
|
1910
|
+
expect(singleNodeTree.upperBound(5)?.key).toBe(10);
|
|
1911
|
+
expect(singleNodeTree.upperBound(10)).toBeUndefined();
|
|
1912
|
+
});
|
|
1913
|
+
});
|
|
1914
|
+
|
|
1915
|
+
describe('Consistency & Fuzz Testing', () => {
|
|
1916
|
+
test('Recursive and Iterative implementations should match on random data', () => {
|
|
1917
|
+
const fuzzTree = new BST<number, number>();
|
|
1918
|
+
// Generate 500 random numbers between 0-1000
|
|
1919
|
+
const randomKeys = generateRandomArray(500, 0, 1000);
|
|
1920
|
+
|
|
1921
|
+
// Insert unique keys
|
|
1922
|
+
[...new Set(randomKeys)].forEach(k => fuzzTree.add(k));
|
|
1923
|
+
|
|
1924
|
+
// Sort for verification (Ground Truth)
|
|
1925
|
+
const sortedKeys = Array.from(fuzzTree.keys()).sort((a, b) => a - b);
|
|
1926
|
+
|
|
1927
|
+
// Test with 200 random queries (including existing and non-existing keys)
|
|
1928
|
+
const testQueries = generateRandomArray(200, 0, 1000);
|
|
1929
|
+
|
|
1930
|
+
testQueries.forEach(queryKey => {
|
|
1931
|
+
// 1. Verify lowerBound
|
|
1932
|
+
const recLower = fuzzTree.lowerBound(queryKey, 'RECURSIVE')?.key;
|
|
1933
|
+
const iterLower = fuzzTree.lowerBound(queryKey, 'ITERATIVE')?.key;
|
|
1934
|
+
|
|
1935
|
+
// Verify consistency between recursive and iterative
|
|
1936
|
+
expect(recLower).toBe(iterLower);
|
|
1937
|
+
|
|
1938
|
+
// Verify logic correctness (compare with Array.find)
|
|
1939
|
+
const expectedLower = sortedKeys.find(k => k >= queryKey);
|
|
1940
|
+
expect(recLower).toBe(expectedLower);
|
|
1941
|
+
|
|
1942
|
+
// 2. Verify upperBound
|
|
1943
|
+
const recUpper = fuzzTree.upperBound(queryKey, 'RECURSIVE')?.key;
|
|
1944
|
+
const iterUpper = fuzzTree.upperBound(queryKey, 'ITERATIVE')?.key;
|
|
1945
|
+
|
|
1946
|
+
// Verify consistency
|
|
1947
|
+
expect(recUpper).toBe(iterUpper);
|
|
1948
|
+
|
|
1949
|
+
// Verify logic correctness
|
|
1950
|
+
const expectedUpper = sortedKeys.find(k => k > queryKey);
|
|
1951
|
+
expect(recUpper).toBe(expectedUpper);
|
|
1952
|
+
});
|
|
1953
|
+
});
|
|
1954
|
+
});
|
|
1955
|
+
});
|
|
1956
|
+
|
|
1957
|
+
describe('empty tree', () => {
|
|
1958
|
+
beforeEach(() => {
|
|
1959
|
+
bst = new BST<number, string>();
|
|
1960
|
+
});
|
|
1961
|
+
|
|
1962
|
+
it('lowerBound should return undefined on empty tree', () => {
|
|
1963
|
+
const node = bst.lowerBound(10);
|
|
1964
|
+
expect(node).toBeUndefined();
|
|
1965
|
+
});
|
|
1966
|
+
|
|
1967
|
+
it('upperBound should return undefined on empty tree', () => {
|
|
1968
|
+
const node = bst.upperBound(10);
|
|
1969
|
+
expect(node).toBeUndefined();
|
|
1970
|
+
});
|
|
1971
|
+
});
|
|
1972
|
+
|
|
1973
|
+
describe('single node tree', () => {
|
|
1974
|
+
beforeEach(() => {
|
|
1975
|
+
bst = new BST<number, string>();
|
|
1976
|
+
bst.add(10, 'ten');
|
|
1977
|
+
});
|
|
1978
|
+
|
|
1979
|
+
it('lowerBound should return the node if key matches', () => {
|
|
1980
|
+
const node = bst.lowerBound(10);
|
|
1981
|
+
expect(node).toBeDefined();
|
|
1982
|
+
expect(node?.key).toBe(10);
|
|
1983
|
+
});
|
|
1984
|
+
|
|
1985
|
+
it('lowerBound should return the node if search key is less', () => {
|
|
1986
|
+
const node = bst.lowerBound(5);
|
|
1987
|
+
expect(node).toBeDefined();
|
|
1988
|
+
expect(node?.key).toBe(10);
|
|
1989
|
+
});
|
|
1990
|
+
|
|
1991
|
+
it('lowerBound should return undefined if search key is greater', () => {
|
|
1992
|
+
const node = bst.lowerBound(15);
|
|
1993
|
+
expect(node).toBeUndefined();
|
|
1994
|
+
});
|
|
1995
|
+
|
|
1996
|
+
it('upperBound should return undefined if key matches', () => {
|
|
1997
|
+
const node = bst.upperBound(10);
|
|
1998
|
+
expect(node).toBeUndefined();
|
|
1999
|
+
});
|
|
2000
|
+
|
|
2001
|
+
it('upperBound should return the node if search key is less', () => {
|
|
2002
|
+
const node = bst.upperBound(5);
|
|
2003
|
+
expect(node).toBeDefined();
|
|
2004
|
+
expect(node?.key).toBe(10);
|
|
2005
|
+
});
|
|
2006
|
+
|
|
2007
|
+
it('upperBound should return undefined if search key is greater', () => {
|
|
2008
|
+
const node = bst.upperBound(15);
|
|
2009
|
+
expect(node).toBeUndefined();
|
|
2010
|
+
});
|
|
2011
|
+
});
|
|
2012
|
+
|
|
2013
|
+
describe('practical LeetCode use case: Range search', () => {
|
|
2014
|
+
it('should find all keys in range [12, 20]', () => {
|
|
2015
|
+
// Use lowerBound to find start of range and upperBound to find end
|
|
2016
|
+
const start = bst.lowerBound(12);
|
|
2017
|
+
|
|
2018
|
+
// Simple traversal simulation (for test only, real iteration is more complex)
|
|
2019
|
+
// We manually add keys we know are in range to verify logic works
|
|
2020
|
+
// The lowerBound(12) returns 12. upperBound(20) returns 25 (or null if max).
|
|
2021
|
+
// Logic: we want >= 12 and <= 20.
|
|
2022
|
+
|
|
2023
|
+
expect(start?.key).toBe(12);
|
|
2024
|
+
|
|
2025
|
+
// Let's verify we found the correct bounds
|
|
2026
|
+
expect(start?.key).toBeGreaterThanOrEqual(12);
|
|
2027
|
+
|
|
2028
|
+
// Check overlap
|
|
2029
|
+
const rangeStart = bst.lowerBound(12);
|
|
2030
|
+
const rangeEndInclusive = bst.lowerBound(20); // Should be 20
|
|
2031
|
+
|
|
2032
|
+
expect(rangeStart?.key).toBe(12);
|
|
2033
|
+
expect(rangeEndInclusive?.key).toBe(20);
|
|
2034
|
+
});
|
|
2035
|
+
|
|
2036
|
+
it('should help identify interval overlaps', () => {
|
|
2037
|
+
// Check if interval [14, 19] overlaps with any existing interval
|
|
2038
|
+
// Use upperBound to find the first key > 19
|
|
2039
|
+
const nextKey = bst.upperBound(19);
|
|
2040
|
+
// Use lowerBound to find the first key >= 14
|
|
2041
|
+
const firstInRange = bst.lowerBound(14);
|
|
2042
|
+
|
|
2043
|
+
expect(firstInRange?.key).toBe(15);
|
|
2044
|
+
expect(nextKey?.key).toBe(20);
|
|
2045
|
+
});
|
|
2046
|
+
});
|
|
2047
|
+
});
|
|
2048
|
+
|
|
2049
|
+
describe('BST Advanced Bound Methods Tests', () => {
|
|
2050
|
+
let bst: BST<number, string>;
|
|
2051
|
+
|
|
2052
|
+
// Helper: Generate random number array
|
|
2053
|
+
const generateRandomArray = (size: number, min: number, max: number) => {
|
|
2054
|
+
return Array.from({ length: size }, () => Math.floor(Math.random() * (max - min + 1)) + min);
|
|
2055
|
+
};
|
|
2056
|
+
|
|
2057
|
+
beforeEach(() => {
|
|
2058
|
+
// Construct a standard test tree
|
|
2059
|
+
// Structure:
|
|
2060
|
+
// 10
|
|
2061
|
+
// / \
|
|
2062
|
+
// 5 15
|
|
2063
|
+
// / \ / \
|
|
2064
|
+
// 2 8 12 20
|
|
2065
|
+
// / \
|
|
2066
|
+
// 6 9
|
|
2067
|
+
bst = new BST<number, string>();
|
|
2068
|
+
const keys = [10, 5, 15, 2, 8, 12, 20, 6, 9];
|
|
2069
|
+
keys.forEach(k => bst.add(k, `val-${k}`));
|
|
2070
|
+
});
|
|
2071
|
+
|
|
2072
|
+
describe('Polymorphic Input Support', () => {
|
|
2073
|
+
test('should accept raw Key', () => {
|
|
2074
|
+
expect(bst.lowerBound(7)?.key).toBe(8);
|
|
2075
|
+
expect(bst.upperBound(7)?.key).toBe(8);
|
|
2076
|
+
});
|
|
2077
|
+
|
|
2078
|
+
test('should accept BSTNode object', () => {
|
|
2079
|
+
// Find a node first
|
|
2080
|
+
const node5 = bst.getNode(5);
|
|
2081
|
+
expect(node5).toBeDefined();
|
|
2082
|
+
|
|
2083
|
+
// Use the node as input for lowerBound (should return itself)
|
|
2084
|
+
expect(bst.lowerBound(node5)?.key).toBe(5);
|
|
2085
|
+
|
|
2086
|
+
// Use the node as input for upperBound (should return next larger)
|
|
2087
|
+
expect(bst.upperBound(node5)?.key).toBe(6);
|
|
2088
|
+
});
|
|
2089
|
+
|
|
2090
|
+
test('should accept Entry tuple [key, value]', () => {
|
|
2091
|
+
// Input as [key, value] tuple
|
|
2092
|
+
const entry: [number, string] = [7, 'val-7'];
|
|
2093
|
+
|
|
2094
|
+
expect(bst.lowerBound(entry)?.key).toBe(8);
|
|
2095
|
+
expect(bst.upperBound(entry)?.key).toBe(8);
|
|
2096
|
+
|
|
2097
|
+
const existingEntry: [number, string] = [10, 'val-10'];
|
|
2098
|
+
expect(bst.lowerBound(existingEntry)?.key).toBe(10);
|
|
2099
|
+
expect(bst.upperBound(existingEntry)?.key).toBe(12);
|
|
2100
|
+
});
|
|
2101
|
+
|
|
2102
|
+
test('should accept Predicate function (Linear Search Fallback)', () => {
|
|
2103
|
+
// Predicate: Find first node with key > 11 (Expect 12)
|
|
2104
|
+
// Note: Predicate search uses in-order traversal
|
|
2105
|
+
const predicate = (node: any) => node.key > 11;
|
|
2106
|
+
|
|
2107
|
+
// For predicate, lowerBound and upperBound behave identically
|
|
2108
|
+
// (they just return the first match of the predicate)
|
|
2109
|
+
expect(bst.lowerBound(predicate)?.key).toBe(12);
|
|
2110
|
+
expect(bst.upperBound(predicate)?.key).toBe(12);
|
|
2111
|
+
|
|
2112
|
+
// Predicate: Find specific value
|
|
2113
|
+
expect(bst.lowerBound(n => n.key === 6)?.key).toBe(6);
|
|
2114
|
+
});
|
|
2115
|
+
});
|
|
2116
|
+
|
|
2117
|
+
describe('Iteration Types with Polymorphic Inputs', () => {
|
|
2118
|
+
test('should work recursively with all types', () => {
|
|
2119
|
+
const type = 'RECURSIVE';
|
|
2120
|
+
|
|
2121
|
+
expect(bst.lowerBound(7, type)?.key).toBe(8);
|
|
2122
|
+
expect(bst.lowerBound(bst.getNode(5), type)?.key).toBe(5);
|
|
2123
|
+
expect(bst.lowerBound([7, 'val'], type)?.key).toBe(8);
|
|
2124
|
+
expect(bst.lowerBound(n => n.key > 11, type)?.key).toBe(12);
|
|
2125
|
+
});
|
|
2126
|
+
|
|
2127
|
+
test('should work iteratively with all types', () => {
|
|
2128
|
+
const type = 'ITERATIVE';
|
|
2129
|
+
|
|
2130
|
+
expect(bst.lowerBound(7, type)?.key).toBe(8);
|
|
2131
|
+
expect(bst.lowerBound(bst.getNode(5), type)?.key).toBe(5);
|
|
2132
|
+
expect(bst.lowerBound([7, 'val'], type)?.key).toBe(8);
|
|
2133
|
+
expect(bst.lowerBound(n => n.key > 11, type)?.key).toBe(12);
|
|
2134
|
+
});
|
|
2135
|
+
});
|
|
2136
|
+
|
|
2137
|
+
describe('Complex Edge Cases', () => {
|
|
2138
|
+
test('should return undefined for null/undefined input', () => {
|
|
2139
|
+
expect(bst.lowerBound(null)).toBeUndefined();
|
|
2140
|
+
expect(bst.lowerBound(undefined)).toBeUndefined();
|
|
2141
|
+
});
|
|
2142
|
+
|
|
2143
|
+
test('should handle predicate returning false for all nodes', () => {
|
|
2144
|
+
expect(bst.lowerBound(n => n.key > 100)).toBeUndefined();
|
|
2145
|
+
});
|
|
2146
|
+
});
|
|
2147
|
+
|
|
2148
|
+
describe('Consistency Check (Fuzz Testing)', () => {
|
|
2149
|
+
test('All input methods should yield consistent results for Raw Key and Entry', () => {
|
|
2150
|
+
const fuzzTree = new BST<number, number>();
|
|
2151
|
+
const randomKeys = generateRandomArray(100, 0, 200);
|
|
2152
|
+
[...new Set(randomKeys)].forEach(k => fuzzTree.add(k));
|
|
2153
|
+
|
|
2154
|
+
const queries = generateRandomArray(50, 0, 200);
|
|
2155
|
+
|
|
2156
|
+
queries.forEach(q => {
|
|
2157
|
+
// 1. Raw Key
|
|
2158
|
+
const resKey = fuzzTree.lowerBound(q);
|
|
2159
|
+
|
|
2160
|
+
// 2. Entry
|
|
2161
|
+
const resEntry = fuzzTree.lowerBound([q, undefined]);
|
|
2162
|
+
|
|
2163
|
+
// Verify consistency between Key and Entry
|
|
2164
|
+
expect(resKey?.key).toBe(resEntry?.key);
|
|
2165
|
+
});
|
|
2166
|
+
});
|
|
2167
|
+
|
|
2168
|
+
test('BSTNode input should work correctly', () => {
|
|
2169
|
+
const fuzzTree = new BST<number, number>();
|
|
2170
|
+
const randomKeys = generateRandomArray(50, 0, 100);
|
|
2171
|
+
const uniqueKeys = [...new Set(randomKeys)];
|
|
2172
|
+
uniqueKeys.forEach(k => fuzzTree.add(k));
|
|
2173
|
+
|
|
2174
|
+
// Test with actual nodes from the tree
|
|
2175
|
+
for (const key of uniqueKeys) {
|
|
2176
|
+
const node = fuzzTree.getNode(key);
|
|
2177
|
+
if (node) {
|
|
2178
|
+
// lowerBound with node should return itself (since node.key >= key)
|
|
2179
|
+
expect(fuzzTree.lowerBound(node)?.key).toBe(key);
|
|
2180
|
+
|
|
2181
|
+
// upperBound with node should return next larger
|
|
2182
|
+
const upperRes = fuzzTree.upperBound(node);
|
|
2183
|
+
if (upperRes) {
|
|
2184
|
+
expect(upperRes.key).toBeGreaterThan(key);
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
});
|
|
2189
|
+
|
|
2190
|
+
test('Predicate input should find matching nodes', () => {
|
|
2191
|
+
const fuzzTree = new BST<number, number>();
|
|
2192
|
+
const randomKeys = generateRandomArray(50, 0, 100);
|
|
2193
|
+
const uniqueKeys = [...new Set(randomKeys)];
|
|
2194
|
+
uniqueKeys.forEach(k => fuzzTree.add(k));
|
|
2195
|
+
|
|
2196
|
+
// Test various predicates
|
|
2197
|
+
const predicates = [(n: any) => n.key > 50, (n: any) => n.key < 30, (n: any) => n.key >= 25 && n.key <= 75];
|
|
2198
|
+
|
|
2199
|
+
predicates.forEach(predicate => {
|
|
2200
|
+
const result = fuzzTree.lowerBound(predicate);
|
|
2201
|
+
if (result) {
|
|
2202
|
+
// Verify the result actually satisfies the predicate
|
|
2203
|
+
expect(predicate(result)).toBe(true);
|
|
2204
|
+
}
|
|
2205
|
+
});
|
|
2206
|
+
});
|
|
2207
|
+
|
|
2208
|
+
test('Consistency between RECURSIVE and ITERATIVE modes', () => {
|
|
2209
|
+
const fuzzTree = new BST<number, number>();
|
|
2210
|
+
const randomKeys = generateRandomArray(50, 0, 100);
|
|
2211
|
+
[...new Set(randomKeys)].forEach(k => fuzzTree.add(k));
|
|
2212
|
+
|
|
2213
|
+
const queries = generateRandomArray(30, 0, 100);
|
|
2214
|
+
|
|
2215
|
+
queries.forEach(q => {
|
|
2216
|
+
// Test with Key
|
|
2217
|
+
const recKey = fuzzTree.lowerBound(q, 'RECURSIVE');
|
|
2218
|
+
const iterKey = fuzzTree.lowerBound(q, 'ITERATIVE');
|
|
2219
|
+
expect(recKey?.key).toBe(iterKey?.key);
|
|
2220
|
+
|
|
2221
|
+
// Test with Entry
|
|
2222
|
+
const recEntry = fuzzTree.lowerBound([q, undefined], 'RECURSIVE');
|
|
2223
|
+
const iterEntry = fuzzTree.lowerBound([q, undefined], 'ITERATIVE');
|
|
2224
|
+
expect(recEntry?.key).toBe(iterEntry?.key);
|
|
2225
|
+
|
|
2226
|
+
// Test upperBound too
|
|
2227
|
+
const recUpper = fuzzTree.upperBound(q, 'RECURSIVE');
|
|
2228
|
+
const iterUpper = fuzzTree.upperBound(q, 'ITERATIVE');
|
|
2229
|
+
expect(recUpper?.key).toBe(iterUpper?.key);
|
|
2230
|
+
});
|
|
2231
|
+
});
|
|
2232
|
+
});
|
|
2233
|
+
});
|
|
2234
|
+
|
|
1619
2235
|
describe('classic use', () => {
|
|
2236
|
+
it('@example basic BST creation and add operation', () => {
|
|
2237
|
+
// Create a simple BST with numeric keys
|
|
2238
|
+
const bst = new BST<number>([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
2239
|
+
|
|
2240
|
+
bst.print();
|
|
2241
|
+
// _______8__________
|
|
2242
|
+
// / \
|
|
2243
|
+
// ___4___ ____12_____
|
|
2244
|
+
// / \ / \
|
|
2245
|
+
// _2_ _6_ _10__ _14__
|
|
2246
|
+
// / \ / \ / \ / \
|
|
2247
|
+
// 1 3 5 7 9 11 13 15__
|
|
2248
|
+
// \
|
|
2249
|
+
// 16
|
|
2250
|
+
|
|
2251
|
+
// Verify size
|
|
2252
|
+
expect(bst.size).toBe(16);
|
|
2253
|
+
|
|
2254
|
+
// Add new elements
|
|
2255
|
+
bst.add(17);
|
|
2256
|
+
bst.add(0);
|
|
2257
|
+
expect(bst.size).toBe(18);
|
|
2258
|
+
|
|
2259
|
+
// Verify keys are searchable
|
|
2260
|
+
expect(bst.has(11)).toBe(true);
|
|
2261
|
+
expect(bst.has(100)).toBe(false);
|
|
2262
|
+
});
|
|
2263
|
+
|
|
2264
|
+
it('@example BST delete and search after deletion', () => {
|
|
2265
|
+
const bst = new BST<number>([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
2266
|
+
|
|
2267
|
+
// Delete a leaf node
|
|
2268
|
+
bst.delete(1);
|
|
2269
|
+
expect(bst.has(1)).toBe(false);
|
|
2270
|
+
|
|
2271
|
+
// Delete a node with one child
|
|
2272
|
+
bst.delete(2);
|
|
2273
|
+
expect(bst.has(2)).toBe(false);
|
|
2274
|
+
|
|
2275
|
+
// Delete a node with two children
|
|
2276
|
+
bst.delete(3);
|
|
2277
|
+
expect(bst.has(3)).toBe(false);
|
|
2278
|
+
|
|
2279
|
+
// Size decreases with each deletion
|
|
2280
|
+
expect(bst.size).toBe(13);
|
|
2281
|
+
|
|
2282
|
+
// Other nodes remain searchable
|
|
2283
|
+
expect(bst.has(11)).toBe(true);
|
|
2284
|
+
expect(bst.has(15)).toBe(true);
|
|
2285
|
+
});
|
|
2286
|
+
|
|
1620
2287
|
it('@example Merge 3 sorted datasets', () => {
|
|
1621
2288
|
const dataset1 = new BST<number, string>([
|
|
1622
2289
|
[1, 'A'],
|
|
@@ -1641,14 +2308,53 @@ describe('classic use', () => {
|
|
|
1641
2308
|
expect([...merged.values()]).toEqual(['A', 'B', 'C', 'D', 'E', 'F', 'G']);
|
|
1642
2309
|
});
|
|
1643
2310
|
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
2311
|
+
it('@example BST with custom objects for expression evaluation', () => {
|
|
2312
|
+
interface Expression {
|
|
2313
|
+
id: number;
|
|
2314
|
+
operator: string;
|
|
2315
|
+
precedence: number;
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
// BST efficiently stores and retrieves operators by precedence
|
|
2319
|
+
const operatorTree = new BST<number, Expression>(
|
|
2320
|
+
[
|
|
2321
|
+
[1, { id: 1, operator: '+', precedence: 1 }],
|
|
2322
|
+
[2, { id: 2, operator: '*', precedence: 2 }],
|
|
2323
|
+
[3, { id: 3, operator: '/', precedence: 2 }],
|
|
2324
|
+
[4, { id: 4, operator: '-', precedence: 1 }],
|
|
2325
|
+
[5, { id: 5, operator: '^', precedence: 3 }]
|
|
2326
|
+
],
|
|
2327
|
+
{ isMapMode: false }
|
|
2328
|
+
);
|
|
2329
|
+
|
|
2330
|
+
expect(operatorTree.size).toBe(5);
|
|
2331
|
+
|
|
2332
|
+
// Quick lookup of operators
|
|
2333
|
+
const mult = operatorTree.get(2);
|
|
2334
|
+
expect(mult?.operator).toBe('*');
|
|
2335
|
+
expect(mult?.precedence).toBe(2);
|
|
2336
|
+
|
|
2337
|
+
// Check if operator exists
|
|
2338
|
+
expect(operatorTree.has(5)).toBe(true);
|
|
2339
|
+
expect(operatorTree.has(99)).toBe(false);
|
|
2340
|
+
|
|
2341
|
+
// Retrieve operator by precedence level
|
|
2342
|
+
const expNode = operatorTree.getNode(3);
|
|
2343
|
+
expect(expNode?.key).toBe(3);
|
|
2344
|
+
expect(expNode?.value?.precedence).toBe(2);
|
|
2345
|
+
|
|
2346
|
+
// Delete operator and verify
|
|
2347
|
+
operatorTree.delete(1);
|
|
2348
|
+
expect(operatorTree.has(1)).toBe(false);
|
|
2349
|
+
expect(operatorTree.size).toBe(4);
|
|
2350
|
+
|
|
2351
|
+
// Get tree height for optimization analysis
|
|
2352
|
+
const treeHeight = operatorTree.getHeight();
|
|
2353
|
+
expect(treeHeight).toBeGreaterThan(0);
|
|
2354
|
+
|
|
2355
|
+
// Remaining operators are still accessible
|
|
2356
|
+
const remaining = operatorTree.get(2);
|
|
2357
|
+
expect(remaining).toBeDefined();
|
|
1652
2358
|
});
|
|
1653
2359
|
|
|
1654
2360
|
// Test case for Lowest Common Ancestor (LCA)
|
|
@@ -1677,4 +2383,53 @@ describe('classic use', () => {
|
|
|
1677
2383
|
expect(findLCA(5, 35)).toBe(15);
|
|
1678
2384
|
expect(findLCA(20, 30)).toBe(25);
|
|
1679
2385
|
});
|
|
2386
|
+
|
|
2387
|
+
// Test case for finding elements in a given range
|
|
2388
|
+
it('Find elements in a range', () => {
|
|
2389
|
+
const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18]);
|
|
2390
|
+
expect(bst.search(new Range(5, 10))).toEqual([5, 7, 10]);
|
|
2391
|
+
expect(bst.rangeSearch([4, 12], node => node.key.toString())).toEqual(['5', '7', '10', '12']);
|
|
2392
|
+
expect(bst.search(new Range(4, 12, true, false))).toEqual([5, 7, 10]);
|
|
2393
|
+
expect(bst.rangeSearch([15, 20])).toEqual([15, 18]);
|
|
2394
|
+
expect(bst.search(new Range(15, 20, false))).toEqual([18]);
|
|
2395
|
+
});
|
|
2396
|
+
|
|
2397
|
+
it('BST get and getNode operations', () => {
|
|
2398
|
+
const bst = new BST<number>([5, 3, 7, 1, 4, 6, 8]);
|
|
2399
|
+
|
|
2400
|
+
// Get value by key
|
|
2401
|
+
const value = bst.get(5);
|
|
2402
|
+
expect(value).toBe(undefined);
|
|
2403
|
+
|
|
2404
|
+
// Get node returns the actual node object
|
|
2405
|
+
const node = bst.getNode(3);
|
|
2406
|
+
expect(node?.key).toBe(3);
|
|
2407
|
+
expect(node?.left).toBeDefined();
|
|
2408
|
+
expect(node?.right).toBeDefined();
|
|
2409
|
+
|
|
2410
|
+
// Get from non-existent key returns undefined
|
|
2411
|
+
expect(bst.get(100)).toBeUndefined();
|
|
2412
|
+
expect(bst.getNode(100)).toBeUndefined();
|
|
2413
|
+
});
|
|
2414
|
+
|
|
2415
|
+
it('BST getHeight and tree structure queries', () => {
|
|
2416
|
+
const bst = new BST<number>([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
2417
|
+
|
|
2418
|
+
// Get overall tree height
|
|
2419
|
+
const treeHeight = bst.getHeight();
|
|
2420
|
+
expect(typeof treeHeight).toBe('number');
|
|
2421
|
+
expect(treeHeight).toBeGreaterThan(0);
|
|
2422
|
+
|
|
2423
|
+
// Get height of specific node
|
|
2424
|
+
const heightOf3 = bst.getHeight(3);
|
|
2425
|
+
expect(typeof heightOf3).toBe('number');
|
|
2426
|
+
|
|
2427
|
+
// Root node should have the maximum height
|
|
2428
|
+
const heightOf11 = bst.getHeight(11);
|
|
2429
|
+
expect(heightOf11).toBe(0);
|
|
2430
|
+
|
|
2431
|
+
// Leaf nodes have height 0
|
|
2432
|
+
const heightOf2 = bst.getHeight(2);
|
|
2433
|
+
expect(heightOf2).toBe(1);
|
|
2434
|
+
});
|
|
1680
2435
|
});
|