data-structure-typed 2.2.4 → 2.2.6
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 +1 -1
- package/dist/cjs/index.cjs +327 -57
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +329 -57
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +327 -57
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +329 -57
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/data-structures/base/iterable-entry-base.d.ts +6 -0
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +14 -57
- package/dist/types/data-structures/binary-tree/bst.d.ts +110 -96
- package/dist/umd/data-structure-typed.js +329 -57
- 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 +5 -3
- package/src/common/index.ts +2 -4
- package/src/data-structures/base/iterable-entry-base.ts +9 -0
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +3 -1
- package/src/data-structures/binary-tree/binary-tree.ts +67 -0
- package/src/data-structures/binary-tree/bst.ts +658 -71
- package/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts +1 -1
- package/test/unit/data-structures/binary-tree/bst.test.ts +1385 -318
- package/test/unit/data-structures/binary-tree/overall.test.ts +2 -2
- package/test/unit/data-structures/binary-tree/red-black-tree.test.ts +1 -1
- package/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +13 -11
- package/tsup.config.js +6 -0
- package/tsup.leetcode.config.js +3 -0
- package/tsup.node.config.js +12 -0
|
@@ -492,7 +492,6 @@ describe('BST operations test', () => {
|
|
|
492
492
|
it('should search in range', () => {
|
|
493
493
|
const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18]);
|
|
494
494
|
expect(bst.rangeSearch([4, 12])).toEqual([5, 7, 10, 12]);
|
|
495
|
-
expect(() => bst.rangeSearch([12, 4])).toThrow('low must be less than or equal to high');
|
|
496
495
|
expect(bst.rangeSearch([12, 12])).toEqual([12]);
|
|
497
496
|
});
|
|
498
497
|
});
|
|
@@ -1015,7 +1014,7 @@ describe('BST isBST', function () {
|
|
|
1015
1014
|
|
|
1016
1015
|
it('isBST when variant is Max', () => {
|
|
1017
1016
|
const bst = new BST<number, number>([1, 2, 3, 9, 8, 5, 6, 7, 4], {
|
|
1018
|
-
comparator: (a, b) => b - a
|
|
1017
|
+
comparator: (a, b) => b - a
|
|
1019
1018
|
});
|
|
1020
1019
|
bst.addMany([1, 2, 3, 9, 8, 5, 6, 7, 4]);
|
|
1021
1020
|
expect(bst.isBST()).toBe(true);
|
|
@@ -1539,7 +1538,7 @@ describe('BST iterative methods not map mode test', () => {
|
|
|
1539
1538
|
describe('BST constructor and comparator edge cases', () => {
|
|
1540
1539
|
it('should support comparator', () => {
|
|
1541
1540
|
const bst = new BST<number>([], {
|
|
1542
|
-
comparator: (a, b) => b - a
|
|
1541
|
+
comparator: (a, b) => b - a
|
|
1543
1542
|
});
|
|
1544
1543
|
bst.add(1);
|
|
1545
1544
|
bst.add(2);
|
|
@@ -1613,7 +1612,7 @@ describe('BST _keyValueNodeOrEntryToNodeAndValue edge', () => {
|
|
|
1613
1612
|
});
|
|
1614
1613
|
});
|
|
1615
1614
|
|
|
1616
|
-
describe('BST
|
|
1615
|
+
describe('BST ceiling and higher', () => {
|
|
1617
1616
|
let bst: BST<number, string>;
|
|
1618
1617
|
|
|
1619
1618
|
beforeEach(() => {
|
|
@@ -1621,184 +1620,184 @@ describe('BST lowerBound and upperBound', () => {
|
|
|
1621
1620
|
bst = new BST<number, string>([10, 5, 15, 3, 7, 12, 20, 1, 4, 6, 8, 11, 13, 18, 25]);
|
|
1622
1621
|
});
|
|
1623
1622
|
|
|
1624
|
-
describe('
|
|
1625
|
-
it('should return the
|
|
1623
|
+
describe('ceiling', () => {
|
|
1624
|
+
it('should return the key with exact key match', () => {
|
|
1626
1625
|
// Test for key that exists in tree
|
|
1627
|
-
const
|
|
1628
|
-
expect(
|
|
1629
|
-
expect(
|
|
1626
|
+
const key = bst.ceiling(10);
|
|
1627
|
+
expect(key).toBeDefined();
|
|
1628
|
+
expect(key).toBe(10);
|
|
1630
1629
|
});
|
|
1631
1630
|
|
|
1632
|
-
it('should return the smallest
|
|
1631
|
+
it('should return the smallest key >= key when exact match not found', () => {
|
|
1633
1632
|
// Test for key 9 (doesn't exist, should return 10)
|
|
1634
|
-
const
|
|
1635
|
-
expect(
|
|
1636
|
-
expect(
|
|
1633
|
+
const key = bst.ceiling(9);
|
|
1634
|
+
expect(key).toBeDefined();
|
|
1635
|
+
expect(key).toBe(10);
|
|
1637
1636
|
});
|
|
1638
1637
|
|
|
1639
|
-
it('should return
|
|
1638
|
+
it('should return key with key >= search key from multiple candidates', () => {
|
|
1640
1639
|
// Test for key 6 (exists, should return 6)
|
|
1641
|
-
const
|
|
1642
|
-
expect(
|
|
1643
|
-
expect(
|
|
1640
|
+
const key = bst.ceiling(6);
|
|
1641
|
+
expect(key).toBeDefined();
|
|
1642
|
+
expect(key).toBe(6);
|
|
1644
1643
|
|
|
1645
1644
|
// Test for key 7 (exists, should return 7)
|
|
1646
|
-
const
|
|
1647
|
-
expect(
|
|
1648
|
-
expect(
|
|
1645
|
+
const key2 = bst.ceiling(7);
|
|
1646
|
+
expect(key2).toBeDefined();
|
|
1647
|
+
expect(key2).toBe(7);
|
|
1649
1648
|
});
|
|
1650
1649
|
|
|
1651
1650
|
it('should return smallest key >= search key for non-existent key in middle range', () => {
|
|
1652
1651
|
// Test for key 14 (doesn't exist, should return 15)
|
|
1653
|
-
const
|
|
1654
|
-
expect(
|
|
1655
|
-
expect(
|
|
1652
|
+
const key = bst.ceiling(14);
|
|
1653
|
+
expect(key).toBeDefined();
|
|
1654
|
+
expect(key).toBe(15);
|
|
1656
1655
|
|
|
1657
1656
|
// Test for key 11.5 (doesn't exist, should return 12)
|
|
1658
|
-
const
|
|
1659
|
-
expect(
|
|
1660
|
-
expect(
|
|
1657
|
+
const key2 = bst.ceiling(11.5);
|
|
1658
|
+
expect(key2).toBeDefined();
|
|
1659
|
+
expect(key2).toBe(12);
|
|
1661
1660
|
});
|
|
1662
1661
|
|
|
1663
1662
|
it('should return smallest key when search key is less than minimum', () => {
|
|
1664
1663
|
// Test for key 0 (should return 1, the minimum)
|
|
1665
|
-
const
|
|
1666
|
-
expect(
|
|
1667
|
-
expect(
|
|
1664
|
+
const key = bst.ceiling(0);
|
|
1665
|
+
expect(key).toBeDefined();
|
|
1666
|
+
expect(key).toBe(1);
|
|
1668
1667
|
|
|
1669
1668
|
// Test for key -100 (should return 1, the minimum)
|
|
1670
|
-
const
|
|
1671
|
-
expect(
|
|
1672
|
-
expect(
|
|
1669
|
+
const key2 = bst.ceiling(-100);
|
|
1670
|
+
expect(key2).toBeDefined();
|
|
1671
|
+
expect(key2).toBe(1);
|
|
1673
1672
|
});
|
|
1674
1673
|
|
|
1675
1674
|
it('should return undefined when search key is greater than maximum', () => {
|
|
1676
1675
|
// Test for key 100 (no key >= 100, should return undefined)
|
|
1677
|
-
const
|
|
1678
|
-
expect(
|
|
1676
|
+
const key = bst.ceiling(100);
|
|
1677
|
+
expect(key).toBeUndefined();
|
|
1679
1678
|
|
|
1680
1679
|
// Test for key 26 (no key >= 26, should return undefined)
|
|
1681
|
-
const
|
|
1682
|
-
expect(
|
|
1680
|
+
const key2 = bst.ceiling(26);
|
|
1681
|
+
expect(key2).toBeUndefined();
|
|
1683
1682
|
});
|
|
1684
1683
|
|
|
1685
|
-
it('should return correct
|
|
1684
|
+
it('should return correct key for edge cases', () => {
|
|
1686
1685
|
// Test for key 25 (maximum, should return 25)
|
|
1687
|
-
const
|
|
1688
|
-
expect(
|
|
1689
|
-
expect(
|
|
1686
|
+
const key = bst.ceiling(25);
|
|
1687
|
+
expect(key).toBeDefined();
|
|
1688
|
+
expect(key).toBe(25);
|
|
1690
1689
|
|
|
1691
1690
|
// Test for key 1 (minimum, should return 1)
|
|
1692
|
-
const
|
|
1693
|
-
expect(
|
|
1694
|
-
expect(
|
|
1691
|
+
const key2 = bst.ceiling(1);
|
|
1692
|
+
expect(key2).toBeDefined();
|
|
1693
|
+
expect(key2).toBe(1);
|
|
1695
1694
|
});
|
|
1696
1695
|
});
|
|
1697
1696
|
|
|
1698
|
-
describe('
|
|
1697
|
+
describe('higher', () => {
|
|
1699
1698
|
it('should return the smallest key > search key', () => {
|
|
1700
1699
|
// Test for key 10 (exists, should return next key > 10, which is 11)
|
|
1701
|
-
const
|
|
1702
|
-
expect(
|
|
1703
|
-
expect(
|
|
1700
|
+
const key = bst.higher(10);
|
|
1701
|
+
expect(key).toBeDefined();
|
|
1702
|
+
expect(key).toBe(11);
|
|
1704
1703
|
});
|
|
1705
1704
|
|
|
1706
1705
|
it('should return the smallest key > search key for non-existent key', () => {
|
|
1707
1706
|
// Test for key 9 (doesn't exist, should return 10, the smallest key > 9)
|
|
1708
|
-
const
|
|
1709
|
-
expect(
|
|
1710
|
-
expect(
|
|
1707
|
+
const key = bst.higher(9);
|
|
1708
|
+
expect(key).toBeDefined();
|
|
1709
|
+
expect(key).toBe(10);
|
|
1711
1710
|
|
|
1712
1711
|
// Test for key 14 (doesn't exist, should return 15)
|
|
1713
|
-
const
|
|
1714
|
-
expect(
|
|
1715
|
-
expect(
|
|
1712
|
+
const key2 = bst.higher(14);
|
|
1713
|
+
expect(key2).toBeDefined();
|
|
1714
|
+
expect(key2).toBe(15);
|
|
1716
1715
|
});
|
|
1717
1716
|
|
|
1718
1717
|
it('should skip equal keys and return the next larger key', () => {
|
|
1719
1718
|
// Test for key 5 (exists, should return 6, the smallest key > 5)
|
|
1720
|
-
const
|
|
1721
|
-
expect(
|
|
1722
|
-
expect(
|
|
1719
|
+
const key = bst.higher(5);
|
|
1720
|
+
expect(key).toBeDefined();
|
|
1721
|
+
expect(key).toBe(6);
|
|
1723
1722
|
|
|
1724
1723
|
// Test for key 20 (exists, should return 25)
|
|
1725
|
-
const
|
|
1726
|
-
expect(
|
|
1727
|
-
expect(
|
|
1724
|
+
const key2 = bst.higher(20);
|
|
1725
|
+
expect(key2).toBeDefined();
|
|
1726
|
+
expect(key2).toBe(25);
|
|
1728
1727
|
});
|
|
1729
1728
|
|
|
1730
1729
|
it('should return smallest key > search key for non-existent key in middle range', () => {
|
|
1731
1730
|
// Test for key 11.5 (doesn't exist, should return 12)
|
|
1732
|
-
const
|
|
1733
|
-
expect(
|
|
1734
|
-
expect(
|
|
1731
|
+
const key = bst.higher(11.5);
|
|
1732
|
+
expect(key).toBeDefined();
|
|
1733
|
+
expect(key).toBe(12);
|
|
1735
1734
|
|
|
1736
1735
|
// Test for key 19 (doesn't exist, should return 20)
|
|
1737
|
-
const
|
|
1738
|
-
expect(
|
|
1739
|
-
expect(
|
|
1736
|
+
const key2 = bst.higher(19);
|
|
1737
|
+
expect(key2).toBeDefined();
|
|
1738
|
+
expect(key2).toBe(20);
|
|
1740
1739
|
});
|
|
1741
1740
|
|
|
1742
1741
|
it('should return smallest key when search key is less than minimum', () => {
|
|
1743
1742
|
// Test for key 0 (should return 1, the minimum)
|
|
1744
|
-
const
|
|
1745
|
-
expect(
|
|
1746
|
-
expect(
|
|
1743
|
+
const key = bst.higher(0);
|
|
1744
|
+
expect(key).toBeDefined();
|
|
1745
|
+
expect(key).toBe(1);
|
|
1747
1746
|
|
|
1748
1747
|
// Test for key -100 (should return 1, the minimum)
|
|
1749
|
-
const
|
|
1750
|
-
expect(
|
|
1751
|
-
expect(
|
|
1748
|
+
const key2 = bst.higher(-100);
|
|
1749
|
+
expect(key2).toBeDefined();
|
|
1750
|
+
expect(key2).toBe(1);
|
|
1752
1751
|
});
|
|
1753
1752
|
|
|
1754
1753
|
it('should return undefined when search key is >= maximum', () => {
|
|
1755
1754
|
// Test for key 100 (no key > 100, should return undefined)
|
|
1756
|
-
const
|
|
1757
|
-
expect(
|
|
1755
|
+
const key = bst.higher(100);
|
|
1756
|
+
expect(key).toBeUndefined();
|
|
1758
1757
|
|
|
1759
1758
|
// Test for key 25 (maximum, no key > 25, should return undefined)
|
|
1760
|
-
const
|
|
1761
|
-
expect(
|
|
1759
|
+
const key2 = bst.higher(25);
|
|
1760
|
+
expect(key2).toBeUndefined();
|
|
1762
1761
|
|
|
1763
1762
|
// Test for key 26 (no key > 26, should return undefined)
|
|
1764
|
-
const
|
|
1765
|
-
expect(
|
|
1763
|
+
const key3 = bst.higher(26);
|
|
1764
|
+
expect(key3).toBeUndefined();
|
|
1766
1765
|
});
|
|
1767
1766
|
|
|
1768
|
-
it('should return correct
|
|
1767
|
+
it('should return correct key for edge cases', () => {
|
|
1769
1768
|
// Test for key 1 (minimum, should return next key 3)
|
|
1770
|
-
const
|
|
1771
|
-
expect(
|
|
1772
|
-
expect(
|
|
1769
|
+
const key = bst.higher(1);
|
|
1770
|
+
expect(key).toBeDefined();
|
|
1771
|
+
expect(key).toBe(3);
|
|
1773
1772
|
|
|
1774
1773
|
// Test for key 24 (doesn't exist, should return 25)
|
|
1775
|
-
const
|
|
1776
|
-
expect(
|
|
1777
|
-
expect(
|
|
1774
|
+
const key2 = bst.higher(24);
|
|
1775
|
+
expect(key2).toBeDefined();
|
|
1776
|
+
expect(key2).toBe(25);
|
|
1778
1777
|
});
|
|
1779
1778
|
});
|
|
1780
1779
|
|
|
1781
|
-
describe('
|
|
1782
|
-
it('should demonstrate difference between
|
|
1780
|
+
describe('ceiling vs higher comparison', () => {
|
|
1781
|
+
it('should demonstrate difference between ceiling and higher', () => {
|
|
1783
1782
|
const searchKey = 12;
|
|
1784
1783
|
|
|
1785
|
-
//
|
|
1786
|
-
const lower = bst.
|
|
1787
|
-
expect(lower
|
|
1784
|
+
// ceiling(12) should return 12 (key >= 12)
|
|
1785
|
+
const lower = bst.ceiling(searchKey);
|
|
1786
|
+
expect(lower).toBe(12);
|
|
1788
1787
|
|
|
1789
|
-
//
|
|
1790
|
-
const upper = bst.
|
|
1791
|
-
expect(upper
|
|
1788
|
+
// higher(12) should return 13 (key > 12)
|
|
1789
|
+
const upper = bst.higher(searchKey);
|
|
1790
|
+
expect(upper).toBe(13);
|
|
1792
1791
|
});
|
|
1793
1792
|
|
|
1794
1793
|
it('should return same result for non-existent key in both methods', () => {
|
|
1795
1794
|
const searchKey = 11.5;
|
|
1796
1795
|
|
|
1797
|
-
// Both should return 12 (smallest key >= 11.5 for
|
|
1798
|
-
const lower = bst.
|
|
1799
|
-
const upper = bst.
|
|
1800
|
-
expect(lower
|
|
1801
|
-
expect(upper
|
|
1796
|
+
// Both should return 12 (smallest key >= 11.5 for ceiling, smallest key > 11.5 for higher)
|
|
1797
|
+
const lower = bst.ceiling(searchKey);
|
|
1798
|
+
const upper = bst.higher(searchKey);
|
|
1799
|
+
expect(lower).toBe(12);
|
|
1800
|
+
expect(upper).toBe(12);
|
|
1802
1801
|
});
|
|
1803
1802
|
});
|
|
1804
1803
|
|
|
@@ -1825,90 +1824,90 @@ describe('BST lowerBound and upperBound', () => {
|
|
|
1825
1824
|
keys.forEach(k => bst.add(k, `val-${k}`));
|
|
1826
1825
|
});
|
|
1827
1826
|
|
|
1828
|
-
describe('
|
|
1827
|
+
describe('ceiling (First key >= key)', () => {
|
|
1829
1828
|
test('should return strict match if key exists', () => {
|
|
1830
1829
|
// Case: Key exists
|
|
1831
|
-
expect(bst.
|
|
1832
|
-
expect(bst.
|
|
1833
|
-
expect(bst.
|
|
1830
|
+
expect(bst.ceiling(10)).toBe(10);
|
|
1831
|
+
expect(bst.ceiling(6)).toBe(6);
|
|
1832
|
+
expect(bst.ceiling(20)).toBe(20);
|
|
1834
1833
|
});
|
|
1835
1834
|
|
|
1836
|
-
test('should return next larger
|
|
1835
|
+
test('should return next larger key if key does not exist', () => {
|
|
1837
1836
|
// Case: Key doesn't exist, but falls within range
|
|
1838
|
-
expect(bst.
|
|
1839
|
-
expect(bst.
|
|
1840
|
-
expect(bst.
|
|
1837
|
+
expect(bst.ceiling(7)).toBe(8); // 7 -> 8
|
|
1838
|
+
expect(bst.ceiling(11)).toBe(12); // 11 -> 12
|
|
1839
|
+
expect(bst.ceiling(3)).toBe(5); // 3 -> 5
|
|
1841
1840
|
});
|
|
1842
1841
|
|
|
1843
|
-
test('should return smallest
|
|
1842
|
+
test('should return smallest key if key is smaller than min', () => {
|
|
1844
1843
|
// Case: Key is smaller than the minimum value in the tree
|
|
1845
|
-
expect(bst.
|
|
1846
|
-
expect(bst.
|
|
1844
|
+
expect(bst.ceiling(0)).toBe(2);
|
|
1845
|
+
expect(bst.ceiling(-100)).toBe(2);
|
|
1847
1846
|
});
|
|
1848
1847
|
|
|
1849
1848
|
test('should return undefined if key is larger than max', () => {
|
|
1850
1849
|
// Case: Key is larger than the maximum value in the tree
|
|
1851
|
-
expect(bst.
|
|
1852
|
-
expect(bst.
|
|
1850
|
+
expect(bst.ceiling(21)).toBeUndefined();
|
|
1851
|
+
expect(bst.ceiling(100)).toBeUndefined();
|
|
1853
1852
|
});
|
|
1854
1853
|
|
|
1855
1854
|
test('should work with IterationType.RECURSIVE explicitly', () => {
|
|
1856
|
-
expect(bst.
|
|
1857
|
-
expect(bst.
|
|
1858
|
-
expect(bst.
|
|
1855
|
+
expect(bst.ceiling(7)).toBe(8);
|
|
1856
|
+
expect(bst.ceiling(10)).toBe(10);
|
|
1857
|
+
expect(bst.ceiling(21)).toBeUndefined();
|
|
1859
1858
|
});
|
|
1860
1859
|
|
|
1861
1860
|
test('should work with IterationType.ITERATIVE explicitly', () => {
|
|
1862
|
-
expect(bst.
|
|
1863
|
-
expect(bst.
|
|
1864
|
-
expect(bst.
|
|
1861
|
+
expect(bst.ceiling(7)).toBe(8);
|
|
1862
|
+
expect(bst.ceiling(10)).toBe(10);
|
|
1863
|
+
expect(bst.ceiling(21)).toBeUndefined();
|
|
1865
1864
|
});
|
|
1866
1865
|
});
|
|
1867
1866
|
|
|
1868
|
-
describe('
|
|
1869
|
-
test('should return next larger
|
|
1867
|
+
describe('higher (First key > key)', () => {
|
|
1868
|
+
test('should return next larger key even if key exists', () => {
|
|
1870
1869
|
// Case: Key exists, but we want strictly greater
|
|
1871
|
-
expect(bst.
|
|
1872
|
-
expect(bst.
|
|
1873
|
-
expect(bst.
|
|
1870
|
+
expect(bst.higher(10)).toBe(12); // > 10 is 12
|
|
1871
|
+
expect(bst.higher(6)).toBe(8); // > 6 is 8
|
|
1872
|
+
expect(bst.higher(20)).toBeUndefined(); // > 20 is undefined
|
|
1874
1873
|
});
|
|
1875
1874
|
|
|
1876
|
-
test('should return next larger
|
|
1875
|
+
test('should return next larger key if key does not exist', () => {
|
|
1877
1876
|
// Case: Key doesn't exist
|
|
1878
|
-
expect(bst.
|
|
1879
|
-
expect(bst.
|
|
1877
|
+
expect(bst.higher(7)).toBe(8);
|
|
1878
|
+
expect(bst.higher(11)).toBe(12);
|
|
1880
1879
|
});
|
|
1881
1880
|
|
|
1882
1881
|
test('should return undefined if key is larger than or equal to max', () => {
|
|
1883
|
-
expect(bst.
|
|
1884
|
-
expect(bst.
|
|
1882
|
+
expect(bst.higher(20)).toBeUndefined();
|
|
1883
|
+
expect(bst.higher(21)).toBeUndefined();
|
|
1885
1884
|
});
|
|
1886
1885
|
|
|
1887
1886
|
test('should work with IterationType.RECURSIVE explicitly', () => {
|
|
1888
|
-
expect(bst.
|
|
1889
|
-
expect(bst.
|
|
1887
|
+
expect(bst.higher(10)).toBe(12);
|
|
1888
|
+
expect(bst.higher(20)).toBeUndefined();
|
|
1890
1889
|
});
|
|
1891
1890
|
});
|
|
1892
1891
|
|
|
1893
1892
|
describe('Edge Cases', () => {
|
|
1894
1893
|
test('should handle empty tree', () => {
|
|
1895
1894
|
const emptyTree = new BST<number, string>();
|
|
1896
|
-
expect(emptyTree.
|
|
1897
|
-
expect(emptyTree.
|
|
1895
|
+
expect(emptyTree.ceiling(10)).toBeUndefined();
|
|
1896
|
+
expect(emptyTree.higher(10)).toBeUndefined();
|
|
1898
1897
|
});
|
|
1899
1898
|
|
|
1900
1899
|
test('should handle single node tree', () => {
|
|
1901
1900
|
const singleNodeTree = new BST<number, string>();
|
|
1902
1901
|
singleNodeTree.add(10);
|
|
1903
1902
|
|
|
1904
|
-
//
|
|
1905
|
-
expect(singleNodeTree.
|
|
1906
|
-
expect(singleNodeTree.
|
|
1907
|
-
expect(singleNodeTree.
|
|
1903
|
+
// ceiling
|
|
1904
|
+
expect(singleNodeTree.ceiling(5)).toBe(10);
|
|
1905
|
+
expect(singleNodeTree.ceiling(10)).toBe(10);
|
|
1906
|
+
expect(singleNodeTree.ceiling(15)).toBeUndefined();
|
|
1908
1907
|
|
|
1909
|
-
//
|
|
1910
|
-
expect(singleNodeTree.
|
|
1911
|
-
expect(singleNodeTree.
|
|
1908
|
+
// higher
|
|
1909
|
+
expect(singleNodeTree.higher(5)).toBe(10);
|
|
1910
|
+
expect(singleNodeTree.higher(10)).toBeUndefined();
|
|
1912
1911
|
});
|
|
1913
1912
|
});
|
|
1914
1913
|
|
|
@@ -1928,9 +1927,9 @@ describe('BST lowerBound and upperBound', () => {
|
|
|
1928
1927
|
const testQueries = generateRandomArray(200, 0, 1000);
|
|
1929
1928
|
|
|
1930
1929
|
testQueries.forEach(queryKey => {
|
|
1931
|
-
// 1. Verify
|
|
1932
|
-
const recLower = fuzzTree.
|
|
1933
|
-
const iterLower = fuzzTree.
|
|
1930
|
+
// 1. Verify ceiling
|
|
1931
|
+
const recLower = fuzzTree.ceiling(queryKey);
|
|
1932
|
+
const iterLower = fuzzTree.ceiling(queryKey);
|
|
1934
1933
|
|
|
1935
1934
|
// Verify consistency between recursive and iterative
|
|
1936
1935
|
expect(recLower).toBe(iterLower);
|
|
@@ -1939,9 +1938,9 @@ describe('BST lowerBound and upperBound', () => {
|
|
|
1939
1938
|
const expectedLower = sortedKeys.find(k => k >= queryKey);
|
|
1940
1939
|
expect(recLower).toBe(expectedLower);
|
|
1941
1940
|
|
|
1942
|
-
// 2. Verify
|
|
1943
|
-
const recUpper = fuzzTree.
|
|
1944
|
-
const iterUpper = fuzzTree.
|
|
1941
|
+
// 2. Verify higher
|
|
1942
|
+
const recUpper = fuzzTree.higher(queryKey);
|
|
1943
|
+
const iterUpper = fuzzTree.higher(queryKey);
|
|
1945
1944
|
|
|
1946
1945
|
// Verify consistency
|
|
1947
1946
|
expect(recUpper).toBe(iterUpper);
|
|
@@ -1959,14 +1958,14 @@ describe('BST lowerBound and upperBound', () => {
|
|
|
1959
1958
|
bst = new BST<number, string>();
|
|
1960
1959
|
});
|
|
1961
1960
|
|
|
1962
|
-
it('
|
|
1963
|
-
const
|
|
1964
|
-
expect(
|
|
1961
|
+
it('ceiling should return undefined on empty tree', () => {
|
|
1962
|
+
const key = bst.ceiling(10);
|
|
1963
|
+
expect(key).toBeUndefined();
|
|
1965
1964
|
});
|
|
1966
1965
|
|
|
1967
|
-
it('
|
|
1968
|
-
const
|
|
1969
|
-
expect(
|
|
1966
|
+
it('higher should return undefined on empty tree', () => {
|
|
1967
|
+
const key = bst.higher(10);
|
|
1968
|
+
expect(key).toBeUndefined();
|
|
1970
1969
|
});
|
|
1971
1970
|
});
|
|
1972
1971
|
|
|
@@ -1976,259 +1975,1327 @@ describe('BST lowerBound and upperBound', () => {
|
|
|
1976
1975
|
bst.add(10, 'ten');
|
|
1977
1976
|
});
|
|
1978
1977
|
|
|
1979
|
-
it('
|
|
1980
|
-
const
|
|
1981
|
-
expect(
|
|
1982
|
-
expect(
|
|
1978
|
+
it('ceiling should return the key if key matches', () => {
|
|
1979
|
+
const key = bst.ceiling(10);
|
|
1980
|
+
expect(key).toBeDefined();
|
|
1981
|
+
expect(key).toBe(10);
|
|
1983
1982
|
});
|
|
1984
1983
|
|
|
1985
|
-
it('
|
|
1986
|
-
const
|
|
1987
|
-
expect(
|
|
1988
|
-
expect(
|
|
1984
|
+
it('ceiling should return the key if search key is less', () => {
|
|
1985
|
+
const key = bst.ceiling(5);
|
|
1986
|
+
expect(key).toBeDefined();
|
|
1987
|
+
expect(key).toBe(10);
|
|
1989
1988
|
});
|
|
1990
1989
|
|
|
1991
|
-
it('
|
|
1992
|
-
const
|
|
1993
|
-
expect(
|
|
1990
|
+
it('ceiling should return undefined if search key is greater', () => {
|
|
1991
|
+
const key = bst.ceiling(15);
|
|
1992
|
+
expect(key).toBeUndefined();
|
|
1994
1993
|
});
|
|
1995
1994
|
|
|
1996
|
-
it('
|
|
1997
|
-
const
|
|
1998
|
-
expect(
|
|
1995
|
+
it('higher should return undefined if key matches', () => {
|
|
1996
|
+
const key = bst.higher(10);
|
|
1997
|
+
expect(key).toBeUndefined();
|
|
1999
1998
|
});
|
|
2000
1999
|
|
|
2001
|
-
it('
|
|
2002
|
-
const
|
|
2003
|
-
expect(
|
|
2004
|
-
expect(
|
|
2000
|
+
it('higher should return the key if search key is less', () => {
|
|
2001
|
+
const key = bst.higher(5);
|
|
2002
|
+
expect(key).toBeDefined();
|
|
2003
|
+
expect(key).toBe(10);
|
|
2005
2004
|
});
|
|
2006
2005
|
|
|
2007
|
-
it('
|
|
2008
|
-
const
|
|
2009
|
-
expect(
|
|
2006
|
+
it('higher should return undefined if search key is greater', () => {
|
|
2007
|
+
const key = bst.higher(15);
|
|
2008
|
+
expect(key).toBeUndefined();
|
|
2010
2009
|
});
|
|
2011
2010
|
});
|
|
2012
2011
|
|
|
2013
2012
|
describe('practical LeetCode use case: Range search', () => {
|
|
2014
2013
|
it('should find all keys in range [12, 20]', () => {
|
|
2015
|
-
// Use
|
|
2016
|
-
const start = bst.
|
|
2014
|
+
// Use ceiling to find start of range and higher to find end
|
|
2015
|
+
const start = bst.ceiling(12);
|
|
2017
2016
|
|
|
2018
|
-
|
|
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);
|
|
2017
|
+
expect(start).toBe(12);
|
|
2024
2018
|
|
|
2025
2019
|
// Let's verify we found the correct bounds
|
|
2026
|
-
expect(start
|
|
2020
|
+
expect(start).toBeGreaterThanOrEqual(12);
|
|
2027
2021
|
|
|
2028
2022
|
// Check overlap
|
|
2029
|
-
const rangeStart = bst.
|
|
2030
|
-
const rangeEndInclusive = bst.
|
|
2023
|
+
const rangeStart = bst.ceiling(12);
|
|
2024
|
+
const rangeEndInclusive = bst.ceiling(20); // Should be 20
|
|
2031
2025
|
|
|
2032
|
-
expect(rangeStart
|
|
2033
|
-
expect(rangeEndInclusive
|
|
2026
|
+
expect(rangeStart).toBe(12);
|
|
2027
|
+
expect(rangeEndInclusive).toBe(20);
|
|
2034
2028
|
});
|
|
2035
2029
|
|
|
2036
2030
|
it('should help identify interval overlaps', () => {
|
|
2037
2031
|
// Check if interval [14, 19] overlaps with any existing interval
|
|
2038
|
-
// Use
|
|
2039
|
-
const nextKey = bst.
|
|
2040
|
-
// Use
|
|
2041
|
-
const firstInRange = bst.
|
|
2032
|
+
// Use higher to find the first key > 19
|
|
2033
|
+
const nextKey = bst.higher(19);
|
|
2034
|
+
// Use ceiling to find the first key >= 14
|
|
2035
|
+
const firstInRange = bst.ceiling(14);
|
|
2042
2036
|
|
|
2043
|
-
expect(firstInRange
|
|
2044
|
-
expect(nextKey
|
|
2037
|
+
expect(firstInRange).toBe(15);
|
|
2038
|
+
expect(nextKey).toBe(20);
|
|
2045
2039
|
});
|
|
2046
2040
|
});
|
|
2047
2041
|
});
|
|
2048
2042
|
|
|
2049
|
-
describe('BST
|
|
2043
|
+
describe('BST Range Query Methods', () => {
|
|
2050
2044
|
let bst: BST<number, string>;
|
|
2051
2045
|
|
|
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
2046
|
beforeEach(() => {
|
|
2058
|
-
//
|
|
2059
|
-
|
|
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}`));
|
|
2047
|
+
// Create a balanced BST: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 20]
|
|
2048
|
+
bst = new BST([10, 5, 15, 3, 7, 13, 17, 1, 4, 6, 9, 11, 14, 16, 19, 20]);
|
|
2070
2049
|
});
|
|
2071
2050
|
|
|
2072
|
-
describe('
|
|
2073
|
-
test('should
|
|
2074
|
-
|
|
2075
|
-
expect(
|
|
2051
|
+
describe('ceiling - finds >= key (minimum value >= target)', () => {
|
|
2052
|
+
test('should find ceiling when key exists', () => {
|
|
2053
|
+
const result = bst.ceiling(10);
|
|
2054
|
+
expect(result).toBeDefined();
|
|
2055
|
+
expect(result).toBe(10);
|
|
2056
|
+
});
|
|
2057
|
+
|
|
2058
|
+
test('should find ceiling when key does not exist but higher value exists', () => {
|
|
2059
|
+
const result = bst.ceiling(8);
|
|
2060
|
+
expect(result).toBeDefined();
|
|
2061
|
+
expect(result).toBe(9);
|
|
2062
|
+
});
|
|
2063
|
+
|
|
2064
|
+
test('should return undefined when no ceiling exists (key greater than all)', () => {
|
|
2065
|
+
const result = bst.ceiling(100);
|
|
2066
|
+
expect(result).toBeUndefined();
|
|
2067
|
+
});
|
|
2068
|
+
|
|
2069
|
+
test('should find minimum element as ceiling for key smaller than all', () => {
|
|
2070
|
+
const result = bst.ceiling(-10);
|
|
2071
|
+
expect(result).toBeDefined();
|
|
2072
|
+
expect(result).toBe(1);
|
|
2073
|
+
});
|
|
2074
|
+
|
|
2075
|
+
test('should handle ceiling with node input', () => {
|
|
2076
|
+
const targetNode = bst.getNode(7);
|
|
2077
|
+
expect(targetNode).toBeDefined();
|
|
2078
|
+
const result = bst.ceiling(targetNode!);
|
|
2079
|
+
expect(result).toBe(7);
|
|
2080
|
+
});
|
|
2081
|
+
|
|
2082
|
+
test('should handle ceiling with entry input', () => {
|
|
2083
|
+
const result = bst.ceiling([11, 'test']);
|
|
2084
|
+
expect(result).toBeDefined();
|
|
2085
|
+
expect(result).toBe(11);
|
|
2086
|
+
});
|
|
2087
|
+
|
|
2088
|
+
test('should handle null/undefined inputs', () => {
|
|
2089
|
+
expect(bst.ceiling(null)).toBeUndefined();
|
|
2090
|
+
expect(bst.ceiling(undefined)).toBeUndefined();
|
|
2076
2091
|
});
|
|
2077
2092
|
|
|
2078
|
-
test('should
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
expect(
|
|
2093
|
+
test('should work with ITERATIVE mode', () => {
|
|
2094
|
+
const result = bst.ceiling(12);
|
|
2095
|
+
expect(result).toBeDefined();
|
|
2096
|
+
expect(result).toBe(13);
|
|
2097
|
+
});
|
|
2098
|
+
|
|
2099
|
+
test('should work with RECURSIVE mode', () => {
|
|
2100
|
+
const result = bst.ceiling(12);
|
|
2101
|
+
expect(result).toBeDefined();
|
|
2102
|
+
expect(result).toBe(13);
|
|
2103
|
+
});
|
|
2104
|
+
|
|
2105
|
+
test('should find exact match as ceiling', () => {
|
|
2106
|
+
const result = bst.ceiling(15);
|
|
2107
|
+
expect(result).toBeDefined();
|
|
2108
|
+
expect(result).toBe(15);
|
|
2109
|
+
});
|
|
2110
|
+
});
|
|
2111
|
+
|
|
2112
|
+
describe('higher - finds > key (minimum value > target)', () => {
|
|
2113
|
+
test('should find higher when key exists (exclude exact match)', () => {
|
|
2114
|
+
const result = bst.higher(10);
|
|
2115
|
+
expect(result).toBeDefined();
|
|
2116
|
+
expect(result).toBe(11);
|
|
2117
|
+
expect(result).not.toBe(10);
|
|
2118
|
+
});
|
|
2119
|
+
|
|
2120
|
+
test('should find higher when key does not exist', () => {
|
|
2121
|
+
const result = bst.higher(8);
|
|
2122
|
+
expect(result).toBeDefined();
|
|
2123
|
+
expect(result).toBe(9);
|
|
2124
|
+
});
|
|
2125
|
+
|
|
2126
|
+
test('should return undefined when no higher exists (key >= all)', () => {
|
|
2127
|
+
const result = bst.higher(20);
|
|
2128
|
+
expect(result).toBeUndefined();
|
|
2129
|
+
});
|
|
2130
|
+
|
|
2131
|
+
test('should find minimum element as higher for key < all', () => {
|
|
2132
|
+
const result = bst.higher(-10);
|
|
2133
|
+
expect(result).toBeDefined();
|
|
2134
|
+
expect(result).toBe(1);
|
|
2135
|
+
});
|
|
2136
|
+
|
|
2137
|
+
test('should not return the key itself', () => {
|
|
2138
|
+
const result = bst.higher(7);
|
|
2139
|
+
expect(result).not.toBe(7);
|
|
2140
|
+
expect(result).toBe(9);
|
|
2141
|
+
});
|
|
2142
|
+
|
|
2143
|
+
test('should handle higher with node input', () => {
|
|
2144
|
+
const targetNode = bst.getNode(5);
|
|
2145
|
+
expect(targetNode).toBeDefined();
|
|
2146
|
+
const result = bst.higher(targetNode!);
|
|
2147
|
+
expect(result).toBeGreaterThan(5);
|
|
2148
|
+
expect(result).toBe(6);
|
|
2149
|
+
});
|
|
2150
|
+
|
|
2151
|
+
test('should work with ITERATIVE mode', () => {
|
|
2152
|
+
const result = bst.higher(13);
|
|
2153
|
+
expect(result).toBeDefined();
|
|
2154
|
+
expect(result).toBe(14);
|
|
2155
|
+
});
|
|
2156
|
+
|
|
2157
|
+
test('should work with RECURSIVE mode', () => {
|
|
2158
|
+
const result = bst.higher(13);
|
|
2159
|
+
expect(result).toBeDefined();
|
|
2160
|
+
expect(result).toBe(14);
|
|
2161
|
+
});
|
|
2162
|
+
});
|
|
2163
|
+
|
|
2164
|
+
describe('floor - finds <= key (maximum value <= target)', () => {
|
|
2165
|
+
test('should find floor when key exists', () => {
|
|
2166
|
+
const result = bst.floor(10);
|
|
2167
|
+
expect(result).toBeDefined();
|
|
2168
|
+
expect(result).toBe(10);
|
|
2169
|
+
});
|
|
2170
|
+
|
|
2171
|
+
test('should find floor when key does not exist but lower value exists', () => {
|
|
2172
|
+
const result = bst.floor(12);
|
|
2173
|
+
expect(result).toBeDefined();
|
|
2174
|
+
expect(result).toBe(11);
|
|
2175
|
+
});
|
|
2176
|
+
|
|
2177
|
+
test('should return undefined when no floor exists (key less than all)', () => {
|
|
2178
|
+
const result = bst.floor(-10);
|
|
2179
|
+
expect(result).toBeUndefined();
|
|
2180
|
+
});
|
|
2082
2181
|
|
|
2083
|
-
|
|
2084
|
-
|
|
2182
|
+
test('should find maximum element as floor for key greater than all', () => {
|
|
2183
|
+
const result = bst.floor(100);
|
|
2184
|
+
expect(result).toBeDefined();
|
|
2185
|
+
expect(result).toBe(20);
|
|
2186
|
+
});
|
|
2085
2187
|
|
|
2086
|
-
|
|
2087
|
-
|
|
2188
|
+
test('should handle floor with node input', () => {
|
|
2189
|
+
const targetNode = bst.getNode(13);
|
|
2190
|
+
expect(targetNode).toBeDefined();
|
|
2191
|
+
const result = bst.floor(targetNode!);
|
|
2192
|
+
expect(result).toBe(13);
|
|
2088
2193
|
});
|
|
2089
2194
|
|
|
2090
|
-
test('should
|
|
2091
|
-
|
|
2092
|
-
|
|
2195
|
+
test('should handle floor with entry input', () => {
|
|
2196
|
+
const result = bst.floor([16, 'test']);
|
|
2197
|
+
expect(result).toBeDefined();
|
|
2198
|
+
expect(result).toBe(16);
|
|
2199
|
+
});
|
|
2093
2200
|
|
|
2094
|
-
|
|
2095
|
-
expect(bst.
|
|
2201
|
+
test('should handle null/undefined inputs', () => {
|
|
2202
|
+
expect(bst.floor(null)).toBeUndefined();
|
|
2203
|
+
expect(bst.floor(undefined)).toBeUndefined();
|
|
2204
|
+
});
|
|
2096
2205
|
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
expect(
|
|
2206
|
+
test('should work with ITERATIVE mode', () => {
|
|
2207
|
+
const result = bst.floor(12);
|
|
2208
|
+
expect(result).toBeDefined();
|
|
2209
|
+
expect(result).toBe(11);
|
|
2100
2210
|
});
|
|
2101
2211
|
|
|
2102
|
-
test('should
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2212
|
+
test('should work with RECURSIVE mode', () => {
|
|
2213
|
+
const result = bst.floor(12);
|
|
2214
|
+
expect(result).toBeDefined();
|
|
2215
|
+
expect(result).toBe(11);
|
|
2216
|
+
});
|
|
2106
2217
|
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
expect(
|
|
2110
|
-
expect(
|
|
2218
|
+
test('should find exact match as floor', () => {
|
|
2219
|
+
const result = bst.floor(15);
|
|
2220
|
+
expect(result).toBeDefined();
|
|
2221
|
+
expect(result).toBe(15);
|
|
2222
|
+
});
|
|
2111
2223
|
|
|
2112
|
-
|
|
2113
|
-
|
|
2224
|
+
test('should correctly find floor between two keys', () => {
|
|
2225
|
+
const result = bst.floor(8);
|
|
2226
|
+
expect(result).toBeDefined();
|
|
2227
|
+
expect(result).toBe(7);
|
|
2228
|
+
expect(result).toBeLessThan(8);
|
|
2114
2229
|
});
|
|
2115
2230
|
});
|
|
2116
2231
|
|
|
2117
|
-
describe('
|
|
2118
|
-
test('should
|
|
2119
|
-
const
|
|
2232
|
+
describe('lower - finds < key (maximum value < target)', () => {
|
|
2233
|
+
test('should find lower when key exists (exclude exact match)', () => {
|
|
2234
|
+
const result = bst.lower(10);
|
|
2235
|
+
expect(result).toBeDefined();
|
|
2236
|
+
expect(result).toBe(9);
|
|
2237
|
+
expect(result).not.toBe(10);
|
|
2238
|
+
});
|
|
2239
|
+
|
|
2240
|
+
test('should find lower when key does not exist', () => {
|
|
2241
|
+
const result = bst.lower(12);
|
|
2242
|
+
expect(result).toBeDefined();
|
|
2243
|
+
expect(result).toBe(11);
|
|
2244
|
+
});
|
|
2245
|
+
|
|
2246
|
+
test('should return undefined when no lower exists (key <= all)', () => {
|
|
2247
|
+
const result = bst.lower(1);
|
|
2248
|
+
expect(result).toBeUndefined();
|
|
2249
|
+
});
|
|
2250
|
+
|
|
2251
|
+
test('should find maximum element as lower for key > all', () => {
|
|
2252
|
+
const result = bst.lower(100);
|
|
2253
|
+
expect(result).toBeDefined();
|
|
2254
|
+
expect(result).toBe(20);
|
|
2255
|
+
});
|
|
2256
|
+
|
|
2257
|
+
test('should not return the key itself', () => {
|
|
2258
|
+
const result = bst.lower(15);
|
|
2259
|
+
expect(result).not.toBe(15);
|
|
2260
|
+
expect(result).toBe(14);
|
|
2261
|
+
});
|
|
2120
2262
|
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
expect(
|
|
2124
|
-
|
|
2263
|
+
test('should handle lower with node input', () => {
|
|
2264
|
+
const targetNode = bst.getNode(13);
|
|
2265
|
+
expect(targetNode).toBeDefined();
|
|
2266
|
+
const result = bst.lower(targetNode!);
|
|
2267
|
+
expect(result).toBeLessThan(13);
|
|
2268
|
+
expect(result).toBe(11);
|
|
2125
2269
|
});
|
|
2126
2270
|
|
|
2127
|
-
test('should
|
|
2128
|
-
const
|
|
2271
|
+
test('should handle lower with entry input', () => {
|
|
2272
|
+
const result = bst.lower([17, 'test']);
|
|
2273
|
+
expect(result).toBeDefined();
|
|
2274
|
+
expect(result).toBe(16);
|
|
2275
|
+
});
|
|
2276
|
+
|
|
2277
|
+
test('should work with ITERATIVE mode', () => {
|
|
2278
|
+
const result = bst.lower(14);
|
|
2279
|
+
expect(result).toBeDefined();
|
|
2280
|
+
expect(result).toBe(13);
|
|
2281
|
+
});
|
|
2129
2282
|
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
expect(
|
|
2133
|
-
expect(
|
|
2283
|
+
test('should work with RECURSIVE mode', () => {
|
|
2284
|
+
const result = bst.lower(14);
|
|
2285
|
+
expect(result).toBeDefined();
|
|
2286
|
+
expect(result).toBe(13);
|
|
2134
2287
|
});
|
|
2135
2288
|
});
|
|
2136
2289
|
|
|
2137
|
-
describe('
|
|
2138
|
-
test('
|
|
2139
|
-
|
|
2140
|
-
expect(
|
|
2290
|
+
describe('Edge cases and special scenarios', () => {
|
|
2291
|
+
test('single element tree - ceiling', () => {
|
|
2292
|
+
const singleBst = new BST([5]);
|
|
2293
|
+
expect(singleBst.ceiling(5)).toBe(5);
|
|
2294
|
+
expect(singleBst.ceiling(3)).toBe(5);
|
|
2295
|
+
expect(singleBst.ceiling(7)).toBeUndefined();
|
|
2141
2296
|
});
|
|
2142
2297
|
|
|
2143
|
-
test('
|
|
2144
|
-
|
|
2298
|
+
test('single element tree - higher', () => {
|
|
2299
|
+
const singleBst = new BST([5]);
|
|
2300
|
+
expect(singleBst.higher(5)).toBeUndefined();
|
|
2301
|
+
expect(singleBst.higher(3)).toBe(5);
|
|
2302
|
+
});
|
|
2303
|
+
|
|
2304
|
+
test('single element tree - floor', () => {
|
|
2305
|
+
const singleBst = new BST([5]);
|
|
2306
|
+
expect(singleBst.floor(5)).toBe(5);
|
|
2307
|
+
expect(singleBst.floor(7)).toBe(5);
|
|
2308
|
+
expect(singleBst.floor(3)).toBeUndefined();
|
|
2309
|
+
});
|
|
2310
|
+
|
|
2311
|
+
test('single element tree - lower', () => {
|
|
2312
|
+
const singleBst = new BST([5]);
|
|
2313
|
+
expect(singleBst.lower(5)).toBeUndefined();
|
|
2314
|
+
expect(singleBst.lower(7)).toBe(5);
|
|
2315
|
+
});
|
|
2316
|
+
|
|
2317
|
+
test('empty tree handling', () => {
|
|
2318
|
+
const emptyBst = new BST<number, string>();
|
|
2319
|
+
expect(emptyBst.ceiling(5)).toBeUndefined();
|
|
2320
|
+
expect(emptyBst.higher(5)).toBeUndefined();
|
|
2321
|
+
expect(emptyBst.floor(5)).toBeUndefined();
|
|
2322
|
+
expect(emptyBst.lower(5)).toBeUndefined();
|
|
2323
|
+
});
|
|
2324
|
+
|
|
2325
|
+
test('ceiling and floor of adjacent keys', () => {
|
|
2326
|
+
const ceiling = bst.ceiling(5);
|
|
2327
|
+
const floor = bst.floor(6);
|
|
2328
|
+
expect(ceiling).toBe(5);
|
|
2329
|
+
expect(floor).toBe(6);
|
|
2330
|
+
});
|
|
2331
|
+
|
|
2332
|
+
test('higher and lower of adjacent keys', () => {
|
|
2333
|
+
const higher = bst.higher(5);
|
|
2334
|
+
const lower = bst.lower(6);
|
|
2335
|
+
expect(higher).toBe(6);
|
|
2336
|
+
expect(lower).toBe(5);
|
|
2145
2337
|
});
|
|
2146
2338
|
});
|
|
2147
2339
|
|
|
2148
|
-
describe('
|
|
2149
|
-
test('
|
|
2150
|
-
const
|
|
2151
|
-
|
|
2152
|
-
|
|
2340
|
+
describe('Predicate-based search', () => {
|
|
2341
|
+
test('ceiling with predicate function', () => {
|
|
2342
|
+
const result = bst.ceiling((node: BSTNode<number, string>) => node.key >= 10);
|
|
2343
|
+
expect(result).toBeDefined();
|
|
2344
|
+
expect(result).toBeGreaterThanOrEqual(10);
|
|
2345
|
+
});
|
|
2153
2346
|
|
|
2154
|
-
|
|
2347
|
+
test('floor with predicate function', () => {
|
|
2348
|
+
const result = bst.floor((node: BSTNode<number, string>) => node.key <= 15);
|
|
2349
|
+
expect(result).toBeDefined();
|
|
2350
|
+
expect(result).toBeLessThanOrEqual(15);
|
|
2351
|
+
});
|
|
2155
2352
|
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2353
|
+
test('higher with predicate function', () => {
|
|
2354
|
+
const result = bst.higher((node: BSTNode<number, string>) => node.key > 10);
|
|
2355
|
+
expect(result).toBeDefined();
|
|
2356
|
+
expect(result).toBeGreaterThan(10);
|
|
2357
|
+
});
|
|
2159
2358
|
|
|
2160
|
-
|
|
2161
|
-
|
|
2359
|
+
test('lower with predicate function', () => {
|
|
2360
|
+
const result = bst.lower((node: BSTNode<number, string>) => node.key < 15);
|
|
2361
|
+
expect(result).toBeDefined();
|
|
2362
|
+
expect(result).toBeLessThan(15);
|
|
2363
|
+
});
|
|
2364
|
+
});
|
|
2162
2365
|
|
|
2163
|
-
|
|
2164
|
-
|
|
2366
|
+
describe('Custom comparator', () => {
|
|
2367
|
+
test('should work with reverse order comparator', () => {
|
|
2368
|
+
const reverseBst = new BST([1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 20], {
|
|
2369
|
+
comparator: (a: number, b: number) => b - a // reverse order
|
|
2165
2370
|
});
|
|
2371
|
+
|
|
2372
|
+
// In reverse order tree: keys are stored in descending order
|
|
2373
|
+
// ceiling (>=) should still work correctly
|
|
2374
|
+
const ceiling = reverseBst.ceiling(10);
|
|
2375
|
+
expect(ceiling).toBeDefined();
|
|
2376
|
+
expect(ceiling).toBeLessThanOrEqual(10);
|
|
2166
2377
|
});
|
|
2167
2378
|
|
|
2168
|
-
test('
|
|
2169
|
-
const
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
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
|
-
}
|
|
2379
|
+
test('should work with string comparator', () => {
|
|
2380
|
+
const stringBst = new BST(
|
|
2381
|
+
[
|
|
2382
|
+
{ name: 'Alice', id: 1 },
|
|
2383
|
+
{ name: 'Bob', id: 2 },
|
|
2384
|
+
{ name: 'Charlie', id: 3 },
|
|
2385
|
+
{ name: 'David', id: 4 },
|
|
2386
|
+
{ name: 'Eve', id: 5 }
|
|
2387
|
+
],
|
|
2388
|
+
{
|
|
2389
|
+
comparator: (a, b) => a.name.localeCompare(b.name)
|
|
2186
2390
|
}
|
|
2391
|
+
);
|
|
2392
|
+
|
|
2393
|
+
const ceiling = stringBst.ceiling({ name: 'Bob', id: 0 });
|
|
2394
|
+
expect(ceiling).toBeDefined();
|
|
2395
|
+
expect(ceiling?.name).toBe('Bob');
|
|
2396
|
+
});
|
|
2397
|
+
});
|
|
2398
|
+
|
|
2399
|
+
describe('Performance and correctness validation', () => {
|
|
2400
|
+
test('all range methods return keys in order', () => {
|
|
2401
|
+
const ceiling = bst.ceiling(10);
|
|
2402
|
+
const higher = bst.higher(10);
|
|
2403
|
+
const floor = bst.floor(10);
|
|
2404
|
+
const lower = bst.lower(10);
|
|
2405
|
+
|
|
2406
|
+
expect(floor).toBeLessThanOrEqual(10);
|
|
2407
|
+
expect(ceiling).toBeGreaterThanOrEqual(10);
|
|
2408
|
+
expect(higher).toBeGreaterThan(10);
|
|
2409
|
+
expect(lower).toBeLessThan(10);
|
|
2410
|
+
});
|
|
2411
|
+
|
|
2412
|
+
test('range query iteration with ceiling/higher', () => {
|
|
2413
|
+
const results: number[] = [];
|
|
2414
|
+
let key = bst.ceiling(5);
|
|
2415
|
+
let count = 0;
|
|
2416
|
+
while (key && key <= 15 && count < 20) {
|
|
2417
|
+
results.push(key);
|
|
2418
|
+
key = bst.higher(key);
|
|
2419
|
+
count++;
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
// Should iterate through keys 5, 6, 7, ..., 15
|
|
2423
|
+
expect(results.length).toBeGreaterThan(0);
|
|
2424
|
+
expect(results[0]).toBeGreaterThanOrEqual(5);
|
|
2425
|
+
expect(results[results.length - 1]).toBeLessThanOrEqual(15);
|
|
2426
|
+
// Verify ascending order
|
|
2427
|
+
for (let i = 1; i < results.length; i++) {
|
|
2428
|
+
expect(results[i]).toBeGreaterThan(results[i - 1]);
|
|
2429
|
+
}
|
|
2430
|
+
});
|
|
2431
|
+
|
|
2432
|
+
test('range query iteration with floor/lower', () => {
|
|
2433
|
+
const results: number[] = [];
|
|
2434
|
+
let key = bst.floor(15);
|
|
2435
|
+
let count = 0;
|
|
2436
|
+
while (key && key >= 5 && count < 20) {
|
|
2437
|
+
results.push(key);
|
|
2438
|
+
key = bst.lower(key);
|
|
2439
|
+
count++;
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
// Should iterate through keys 15, 14, 13, ..., 5
|
|
2443
|
+
expect(results.length).toBeGreaterThan(0);
|
|
2444
|
+
expect(results[0]).toBeLessThanOrEqual(15);
|
|
2445
|
+
expect(results[results.length - 1]).toBeGreaterThanOrEqual(5);
|
|
2446
|
+
// Verify descending order
|
|
2447
|
+
for (let i = 1; i < results.length; i++) {
|
|
2448
|
+
expect(results[i]).toBeLessThan(results[i - 1]);
|
|
2449
|
+
}
|
|
2450
|
+
});
|
|
2451
|
+
});
|
|
2452
|
+
|
|
2453
|
+
describe('Boundary value testing', () => {
|
|
2454
|
+
test('boundary: ceiling at min value', () => {
|
|
2455
|
+
const result = bst.ceiling(1);
|
|
2456
|
+
expect(result).toBe(1);
|
|
2457
|
+
});
|
|
2458
|
+
|
|
2459
|
+
test('boundary: floor at max value', () => {
|
|
2460
|
+
const result = bst.floor(20);
|
|
2461
|
+
expect(result).toBe(20);
|
|
2462
|
+
});
|
|
2463
|
+
|
|
2464
|
+
test('boundary: higher at second-last value', () => {
|
|
2465
|
+
const result = bst.higher(19);
|
|
2466
|
+
expect(result).toBe(20);
|
|
2467
|
+
});
|
|
2468
|
+
|
|
2469
|
+
test('boundary: lower at second value', () => {
|
|
2470
|
+
const result = bst.lower(3);
|
|
2471
|
+
expect(result).toBe(1);
|
|
2472
|
+
});
|
|
2473
|
+
|
|
2474
|
+
test('boundary: ceiling slightly below min', () => {
|
|
2475
|
+
const result = bst.ceiling(0);
|
|
2476
|
+
expect(result).toBe(1);
|
|
2477
|
+
});
|
|
2478
|
+
|
|
2479
|
+
test('boundary: floor slightly above max', () => {
|
|
2480
|
+
const result = bst.floor(21);
|
|
2481
|
+
expect(result).toBe(20);
|
|
2482
|
+
});
|
|
2483
|
+
|
|
2484
|
+
test('boundary: higher at max (should be undefined)', () => {
|
|
2485
|
+
const result = bst.higher(20);
|
|
2486
|
+
expect(result).toBeUndefined();
|
|
2487
|
+
});
|
|
2488
|
+
|
|
2489
|
+
test('boundary: lower at min (should be undefined)', () => {
|
|
2490
|
+
const result = bst.lower(1);
|
|
2491
|
+
expect(result).toBeUndefined();
|
|
2492
|
+
});
|
|
2493
|
+
});
|
|
2494
|
+
});
|
|
2495
|
+
|
|
2496
|
+
describe('BST Comparator Tests', () => {
|
|
2497
|
+
describe('Default Comparator with Primitive Types', () => {
|
|
2498
|
+
let bst: BST<number>;
|
|
2499
|
+
|
|
2500
|
+
beforeEach(() => {
|
|
2501
|
+
bst = new BST<number>();
|
|
2502
|
+
});
|
|
2503
|
+
|
|
2504
|
+
it('should compare two numbers correctly - a > b', () => {
|
|
2505
|
+
const result = bst['_compare'](5, 3);
|
|
2506
|
+
expect(result).toBe(1);
|
|
2507
|
+
});
|
|
2508
|
+
|
|
2509
|
+
it('should compare two numbers correctly - a < b', () => {
|
|
2510
|
+
const result = bst['_compare'](2, 8);
|
|
2511
|
+
expect(result).toBe(-1);
|
|
2512
|
+
});
|
|
2513
|
+
|
|
2514
|
+
it('should compare two numbers correctly - a === b', () => {
|
|
2515
|
+
const result = bst['_compare'](5, 5);
|
|
2516
|
+
expect(result).toBe(0);
|
|
2517
|
+
});
|
|
2518
|
+
|
|
2519
|
+
it('should compare negative numbers correctly', () => {
|
|
2520
|
+
const result1 = bst['_compare'](-5, -3);
|
|
2521
|
+
const result2 = bst['_compare'](-10, 0);
|
|
2522
|
+
expect(result1).toBe(-1);
|
|
2523
|
+
expect(result2).toBe(-1);
|
|
2524
|
+
});
|
|
2525
|
+
|
|
2526
|
+
it('should compare zero correctly', () => {
|
|
2527
|
+
const result1 = bst['_compare'](0, 5);
|
|
2528
|
+
const result2 = bst['_compare'](0, 0);
|
|
2529
|
+
const result3 = bst['_compare'](-5, 0);
|
|
2530
|
+
expect(result1).toBe(-1);
|
|
2531
|
+
expect(result2).toBe(0);
|
|
2532
|
+
expect(result3).toBe(-1);
|
|
2533
|
+
});
|
|
2534
|
+
|
|
2535
|
+
it('should compare decimal numbers correctly', () => {
|
|
2536
|
+
const result1 = bst['_compare'](3.14, 3.15);
|
|
2537
|
+
const result2 = bst['_compare'](2.5, 2.5);
|
|
2538
|
+
expect(result1).toBe(-1);
|
|
2539
|
+
expect(result2).toBe(0);
|
|
2540
|
+
});
|
|
2541
|
+
|
|
2542
|
+
it('should compare very large numbers correctly', () => {
|
|
2543
|
+
const result1 = bst['_compare'](Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER - 1);
|
|
2544
|
+
const result2 = bst['_compare'](1e10, 1e9);
|
|
2545
|
+
expect(result1).toBe(1);
|
|
2546
|
+
expect(result2).toBe(1);
|
|
2547
|
+
});
|
|
2548
|
+
});
|
|
2549
|
+
|
|
2550
|
+
describe('Default Comparator with String Types', () => {
|
|
2551
|
+
let bst: BST<string>;
|
|
2552
|
+
|
|
2553
|
+
beforeEach(() => {
|
|
2554
|
+
bst = new BST<string>();
|
|
2555
|
+
});
|
|
2556
|
+
|
|
2557
|
+
it('should compare strings alphabetically - a > b', () => {
|
|
2558
|
+
const result = bst['_compare']('zebra', 'apple');
|
|
2559
|
+
expect(result).toBe(1);
|
|
2560
|
+
});
|
|
2561
|
+
|
|
2562
|
+
it('should compare strings alphabetically - a < b', () => {
|
|
2563
|
+
const result = bst['_compare']('apple', 'zebra');
|
|
2564
|
+
expect(result).toBe(-1);
|
|
2565
|
+
});
|
|
2566
|
+
|
|
2567
|
+
it('should compare identical strings', () => {
|
|
2568
|
+
const result = bst['_compare']('hello', 'hello');
|
|
2569
|
+
expect(result).toBe(0);
|
|
2570
|
+
});
|
|
2571
|
+
|
|
2572
|
+
it('should compare string prefixes correctly', () => {
|
|
2573
|
+
const result1 = bst['_compare']('app', 'apple');
|
|
2574
|
+
const result2 = bst['_compare']('apple', 'app');
|
|
2575
|
+
expect(result1).toBe(-1);
|
|
2576
|
+
expect(result2).toBe(1);
|
|
2577
|
+
});
|
|
2578
|
+
|
|
2579
|
+
it('should compare case-sensitive strings', () => {
|
|
2580
|
+
const result1 = bst['_compare']('Apple', 'apple');
|
|
2581
|
+
const result2 = bst['_compare']('apple', 'Apple');
|
|
2582
|
+
expect(result1).toBe(-1);
|
|
2583
|
+
expect(result2).toBe(1);
|
|
2584
|
+
});
|
|
2585
|
+
|
|
2586
|
+
it('should compare empty strings', () => {
|
|
2587
|
+
const result1 = bst['_compare']('', '');
|
|
2588
|
+
const result2 = bst['_compare']('', 'a');
|
|
2589
|
+
const result3 = bst['_compare']('a', '');
|
|
2590
|
+
expect(result1).toBe(0);
|
|
2591
|
+
expect(result2).toBe(-1);
|
|
2592
|
+
expect(result3).toBe(1);
|
|
2593
|
+
});
|
|
2594
|
+
|
|
2595
|
+
it('should compare special characters in strings', () => {
|
|
2596
|
+
const result1 = bst['_compare']('!', 'a');
|
|
2597
|
+
const result2 = bst['_compare']('a', '1');
|
|
2598
|
+
expect(result1).toBe(-1);
|
|
2599
|
+
expect(result2).toBe(1);
|
|
2600
|
+
});
|
|
2601
|
+
});
|
|
2602
|
+
|
|
2603
|
+
describe('Custom Comparator', () => {
|
|
2604
|
+
interface Person {
|
|
2605
|
+
name: string;
|
|
2606
|
+
age: number;
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
it('should accept custom comparator for objects - compare by age', () => {
|
|
2610
|
+
const comparator = (a: Person, b: Person): number => {
|
|
2611
|
+
if (a.age > b.age) return 1;
|
|
2612
|
+
if (a.age < b.age) return -1;
|
|
2613
|
+
return 0;
|
|
2614
|
+
};
|
|
2615
|
+
|
|
2616
|
+
const bst = new BST<Person>([], { comparator });
|
|
2617
|
+
|
|
2618
|
+
const p1 = { name: 'Alice', age: 30 };
|
|
2619
|
+
const p2 = { name: 'Bob', age: 25 };
|
|
2620
|
+
const p3 = { name: 'Charlie', age: 30 };
|
|
2621
|
+
|
|
2622
|
+
expect(bst['_compare'](p1, p2)).toBe(1);
|
|
2623
|
+
expect(bst['_compare'](p2, p1)).toBe(-1);
|
|
2624
|
+
expect(bst['_compare'](p1, p3)).toBe(0);
|
|
2625
|
+
});
|
|
2626
|
+
|
|
2627
|
+
it('should accept custom comparator - compare by name length', () => {
|
|
2628
|
+
const comparator = (a: string, b: string): number => {
|
|
2629
|
+
if (a.length > b.length) return 1;
|
|
2630
|
+
if (a.length < b.length) return -1;
|
|
2631
|
+
return 0;
|
|
2632
|
+
};
|
|
2633
|
+
|
|
2634
|
+
const bst = new BST<string>([], { comparator });
|
|
2635
|
+
|
|
2636
|
+
expect(bst['_compare']('hello', 'hi')).toBe(1);
|
|
2637
|
+
expect(bst['_compare']('hi', 'hello')).toBe(-1);
|
|
2638
|
+
expect(bst['_compare']('abc', 'def')).toBe(0);
|
|
2639
|
+
});
|
|
2640
|
+
|
|
2641
|
+
it('should accept custom comparator - reverse order', () => {
|
|
2642
|
+
const comparator = (a: number, b: number): number => {
|
|
2643
|
+
if (a < b) return 1;
|
|
2644
|
+
if (a > b) return -1;
|
|
2645
|
+
return 0;
|
|
2646
|
+
};
|
|
2647
|
+
|
|
2648
|
+
const bst = new BST<number>([], { comparator });
|
|
2649
|
+
|
|
2650
|
+
expect(bst['_compare'](5, 3)).toBe(-1);
|
|
2651
|
+
expect(bst['_compare'](2, 8)).toBe(1);
|
|
2652
|
+
expect(bst['_compare'](5, 5)).toBe(0);
|
|
2653
|
+
});
|
|
2654
|
+
|
|
2655
|
+
it('should throw error when comparing object types without custom comparator', () => {
|
|
2656
|
+
const bst = new BST<{ value: number }>();
|
|
2657
|
+
|
|
2658
|
+
expect(() => {
|
|
2659
|
+
bst['_compare']({ value: 1 }, { value: 2 });
|
|
2660
|
+
}).toThrow("When comparing object type keys, a custom comparator must be provided in the constructor's options!");
|
|
2661
|
+
});
|
|
2662
|
+
});
|
|
2663
|
+
|
|
2664
|
+
describe('Comparator Usage in BST Operations', () => {
|
|
2665
|
+
let bst: BST<number>;
|
|
2666
|
+
|
|
2667
|
+
beforeEach(() => {
|
|
2668
|
+
bst = new BST<number>();
|
|
2669
|
+
});
|
|
2670
|
+
|
|
2671
|
+
it('should correctly insert elements based on comparator', () => {
|
|
2672
|
+
bst.add(10);
|
|
2673
|
+
bst.add(5);
|
|
2674
|
+
bst.add(15);
|
|
2675
|
+
bst.add(3);
|
|
2676
|
+
bst.add(7);
|
|
2677
|
+
|
|
2678
|
+
expect(bst.has(10)).toBe(true);
|
|
2679
|
+
expect(bst.has(5)).toBe(true);
|
|
2680
|
+
expect(bst.has(15)).toBe(true);
|
|
2681
|
+
expect(bst.has(3)).toBe(true);
|
|
2682
|
+
expect(bst.has(7)).toBe(true);
|
|
2683
|
+
expect(bst.size).toBe(5);
|
|
2684
|
+
});
|
|
2685
|
+
|
|
2686
|
+
it('should maintain BST property with in-order traversal', () => {
|
|
2687
|
+
bst.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
2688
|
+
|
|
2689
|
+
const inOrder: (number | undefined)[] = bst.dfs(node => node?.key);
|
|
2690
|
+
|
|
2691
|
+
for (let i = 1; i < inOrder.length; i++) {
|
|
2692
|
+
expect(inOrder[i]! >= inOrder[i - 1]!).toBe(true);
|
|
2693
|
+
}
|
|
2694
|
+
});
|
|
2695
|
+
|
|
2696
|
+
it('should correctly find min and max using comparator', () => {
|
|
2697
|
+
bst.addMany([15, 10, 20, 8, 12, 18, 25]);
|
|
2698
|
+
|
|
2699
|
+
const arr = Array.from(bst.keys());
|
|
2700
|
+
const min = Math.min(...arr);
|
|
2701
|
+
const max = Math.max(...arr);
|
|
2702
|
+
|
|
2703
|
+
expect(bst.has(min)).toBe(true);
|
|
2704
|
+
expect(bst.has(max)).toBe(true);
|
|
2705
|
+
});
|
|
2706
|
+
|
|
2707
|
+
it('should correctly delete elements maintaining BST property', () => {
|
|
2708
|
+
bst.addMany([10, 5, 15, 3, 7, 12, 18]);
|
|
2709
|
+
bst.delete(10);
|
|
2710
|
+
|
|
2711
|
+
expect(bst.has(10)).toBe(false);
|
|
2712
|
+
const inOrder: (number | undefined)[] = bst.dfs(node => node?.key);
|
|
2713
|
+
|
|
2714
|
+
for (let i = 1; i < inOrder.length; i++) {
|
|
2715
|
+
expect(inOrder[i]! >= inOrder[i - 1]!).toBe(true);
|
|
2187
2716
|
}
|
|
2188
2717
|
});
|
|
2189
2718
|
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
const randomKeys = generateRandomArray(50, 0, 100);
|
|
2193
|
-
const uniqueKeys = [...new Set(randomKeys)];
|
|
2194
|
-
uniqueKeys.forEach(k => fuzzTree.add(k));
|
|
2719
|
+
it('should correctly search using comparator', () => {
|
|
2720
|
+
bst.addMany([20, 10, 30, 5, 15, 25, 35]);
|
|
2195
2721
|
|
|
2196
|
-
|
|
2197
|
-
|
|
2722
|
+
const result = bst.search(15);
|
|
2723
|
+
expect(result.length).toBeGreaterThan(0);
|
|
2724
|
+
expect(result[0]).toBe(15);
|
|
2725
|
+
});
|
|
2726
|
+
});
|
|
2727
|
+
|
|
2728
|
+
describe('Comparator Edge Cases', () => {
|
|
2729
|
+
describe('with numbers', () => {
|
|
2730
|
+
let bst: BST<number>;
|
|
2198
2731
|
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2732
|
+
beforeEach(() => {
|
|
2733
|
+
bst = new BST<number>();
|
|
2734
|
+
});
|
|
2735
|
+
|
|
2736
|
+
it('should handle Infinity correctly', () => {
|
|
2737
|
+
const result1 = bst['_compare'](Infinity, 100);
|
|
2738
|
+
const result2 = bst['_compare'](100, Infinity);
|
|
2739
|
+
expect(result1).toBe(1);
|
|
2740
|
+
expect(result2).toBe(-1);
|
|
2741
|
+
});
|
|
2742
|
+
|
|
2743
|
+
it('should handle -Infinity correctly', () => {
|
|
2744
|
+
const result1 = bst['_compare'](-Infinity, -100);
|
|
2745
|
+
const result2 = bst['_compare'](-100, -Infinity);
|
|
2746
|
+
expect(result1).toBe(-1);
|
|
2747
|
+
expect(result2).toBe(1);
|
|
2748
|
+
});
|
|
2749
|
+
|
|
2750
|
+
it('should handle NaN correctly', () => {
|
|
2751
|
+
const result1 = bst['_compare'](NaN, 5);
|
|
2752
|
+
const result2 = bst['_compare'](5, NaN);
|
|
2753
|
+
expect(result1).toBe(0);
|
|
2754
|
+
expect(result2).toBe(0);
|
|
2755
|
+
});
|
|
2756
|
+
});
|
|
2757
|
+
|
|
2758
|
+
describe('with strings', () => {
|
|
2759
|
+
let bst: BST<string>;
|
|
2760
|
+
|
|
2761
|
+
beforeEach(() => {
|
|
2762
|
+
bst = new BST<string>();
|
|
2763
|
+
});
|
|
2764
|
+
|
|
2765
|
+
it('should handle Unicode characters', () => {
|
|
2766
|
+
const result1 = bst['_compare']('😀', 'abc');
|
|
2767
|
+
const result2 = bst['_compare']('中文', 'English');
|
|
2768
|
+
expect(typeof result1).toBe('number');
|
|
2769
|
+
expect(typeof result2).toBe('number');
|
|
2770
|
+
});
|
|
2771
|
+
|
|
2772
|
+
it('should handle whitespace strings', () => {
|
|
2773
|
+
const result1 = bst['_compare'](' ', ' ');
|
|
2774
|
+
const result2 = bst['_compare']('\n', '\t');
|
|
2775
|
+
expect(typeof result1).toBe('number');
|
|
2776
|
+
expect(typeof result2).toBe('number');
|
|
2777
|
+
});
|
|
2778
|
+
|
|
2779
|
+
it('should maintain consistency with multiple comparisons', () => {
|
|
2780
|
+
const a = 'apple';
|
|
2781
|
+
const b = 'banana';
|
|
2782
|
+
const c = 'cherry';
|
|
2783
|
+
|
|
2784
|
+
const ab = bst['_compare'](a, b);
|
|
2785
|
+
const bc = bst['_compare'](b, c);
|
|
2786
|
+
const ac = bst['_compare'](a, c);
|
|
2787
|
+
|
|
2788
|
+
if (ab < 0 && bc < 0) {
|
|
2789
|
+
expect(ac).toBeLessThan(0);
|
|
2204
2790
|
}
|
|
2205
2791
|
});
|
|
2206
2792
|
});
|
|
2793
|
+
});
|
|
2794
|
+
|
|
2795
|
+
describe('Comparator with BST Bound Operations', () => {
|
|
2796
|
+
let bst: BST<number>;
|
|
2797
|
+
|
|
2798
|
+
beforeEach(() => {
|
|
2799
|
+
bst = new BST<number>();
|
|
2800
|
+
bst.addMany([10, 5, 15, 3, 7, 12, 18, 1, 4, 6, 8, 11, 13, 16, 20]);
|
|
2801
|
+
});
|
|
2802
|
+
|
|
2803
|
+
it('should find ceiling using comparator', () => {
|
|
2804
|
+
const result = bst.ceiling(7);
|
|
2805
|
+
expect(result).toBe(7);
|
|
2806
|
+
|
|
2807
|
+
const result2 = bst.ceiling(7.5);
|
|
2808
|
+
expect(result2).toBeGreaterThanOrEqual(7.5);
|
|
2809
|
+
});
|
|
2810
|
+
|
|
2811
|
+
it('should find floor using comparator', () => {
|
|
2812
|
+
const result = bst.floor(7);
|
|
2813
|
+
expect(result).toBe(7);
|
|
2814
|
+
|
|
2815
|
+
const result2 = bst.floor(7.5);
|
|
2816
|
+
expect(result2!).toBeLessThanOrEqual(7.5);
|
|
2817
|
+
});
|
|
2818
|
+
|
|
2819
|
+
it('should find higher using comparator', () => {
|
|
2820
|
+
const result = bst.higher(7);
|
|
2821
|
+
expect(result).toBeGreaterThan(7);
|
|
2822
|
+
});
|
|
2823
|
+
|
|
2824
|
+
it('should find lower using comparator', () => {
|
|
2825
|
+
const result = bst.lower(7);
|
|
2826
|
+
expect(result).toBeLessThan(7);
|
|
2827
|
+
});
|
|
2828
|
+
});
|
|
2829
|
+
|
|
2830
|
+
describe('Comparator Consistency', () => {
|
|
2831
|
+
let bst: BST<number>;
|
|
2832
|
+
|
|
2833
|
+
beforeEach(() => {
|
|
2834
|
+
bst = new BST<number>();
|
|
2835
|
+
});
|
|
2207
2836
|
|
|
2208
|
-
|
|
2209
|
-
const
|
|
2210
|
-
const randomKeys = generateRandomArray(50, 0, 100);
|
|
2211
|
-
[...new Set(randomKeys)].forEach(k => fuzzTree.add(k));
|
|
2837
|
+
it('should satisfy reflexivity: a compared with itself returns 0', () => {
|
|
2838
|
+
const values = [0, 1, -1, 100, -100, 0.5];
|
|
2212
2839
|
|
|
2213
|
-
const
|
|
2840
|
+
for (const val of values) {
|
|
2841
|
+
expect(bst['_compare'](val, val)).toBe(0);
|
|
2842
|
+
}
|
|
2843
|
+
});
|
|
2214
2844
|
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2845
|
+
it('should satisfy antisymmetry: if a > b then b < a', () => {
|
|
2846
|
+
const pairs = [
|
|
2847
|
+
[5, 3],
|
|
2848
|
+
[10, 2],
|
|
2849
|
+
[100, 1]
|
|
2850
|
+
];
|
|
2851
|
+
|
|
2852
|
+
for (const [a, b] of pairs) {
|
|
2853
|
+
const cmp1 = bst['_compare'](a, b);
|
|
2854
|
+
const cmp2 = bst['_compare'](b, a);
|
|
2855
|
+
expect(Math.sign(cmp1)).toBe(-Math.sign(cmp2));
|
|
2856
|
+
}
|
|
2857
|
+
});
|
|
2858
|
+
|
|
2859
|
+
it('should satisfy transitivity: if a < b and b < c then a < c', () => {
|
|
2860
|
+
const a = 1;
|
|
2861
|
+
const b = 5;
|
|
2862
|
+
const c = 10;
|
|
2220
2863
|
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
expect(recEntry?.key).toBe(iterEntry?.key);
|
|
2864
|
+
const cmpAB = bst['_compare'](a, b);
|
|
2865
|
+
const cmpBC = bst['_compare'](b, c);
|
|
2866
|
+
const cmpAC = bst['_compare'](a, c);
|
|
2225
2867
|
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2868
|
+
if (cmpAB < 0 && cmpBC < 0) {
|
|
2869
|
+
expect(cmpAC).toBeLessThan(0);
|
|
2870
|
+
}
|
|
2871
|
+
});
|
|
2872
|
+
});
|
|
2873
|
+
|
|
2874
|
+
describe('Custom Comparator with BST Operations', () => {
|
|
2875
|
+
interface Student {
|
|
2876
|
+
name: string;
|
|
2877
|
+
grade: number;
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
it('should work with custom comparator for complex operations', () => {
|
|
2881
|
+
const comparator = (a: Student, b: Student): number => {
|
|
2882
|
+
if (a.grade !== b.grade) {
|
|
2883
|
+
return a.grade > b.grade ? 1 : -1;
|
|
2884
|
+
}
|
|
2885
|
+
return a.name.localeCompare(b.name);
|
|
2886
|
+
};
|
|
2887
|
+
|
|
2888
|
+
const bst = new BST<Student>([], { comparator });
|
|
2889
|
+
|
|
2890
|
+
const students: Student[] = [
|
|
2891
|
+
{ name: 'Alice', grade: 85 },
|
|
2892
|
+
{ name: 'Bob', grade: 90 },
|
|
2893
|
+
{ name: 'Charlie', grade: 85 },
|
|
2894
|
+
{ name: 'David', grade: 95 }
|
|
2895
|
+
];
|
|
2896
|
+
|
|
2897
|
+
students.forEach(s => bst.add(s));
|
|
2898
|
+
|
|
2899
|
+
expect(bst.size).toBe(4);
|
|
2900
|
+
expect(bst.has(students[0])).toBe(true);
|
|
2901
|
+
});
|
|
2902
|
+
|
|
2903
|
+
it('should handle custom comparator with add and search', () => {
|
|
2904
|
+
const comparator = (a: number[], b: number[]): number => {
|
|
2905
|
+
const sumA = a.reduce((acc, val) => acc + val, 0);
|
|
2906
|
+
const sumB = b.reduce((acc, val) => acc + val, 0);
|
|
2907
|
+
if (sumA > sumB) return 1;
|
|
2908
|
+
if (sumA < sumB) return -1;
|
|
2909
|
+
return 0;
|
|
2910
|
+
};
|
|
2911
|
+
|
|
2912
|
+
const bst = new BST<number[]>([], { comparator });
|
|
2913
|
+
|
|
2914
|
+
bst.add([1, 2, 3]);
|
|
2915
|
+
bst.add([2, 2, 2]);
|
|
2916
|
+
bst.add([1, 1, 1]);
|
|
2917
|
+
|
|
2918
|
+
expect(bst.size).toBeGreaterThan(0);
|
|
2919
|
+
});
|
|
2920
|
+
});
|
|
2921
|
+
|
|
2922
|
+
describe('Composite Key Comparator Tests', () => {
|
|
2923
|
+
interface CompositeKey {
|
|
2924
|
+
departmentId: number;
|
|
2925
|
+
employeeId: number;
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2928
|
+
it('should compare composite keys by multiple fields - primary then secondary', () => {
|
|
2929
|
+
const comparator = (a: CompositeKey, b: CompositeKey): number => {
|
|
2930
|
+
if (a.departmentId !== b.departmentId) {
|
|
2931
|
+
return a.departmentId > b.departmentId ? 1 : -1;
|
|
2932
|
+
}
|
|
2933
|
+
if (a.employeeId !== b.employeeId) {
|
|
2934
|
+
return a.employeeId > b.employeeId ? 1 : -1;
|
|
2935
|
+
}
|
|
2936
|
+
return 0;
|
|
2937
|
+
};
|
|
2938
|
+
|
|
2939
|
+
const bst = new BST<CompositeKey>([], { comparator });
|
|
2940
|
+
|
|
2941
|
+
const key1 = { departmentId: 1, employeeId: 101 };
|
|
2942
|
+
const key2 = { departmentId: 1, employeeId: 102 };
|
|
2943
|
+
const key3 = { departmentId: 2, employeeId: 101 };
|
|
2944
|
+
|
|
2945
|
+
expect(bst['_compare'](key1, key2)).toBe(-1);
|
|
2946
|
+
expect(bst['_compare'](key2, key3)).toBe(-1);
|
|
2947
|
+
expect(bst['_compare'](key1, key1)).toBe(0);
|
|
2948
|
+
});
|
|
2949
|
+
|
|
2950
|
+
it('should maintain BST property with composite keys', () => {
|
|
2951
|
+
const comparator = (a: CompositeKey, b: CompositeKey): number => {
|
|
2952
|
+
if (a.departmentId !== b.departmentId) {
|
|
2953
|
+
return a.departmentId > b.departmentId ? 1 : -1;
|
|
2954
|
+
}
|
|
2955
|
+
return a.employeeId > b.employeeId ? 1 : a.employeeId < b.employeeId ? -1 : 0;
|
|
2956
|
+
};
|
|
2957
|
+
|
|
2958
|
+
const bst = new BST<CompositeKey>([], { comparator });
|
|
2959
|
+
|
|
2960
|
+
const keys = [
|
|
2961
|
+
{ departmentId: 2, employeeId: 105 },
|
|
2962
|
+
{ departmentId: 1, employeeId: 101 },
|
|
2963
|
+
{ departmentId: 1, employeeId: 103 },
|
|
2964
|
+
{ departmentId: 2, employeeId: 102 },
|
|
2965
|
+
{ departmentId: 1, employeeId: 102 }
|
|
2966
|
+
];
|
|
2967
|
+
|
|
2968
|
+
keys.forEach(k => bst.add(k));
|
|
2969
|
+
|
|
2970
|
+
expect(bst.size).toBe(5);
|
|
2971
|
+
const inOrder = bst.dfs(node => node?.key);
|
|
2972
|
+
|
|
2973
|
+
for (let i = 1; i < inOrder.length; i++) {
|
|
2974
|
+
const cmp = comparator(inOrder[i - 1]!, inOrder[i]!);
|
|
2975
|
+
expect(cmp).toBeLessThanOrEqual(0);
|
|
2976
|
+
}
|
|
2977
|
+
});
|
|
2978
|
+
|
|
2979
|
+
it('should search with composite keys', () => {
|
|
2980
|
+
const comparator = (a: CompositeKey, b: CompositeKey): number => {
|
|
2981
|
+
if (a.departmentId !== b.departmentId) {
|
|
2982
|
+
return a.departmentId > b.departmentId ? 1 : -1;
|
|
2983
|
+
}
|
|
2984
|
+
return a.employeeId > b.employeeId ? 1 : a.employeeId < b.employeeId ? -1 : 0;
|
|
2985
|
+
};
|
|
2986
|
+
|
|
2987
|
+
const bst = new BST<CompositeKey>([], { comparator });
|
|
2988
|
+
|
|
2989
|
+
const keys = [
|
|
2990
|
+
{ departmentId: 1, employeeId: 101 },
|
|
2991
|
+
{ departmentId: 1, employeeId: 103 },
|
|
2992
|
+
{ departmentId: 2, employeeId: 102 }
|
|
2993
|
+
];
|
|
2994
|
+
|
|
2995
|
+
keys.forEach(k => bst.add(k));
|
|
2996
|
+
|
|
2997
|
+
const searchKey = keys[1];
|
|
2998
|
+
const result = bst.search(searchKey);
|
|
2999
|
+
|
|
3000
|
+
expect(result.length).toBeGreaterThan(0);
|
|
3001
|
+
expect(result[0]).toEqual(searchKey);
|
|
3002
|
+
});
|
|
3003
|
+
|
|
3004
|
+
it('should handle deletion with composite keys', () => {
|
|
3005
|
+
const comparator = (a: CompositeKey, b: CompositeKey): number => {
|
|
3006
|
+
if (a.departmentId !== b.departmentId) {
|
|
3007
|
+
return a.departmentId > b.departmentId ? 1 : -1;
|
|
3008
|
+
}
|
|
3009
|
+
return a.employeeId > b.employeeId ? 1 : a.employeeId < b.employeeId ? -1 : 0;
|
|
3010
|
+
};
|
|
3011
|
+
|
|
3012
|
+
const bst = new BST<CompositeKey>([], { comparator });
|
|
3013
|
+
|
|
3014
|
+
const keys = [
|
|
3015
|
+
{ departmentId: 1, employeeId: 101 },
|
|
3016
|
+
{ departmentId: 1, employeeId: 103 },
|
|
3017
|
+
{ departmentId: 2, employeeId: 102 }
|
|
3018
|
+
];
|
|
3019
|
+
|
|
3020
|
+
keys.forEach(k => bst.add(k));
|
|
3021
|
+
|
|
3022
|
+
const keyToDelete = keys[1];
|
|
3023
|
+
const deleted = bst.delete(keyToDelete);
|
|
3024
|
+
|
|
3025
|
+
expect(deleted.length).toBeGreaterThan(0);
|
|
3026
|
+
expect(bst.has(keyToDelete)).toBe(false);
|
|
3027
|
+
expect(bst.size).toBe(2);
|
|
3028
|
+
});
|
|
3029
|
+
|
|
3030
|
+
it('should find ceiling/floor with composite keys', () => {
|
|
3031
|
+
const comparator = (a: CompositeKey, b: CompositeKey): number => {
|
|
3032
|
+
if (a.departmentId !== b.departmentId) {
|
|
3033
|
+
return a.departmentId > b.departmentId ? 1 : -1;
|
|
3034
|
+
}
|
|
3035
|
+
return a.employeeId > b.employeeId ? 1 : a.employeeId < b.employeeId ? -1 : 0;
|
|
3036
|
+
};
|
|
3037
|
+
|
|
3038
|
+
const bst = new BST<CompositeKey>([], { comparator });
|
|
3039
|
+
|
|
3040
|
+
const keys = [
|
|
3041
|
+
{ departmentId: 1, employeeId: 101 },
|
|
3042
|
+
{ departmentId: 1, employeeId: 105 },
|
|
3043
|
+
{ departmentId: 2, employeeId: 102 }
|
|
3044
|
+
];
|
|
3045
|
+
|
|
3046
|
+
keys.forEach(k => bst.add(k));
|
|
3047
|
+
|
|
3048
|
+
const searchKey = { departmentId: 1, employeeId: 103 };
|
|
3049
|
+
const ceiling = bst.ceiling(searchKey);
|
|
3050
|
+
const floor = bst.floor(searchKey);
|
|
3051
|
+
|
|
3052
|
+
expect(ceiling).toBeDefined();
|
|
3053
|
+
expect(floor).toBeDefined();
|
|
3054
|
+
});
|
|
3055
|
+
});
|
|
3056
|
+
|
|
3057
|
+
describe('Key-Value Storage with Comparator', () => {
|
|
3058
|
+
interface PersonKey {
|
|
3059
|
+
id: number;
|
|
3060
|
+
country: string;
|
|
3061
|
+
}
|
|
3062
|
+
|
|
3063
|
+
interface PersonValue {
|
|
3064
|
+
name: string;
|
|
3065
|
+
email: string;
|
|
3066
|
+
age: number;
|
|
3067
|
+
}
|
|
3068
|
+
|
|
3069
|
+
it('should store and retrieve key-value pairs with composite keys', () => {
|
|
3070
|
+
const comparator = (a: PersonKey, b: PersonKey): number => {
|
|
3071
|
+
const countryCompare = a.country.localeCompare(b.country);
|
|
3072
|
+
if (countryCompare !== 0) return countryCompare;
|
|
3073
|
+
return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
|
|
3074
|
+
};
|
|
3075
|
+
|
|
3076
|
+
const bst = new BST<PersonKey, PersonValue>([], { comparator, isMapMode: true });
|
|
3077
|
+
|
|
3078
|
+
const key1: PersonKey = { id: 1, country: 'USA' };
|
|
3079
|
+
const value1: PersonValue = { name: 'Alice', email: 'alice@example.com', age: 30 };
|
|
3080
|
+
|
|
3081
|
+
const key2: PersonKey = { id: 2, country: 'Canada' };
|
|
3082
|
+
const value2: PersonValue = { name: 'Bob', email: 'bob@example.com', age: 25 };
|
|
3083
|
+
|
|
3084
|
+
bst.add([key1, value1]);
|
|
3085
|
+
bst.add([key2, value2]);
|
|
3086
|
+
|
|
3087
|
+
expect(bst.size).toBe(2);
|
|
3088
|
+
expect(bst.get(key1)).toEqual(value1);
|
|
3089
|
+
expect(bst.get(key2)).toEqual(value2);
|
|
3090
|
+
});
|
|
3091
|
+
|
|
3092
|
+
it('should update values when adding duplicate keys', () => {
|
|
3093
|
+
const comparator = (a: PersonKey, b: PersonKey): number => {
|
|
3094
|
+
const countryCompare = a.country.localeCompare(b.country);
|
|
3095
|
+
if (countryCompare !== 0) return countryCompare;
|
|
3096
|
+
return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
|
|
3097
|
+
};
|
|
3098
|
+
|
|
3099
|
+
const bst = new BST<PersonKey, PersonValue>([], { comparator, isMapMode: true });
|
|
3100
|
+
|
|
3101
|
+
const key: PersonKey = { id: 1, country: 'USA' };
|
|
3102
|
+
const value1: PersonValue = { name: 'Alice', email: 'alice@example.com', age: 30 };
|
|
3103
|
+
const value2: PersonValue = { name: 'Alice Updated', email: 'alice.new@example.com', age: 31 };
|
|
3104
|
+
|
|
3105
|
+
bst.add([key, value1]);
|
|
3106
|
+
expect(bst.get(key)).toEqual(value1);
|
|
3107
|
+
|
|
3108
|
+
bst.add([key, value2]);
|
|
3109
|
+
expect(bst.size).toBe(1);
|
|
3110
|
+
expect(bst.get(key)).toEqual(value2);
|
|
3111
|
+
});
|
|
3112
|
+
|
|
3113
|
+
it('should retrieve all entries in sorted order by key', () => {
|
|
3114
|
+
const comparator = (a: PersonKey, b: PersonKey): number => {
|
|
3115
|
+
const countryCompare = a.country.localeCompare(b.country);
|
|
3116
|
+
if (countryCompare !== 0) return countryCompare;
|
|
3117
|
+
return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
|
|
3118
|
+
};
|
|
3119
|
+
|
|
3120
|
+
const bst = new BST<PersonKey, PersonValue>([], { comparator, isMapMode: true });
|
|
3121
|
+
|
|
3122
|
+
const entries: Array<[PersonKey, PersonValue]> = [
|
|
3123
|
+
[
|
|
3124
|
+
{ id: 1, country: 'USA' },
|
|
3125
|
+
{ name: 'Alice', email: 'alice@usa.com', age: 30 }
|
|
3126
|
+
],
|
|
3127
|
+
[
|
|
3128
|
+
{ id: 2, country: 'Canada' },
|
|
3129
|
+
{ name: 'Bob', email: 'bob@ca.com', age: 25 }
|
|
3130
|
+
],
|
|
3131
|
+
[
|
|
3132
|
+
{ id: 1, country: 'Canada' },
|
|
3133
|
+
{ name: 'Charlie', email: 'charlie@ca.com', age: 28 }
|
|
3134
|
+
]
|
|
3135
|
+
];
|
|
3136
|
+
|
|
3137
|
+
entries.forEach(([key, value]) => bst.add([key, value]));
|
|
3138
|
+
|
|
3139
|
+
expect(bst.size).toBe(3);
|
|
3140
|
+
|
|
3141
|
+
const values: (PersonValue | undefined)[] = [];
|
|
3142
|
+
for (const [key] of entries) {
|
|
3143
|
+
const value = bst.get(key);
|
|
3144
|
+
if (value) values.push(value);
|
|
3145
|
+
}
|
|
3146
|
+
|
|
3147
|
+
expect(values.length).toBeGreaterThan(0);
|
|
3148
|
+
});
|
|
3149
|
+
|
|
3150
|
+
it('should delete key-value pairs correctly', () => {
|
|
3151
|
+
const comparator = (a: PersonKey, b: PersonKey): number => {
|
|
3152
|
+
const countryCompare = a.country.localeCompare(b.country);
|
|
3153
|
+
if (countryCompare !== 0) return countryCompare;
|
|
3154
|
+
return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
|
|
3155
|
+
};
|
|
3156
|
+
|
|
3157
|
+
const bst = new BST<PersonKey, PersonValue>([], { comparator, isMapMode: true });
|
|
3158
|
+
|
|
3159
|
+
const key1: PersonKey = { id: 1, country: 'USA' };
|
|
3160
|
+
const value1: PersonValue = { name: 'Alice', email: 'alice@example.com', age: 30 };
|
|
3161
|
+
|
|
3162
|
+
const key2: PersonKey = { id: 2, country: 'Canada' };
|
|
3163
|
+
const value2: PersonValue = { name: 'Bob', email: 'bob@example.com', age: 25 };
|
|
3164
|
+
|
|
3165
|
+
bst.add([key1, value1]);
|
|
3166
|
+
bst.add([key2, value2]);
|
|
3167
|
+
|
|
3168
|
+
bst.delete(key1);
|
|
3169
|
+
|
|
3170
|
+
expect(bst.has(key1)).toBe(false);
|
|
3171
|
+
expect(bst.has(key2)).toBe(true);
|
|
3172
|
+
expect(bst.size).toBe(1);
|
|
3173
|
+
expect(bst.get(key1)).toBeUndefined();
|
|
3174
|
+
});
|
|
3175
|
+
|
|
3176
|
+
it('should search and retrieve values with composite keys', () => {
|
|
3177
|
+
const comparator = (a: PersonKey, b: PersonKey): number => {
|
|
3178
|
+
const countryCompare = a.country.localeCompare(b.country);
|
|
3179
|
+
if (countryCompare !== 0) return countryCompare;
|
|
3180
|
+
return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
|
|
3181
|
+
};
|
|
3182
|
+
|
|
3183
|
+
const bst = new BST<PersonKey, PersonValue>([], { comparator, isMapMode: true });
|
|
3184
|
+
|
|
3185
|
+
const entries: Array<[PersonKey, PersonValue]> = [
|
|
3186
|
+
[
|
|
3187
|
+
{ id: 1, country: 'USA' },
|
|
3188
|
+
{ name: 'Alice', email: 'alice@usa.com', age: 30 }
|
|
3189
|
+
],
|
|
3190
|
+
[
|
|
3191
|
+
{ id: 2, country: 'USA' },
|
|
3192
|
+
{ name: 'Amy', email: 'amy@usa.com', age: 28 }
|
|
3193
|
+
],
|
|
3194
|
+
[
|
|
3195
|
+
{ id: 1, country: 'Canada' },
|
|
3196
|
+
{ name: 'Bob', email: 'bob@ca.com', age: 25 }
|
|
3197
|
+
]
|
|
3198
|
+
];
|
|
3199
|
+
|
|
3200
|
+
entries.forEach(([key, value]) => bst.add([key, value]));
|
|
3201
|
+
|
|
3202
|
+
const searchKey: PersonKey = entries[1][0];
|
|
3203
|
+
const value = bst.get(searchKey);
|
|
3204
|
+
|
|
3205
|
+
expect(value).toEqual({ name: 'Amy', email: 'amy@usa.com', age: 28 });
|
|
3206
|
+
});
|
|
3207
|
+
|
|
3208
|
+
it('should map over key-value pairs maintaining comparator order', () => {
|
|
3209
|
+
const comparator = (a: PersonKey, b: PersonKey): number => {
|
|
3210
|
+
const countryCompare = a.country.localeCompare(b.country);
|
|
3211
|
+
if (countryCompare !== 0) return countryCompare;
|
|
3212
|
+
return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
|
|
3213
|
+
};
|
|
3214
|
+
|
|
3215
|
+
const bst = new BST<PersonKey, PersonValue>([], { comparator, isMapMode: true });
|
|
3216
|
+
|
|
3217
|
+
const entries: Array<[PersonKey, PersonValue]> = [
|
|
3218
|
+
[
|
|
3219
|
+
{ id: 1, country: 'USA' },
|
|
3220
|
+
{ name: 'Alice', email: 'alice@usa.com', age: 30 }
|
|
3221
|
+
],
|
|
3222
|
+
[
|
|
3223
|
+
{ id: 2, country: 'Canada' },
|
|
3224
|
+
{ name: 'Bob', email: 'bob@ca.com', age: 25 }
|
|
3225
|
+
]
|
|
3226
|
+
];
|
|
3227
|
+
|
|
3228
|
+
entries.forEach(([key, value]) => bst.add([key, value]));
|
|
3229
|
+
|
|
3230
|
+
const mapped = bst.map((value, key) => [key, value?.name], { comparator, isMapMode: false });
|
|
3231
|
+
|
|
3232
|
+
expect(mapped.size).toBe(2);
|
|
3233
|
+
const names = Array.from(mapped.values());
|
|
3234
|
+
expect(names).toContain('Alice');
|
|
3235
|
+
expect(names).toContain('Bob');
|
|
3236
|
+
});
|
|
3237
|
+
|
|
3238
|
+
it('should perform range search on key-value pairs', () => {
|
|
3239
|
+
const comparator = (a: number, b: number): number => {
|
|
3240
|
+
return a > b ? 1 : a < b ? -1 : 0;
|
|
3241
|
+
};
|
|
3242
|
+
|
|
3243
|
+
const bst = new BST<number, string>([], { comparator, isMapMode: true });
|
|
3244
|
+
|
|
3245
|
+
for (let i = 1; i <= 10; i++) {
|
|
3246
|
+
bst.add([i, `value-${i}`]);
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3249
|
+
const result = bst.rangeSearch([3, 7]);
|
|
3250
|
+
|
|
3251
|
+
expect(result.length).toBeGreaterThan(0);
|
|
3252
|
+
result.forEach(key => {
|
|
3253
|
+
expect(key).toBeGreaterThanOrEqual(3);
|
|
3254
|
+
expect(key).toBeLessThanOrEqual(7);
|
|
2230
3255
|
});
|
|
2231
3256
|
});
|
|
3257
|
+
|
|
3258
|
+
it('should maintain sorted iteration over key-value pairs', () => {
|
|
3259
|
+
const comparator = (a: { priority: number; id: number }, b: { priority: number; id: number }): number => {
|
|
3260
|
+
if (a.priority !== b.priority) {
|
|
3261
|
+
return a.priority > b.priority ? 1 : -1;
|
|
3262
|
+
}
|
|
3263
|
+
return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
|
|
3264
|
+
};
|
|
3265
|
+
|
|
3266
|
+
type TaskKey = { priority: number; id: number };
|
|
3267
|
+
type TaskValue = { title: string; completed: boolean };
|
|
3268
|
+
|
|
3269
|
+
const bst = new BST<TaskKey, TaskValue>([], { comparator, isMapMode: true });
|
|
3270
|
+
|
|
3271
|
+
const tasks: Array<[TaskKey, TaskValue]> = [
|
|
3272
|
+
[
|
|
3273
|
+
{ priority: 2, id: 1 },
|
|
3274
|
+
{ title: 'Task 1', completed: false }
|
|
3275
|
+
],
|
|
3276
|
+
[
|
|
3277
|
+
{ priority: 1, id: 2 },
|
|
3278
|
+
{ title: 'Task 2', completed: false }
|
|
3279
|
+
],
|
|
3280
|
+
[
|
|
3281
|
+
{ priority: 3, id: 3 },
|
|
3282
|
+
{ title: 'Task 3', completed: true }
|
|
3283
|
+
],
|
|
3284
|
+
[
|
|
3285
|
+
{ priority: 1, id: 1 },
|
|
3286
|
+
{ title: 'Task 4', completed: false }
|
|
3287
|
+
]
|
|
3288
|
+
];
|
|
3289
|
+
|
|
3290
|
+
tasks.forEach(([key, value]) => bst.add([key, value]));
|
|
3291
|
+
|
|
3292
|
+
expect(bst.size).toBe(4);
|
|
3293
|
+
|
|
3294
|
+
const inOrder = bst.dfs(node => node?.key.priority);
|
|
3295
|
+
for (let i = 1; i < inOrder.length; i++) {
|
|
3296
|
+
expect(inOrder[i]! >= inOrder[i - 1]!).toBe(true);
|
|
3297
|
+
}
|
|
3298
|
+
});
|
|
2232
3299
|
});
|
|
2233
3300
|
});
|
|
2234
3301
|
|
|
@@ -2369,7 +3436,7 @@ describe('classic use', () => {
|
|
|
2369
3436
|
return findFirstCommon(path1, path2);
|
|
2370
3437
|
};
|
|
2371
3438
|
|
|
2372
|
-
function findFirstCommon(arr1: number[], arr2: number[]): number | undefined {
|
|
3439
|
+
function findFirstCommon(arr1: (number | undefined)[], arr2: (number | undefined)[]): number | undefined {
|
|
2373
3440
|
for (const num of arr1) {
|
|
2374
3441
|
if (arr2.indexOf(num) !== -1) {
|
|
2375
3442
|
return num;
|