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.
Files changed (94) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/README.md +355 -1672
  3. package/README_CN.md +509 -0
  4. package/SECURITY.md +962 -11
  5. package/SECURITY.zh-CN.md +966 -0
  6. package/SPECIFICATION.md +689 -30
  7. package/SPECIFICATION.zh-CN.md +715 -0
  8. package/SPONSOR.zh-CN.md +62 -0
  9. package/SPONSOR_POLISHED.md +62 -0
  10. package/benchmark/report.html +1 -1
  11. package/benchmark/report.json +215 -172
  12. package/dist/cjs/index.cjs +245 -72
  13. package/dist/cjs/index.cjs.map +1 -1
  14. package/dist/cjs-legacy/index.cjs +246 -72
  15. package/dist/cjs-legacy/index.cjs.map +1 -1
  16. package/dist/esm/index.mjs +245 -72
  17. package/dist/esm/index.mjs.map +1 -1
  18. package/dist/esm-legacy/index.mjs +246 -72
  19. package/dist/esm-legacy/index.mjs.map +1 -1
  20. package/dist/types/data-structures/binary-tree/avl-tree-counter.d.ts +2 -2
  21. package/dist/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +5 -5
  22. package/dist/types/data-structures/binary-tree/avl-tree.d.ts +98 -5
  23. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +103 -7
  24. package/dist/types/data-structures/binary-tree/bst.d.ts +202 -39
  25. package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +86 -37
  26. package/dist/types/data-structures/binary-tree/tree-counter.d.ts +4 -5
  27. package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +7 -7
  28. package/dist/types/data-structures/graph/directed-graph.d.ts +126 -1
  29. package/dist/types/data-structures/graph/undirected-graph.d.ts +160 -1
  30. package/dist/types/data-structures/hash/hash-map.d.ts +110 -27
  31. package/dist/types/data-structures/heap/heap.d.ts +107 -58
  32. package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +72 -404
  33. package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +121 -5
  34. package/dist/types/data-structures/queue/deque.d.ts +95 -67
  35. package/dist/types/data-structures/queue/queue.d.ts +90 -34
  36. package/dist/types/data-structures/stack/stack.d.ts +58 -40
  37. package/dist/types/data-structures/trie/trie.d.ts +109 -47
  38. package/dist/types/interfaces/binary-tree.d.ts +1 -0
  39. package/dist/types/types/data-structures/binary-tree/bst.d.ts +5 -5
  40. package/dist/umd/data-structure-typed.js +246 -72
  41. package/dist/umd/data-structure-typed.js.map +1 -1
  42. package/dist/umd/data-structure-typed.min.js +3 -3
  43. package/dist/umd/data-structure-typed.min.js.map +1 -1
  44. package/package.json +3 -2
  45. package/src/data-structures/binary-tree/avl-tree-counter.ts +1 -2
  46. package/src/data-structures/binary-tree/avl-tree-multi-map.ts +7 -8
  47. package/src/data-structures/binary-tree/avl-tree.ts +100 -7
  48. package/src/data-structures/binary-tree/binary-tree.ts +117 -7
  49. package/src/data-structures/binary-tree/bst.ts +431 -93
  50. package/src/data-structures/binary-tree/red-black-tree.ts +85 -37
  51. package/src/data-structures/binary-tree/tree-counter.ts +5 -7
  52. package/src/data-structures/binary-tree/tree-multi-map.ts +9 -10
  53. package/src/data-structures/graph/directed-graph.ts +126 -1
  54. package/src/data-structures/graph/undirected-graph.ts +160 -1
  55. package/src/data-structures/hash/hash-map.ts +110 -27
  56. package/src/data-structures/heap/heap.ts +107 -58
  57. package/src/data-structures/linked-list/doubly-linked-list.ts +72 -404
  58. package/src/data-structures/linked-list/singly-linked-list.ts +121 -5
  59. package/src/data-structures/queue/deque.ts +95 -67
  60. package/src/data-structures/queue/queue.ts +90 -34
  61. package/src/data-structures/stack/stack.ts +58 -40
  62. package/src/data-structures/trie/trie.ts +109 -47
  63. package/src/interfaces/binary-tree.ts +2 -0
  64. package/src/types/data-structures/binary-tree/bst.ts +5 -5
  65. package/test/performance/benchmark-runner.ts +14 -11
  66. package/test/performance/data-structures/binary-tree/avl-tree.test.ts +8 -8
  67. package/test/performance/data-structures/binary-tree/binary-tree-overall.test.ts +8 -8
  68. package/test/performance/data-structures/binary-tree/binary-tree.test.ts +6 -6
  69. package/test/performance/data-structures/binary-tree/bst.test.ts +5 -5
  70. package/test/performance/data-structures/binary-tree/red-black-tree.test.ts +10 -10
  71. package/test/performance/reportor.ts +2 -1
  72. package/test/performance/single-suite-runner.ts +7 -4
  73. package/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts +2 -2
  74. package/test/unit/data-structures/binary-tree/avl-tree.test.ts +117 -0
  75. package/test/unit/data-structures/binary-tree/binary-tree.test.ts +166 -0
  76. package/test/unit/data-structures/binary-tree/bst.test.ts +771 -16
  77. package/test/unit/data-structures/binary-tree/overall.test.ts +2 -2
  78. package/test/unit/data-structures/binary-tree/red-black-tree.test.ts +90 -38
  79. package/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +2 -2
  80. package/test/unit/data-structures/graph/directed-graph.test.ts +133 -0
  81. package/test/unit/data-structures/graph/undirected-graph.test.ts +167 -0
  82. package/test/unit/data-structures/hash/hash-map.test.ts +149 -3
  83. package/test/unit/data-structures/heap/heap.test.ts +182 -47
  84. package/test/unit/data-structures/linked-list/doubly-linked-list.test.ts +118 -14
  85. package/test/unit/data-structures/linked-list/singly-linked-list.test.ts +121 -0
  86. package/test/unit/data-structures/queue/deque.test.ts +98 -67
  87. package/test/unit/data-structures/queue/queue.test.ts +85 -51
  88. package/test/unit/data-structures/stack/stack.test.ts +142 -33
  89. package/test/unit/data-structures/trie/trie.test.ts +135 -39
  90. package/tsup.leetcode.config.js +99 -0
  91. package/typedoc.json +2 -1
  92. package/POSTS_zh-CN.md +0 -54
  93. package/README_zh-CN.md +0 -1208
  94. 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>([], { specifyComparable: key => key });
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
- isReverse: true
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 specifyComparable and isReverse', () => {
1540
+ it('should support comparator', () => {
1541
1541
  const bst = new BST<number>([], {
1542
- specifyComparable: k => -k,
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 specifyComparable', () => {
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
- // Test case for finding elements in a given range
1645
- it('@example Find elements in a range', () => {
1646
- const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18]);
1647
- expect(bst.search(new Range(5, 10))).toEqual([5, 7, 10]);
1648
- expect(bst.rangeSearch([4, 12], node => node.key.toString())).toEqual(['5', '7', '10', '12']);
1649
- expect(bst.search(new Range(4, 12, true, false))).toEqual([5, 7, 10]);
1650
- expect(bst.rangeSearch([15, 20])).toEqual([15, 18]);
1651
- expect(bst.search(new Range(15, 20, false))).toEqual([18]);
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
  });