|
1 | 1 | import random
|
| 2 | +import math |
2 | 3 | from collections import deque as Queue
|
3 | 4 | from pydatastructs.utils import TreeNode, CartesianTreeNode, RedBlackTreeNode
|
4 | 5 | from pydatastructs.miscellaneous_data_structures import Stack
|
|
17 | 18 | 'CartesianTree',
|
18 | 19 | 'Treap',
|
19 | 20 | 'SplayTree',
|
20 |
| - 'RedBlackTree' |
| 21 | + 'RedBlackTree', |
| 22 | + 'FusionTree', |
21 | 23 | ]
|
22 | 24 |
|
23 | 25 | class BinaryTree(object):
|
@@ -1887,3 +1889,154 @@ def get_sum(self, left_index, right_index):
|
1887 | 1889 | self.get_prefix_sum(left_index - 1)
|
1888 | 1890 | else:
|
1889 | 1891 | return self.get_prefix_sum(right_index)
|
| 1892 | + |
| 1893 | +class FusionTree(object): |
| 1894 | + """ |
| 1895 | + Implements a Fusion Tree, a multi-way search tree optimized for integer keys. |
| 1896 | +
|
| 1897 | + Parameters |
| 1898 | + ========== |
| 1899 | +
|
| 1900 | + key: int |
| 1901 | + The integer key to insert. |
| 1902 | + root_data: Any |
| 1903 | + Optional data to store with the key. |
| 1904 | + backend: pydatastructs.Backend |
| 1905 | + The backend to be used. Available backends: Python and C++ |
| 1906 | + Optional, by default, the Python backend is used. For faster execution, use the C++ backend. |
| 1907 | + word_size: int |
| 1908 | + The size of the integer keys in bits. |
| 1909 | + Optional, by default, set to 64. |
| 1910 | +
|
| 1911 | + Examples |
| 1912 | + ======== |
| 1913 | +
|
| 1914 | + >>> from pydatastructs import FusionTree |
| 1915 | + >>> ft = FusionTree() |
| 1916 | + >>> ft.insert(1, 1) |
| 1917 | + >>> ft.insert(2, 2) |
| 1918 | + >>> ft.search(1) |
| 1919 | + 0 |
| 1920 | + >>> ft.delete(1) |
| 1921 | + True |
| 1922 | + >>> ft.search(1) |
| 1923 | + None |
| 1924 | +
|
| 1925 | + References: |
| 1926 | + - https://en.wikipedia.org/wiki/Fusion_tree |
| 1927 | + - Fredman & Willard (1990): "Fusion Trees" |
| 1928 | + """ |
| 1929 | + |
| 1930 | + __slots__ = ['root_idx', 'tree', 'size', 'B', |
| 1931 | + 'sketch_mask', 'fingerprint_multiplier'] |
| 1932 | + |
| 1933 | + def __new__(cls, key=None, root_data=None, **kwargs): |
| 1934 | + backend = kwargs.get('backend', Backend.PYTHON) |
| 1935 | + raise_if_backend_is_not_python(cls, backend) |
| 1936 | + |
| 1937 | + obj = object.__new__(cls) |
| 1938 | + key = None if root_data is None else key |
| 1939 | + root = TreeNode(key, root_data) |
| 1940 | + root.is_root = True |
| 1941 | + obj.root_idx = 0 |
| 1942 | + obj.tree, obj.size = ArrayForTrees(TreeNode, [root]), 1 |
| 1943 | + obj.B = int(math.log2(kwargs.get('word_size', 64)) |
| 1944 | + ** (1/5)) # Multi-way branching factor |
| 1945 | + obj.sketch_mask = 0 # Computed dynamically |
| 1946 | + obj.fingerprint_multiplier = 2654435761 # Prime multiplier for fingerprinting |
| 1947 | + return obj |
| 1948 | + |
| 1949 | + def _compute_sketch_mask(self): |
| 1950 | + """ |
| 1951 | + Computes a sketch mask for efficient parallel comparisons. |
| 1952 | + """ |
| 1953 | + keys = [node.key for node in self.tree if node is not None] |
| 1954 | + if len(keys) > 1: |
| 1955 | + significant_bits = [max(k.bit_length() for k in keys)] |
| 1956 | + self.sketch_mask = sum(1 << b for b in significant_bits) |
| 1957 | + |
| 1958 | + def insert(self, key, data=None): |
| 1959 | + """ |
| 1960 | + Inserts a key into the Fusion Tree. |
| 1961 | +
|
| 1962 | + Parameters |
| 1963 | + ========== |
| 1964 | +
|
| 1965 | + key: int |
| 1966 | + The integer key to insert. |
| 1967 | + data: Any |
| 1968 | + Optional data to store with the key. |
| 1969 | + """ |
| 1970 | + node = TreeNode(key, data) |
| 1971 | + self.tree.append(node) |
| 1972 | + self.size += 1 |
| 1973 | + if self.size > 1: |
| 1974 | + self._compute_sketch_mask() |
| 1975 | + |
| 1976 | + def _sketch_key(self, key): |
| 1977 | + """ |
| 1978 | + Applies the sketch mask to compress the key for fast comparison. |
| 1979 | + """ |
| 1980 | + return key & self.sketch_mask |
| 1981 | + |
| 1982 | + def _fingerprint(self, key): |
| 1983 | + """ |
| 1984 | + Uses multiplication-based fingerprinting to create a unique identifier |
| 1985 | + for the key, allowing fast parallel searches. |
| 1986 | + """ |
| 1987 | + return (key * self.fingerprint_multiplier) & ((1 << 64) - 1) |
| 1988 | + |
| 1989 | + def search(self, key): |
| 1990 | + """ |
| 1991 | + Searches for a key in the Fusion Tree using bitwise sketching and fingerprinting. |
| 1992 | +
|
| 1993 | + Parameters |
| 1994 | + ========== |
| 1995 | +
|
| 1996 | + key: int |
| 1997 | + The integer key to search. |
| 1998 | +
|
| 1999 | + Returns |
| 2000 | + ======= |
| 2001 | +
|
| 2002 | + int: The index of the key in the tree, or None if not found. |
| 2003 | + """ |
| 2004 | + sketch = self._sketch_key(key) |
| 2005 | + fingerprint = self._fingerprint(key) |
| 2006 | + for i in range(self.size): |
| 2007 | + if self._sketch_key(self.tree[i].key) == sketch and self._fingerprint(self.tree[i].key) == fingerprint: |
| 2008 | + return i |
| 2009 | + return None |
| 2010 | + |
| 2011 | + def delete(self, key): |
| 2012 | + """ |
| 2013 | + Deletes a key from the Fusion Tree. |
| 2014 | +
|
| 2015 | + Parameters |
| 2016 | + ========== |
| 2017 | +
|
| 2018 | + key: int |
| 2019 | + The integer key to delete. |
| 2020 | +
|
| 2021 | + Returns |
| 2022 | + ======= |
| 2023 | +
|
| 2024 | + bool: True if the key was successfully deleted, False otherwise. |
| 2025 | +
|
| 2026 | + """ |
| 2027 | + index = self.search(key) |
| 2028 | + if index is not None: |
| 2029 | + self.tree[index] = None # Soft delete |
| 2030 | + # Compact tree |
| 2031 | + self.tree = [node for node in self.tree if node is not None] |
| 2032 | + self.size -= 1 |
| 2033 | + if self.size > 1: |
| 2034 | + self._compute_sketch_mask() |
| 2035 | + return True |
| 2036 | + return False |
| 2037 | + |
| 2038 | + def __str__(self): |
| 2039 | + """ |
| 2040 | + Returns a string representation of the Fusion Tree. |
| 2041 | + """ |
| 2042 | + return str([(node.key, node.data) for node in self.tree if node is not None]) |
0 commit comments