def partition_3way(items, start, end): if start >= end: return None, None partitioning_item = items[start] less_than_idx = start greater_than_idx = end # Invariant: # - a[start..less_than_idx - 1] < partitioning_item # - a[greater_than_idx + 1..end] > partitioning_item # - a[less_than_idx..i-1] = partitioning_item # - a[i..greater_than_idx] are not yet examined i = start + 1 while i <= greater_than_idx: if lt(items[i], partitioning_item): # move items < partitioning_item to the left exchange(items, i, less_than_idx) less_than_idx = less_than_idx + 1 # move the pointer because we know that the new item at the position i is # either equal or less than the partitioning item i = i + 1 elif gt(items[i], partitioning_item): # move items > partitioning_item to the right exchange(items, i, greater_than_idx) greater_than_idx = greater_than_idx - 1 # here, we don't move the pointer because we don't know how the new item # at the position i compares to the partitioning item else: # leave the items = partitioning_item where they are i = i + 1 return less_than_idx, greater_than_idx
def min(self): if self.is_empty(): return None current_min = self._items[0].key for i in range(1, self.size_of_range()): if lt(self._items[i].key, current_min): current_min = self._items[i].key return current_min
def delete(self, key): # no node with the given key exists in the tree if self.get(key) is None: return parent = None is_node_left_child = False node = self._root while node is not None: if eq(key, node.key): break parent = node # this is why we want to make sure that the key must exist in the tree. parent.node_count = parent.node_count - 1 if lt(key, node.key): node = node.left is_node_left_child = True else: node = node.right is_node_left_child = False successor, parent_of_successor = self._find_successor_and_parent(node) if successor is None: # the deleted node has no right subtree if parent is None: # the deleted node is the current root self._root = node.left elif is_node_left_child: parent.left = node.left else: parent.right = node.left else: # replace successor by its right child # Note: Because the successor is the left-most node, it can either have no child or only one right child, # and it must be its parent's left child. if parent_of_successor is not None: parent_of_successor.left = successor.right # replace the deleted node by its successor if parent is None: # the deleted node is the current root self._root = successor elif is_node_left_child: parent.left = successor else: parent.right = successor successor.left = node.left # the successor is the node's right child if it is the only node in the node's right subtree, # and we want to avoid cycle here. if not eq(successor, node.right): successor.right = node.right if successor is not None: successor.node_count = node.node_count - 1 if parent_of_successor is not None: parent_of_successor.node_count = parent_of_successor.node_count - 1
def get(self, key) -> Optional[Node]: if self.is_empty(): return None current = self._root while current is not None: if eq(current.key, key): return current current = current.left if lt(key, current.key) else current.right return None
def _floor(self, key, node): if node is None: return None if eq(key, node.key): return node if lt(key, node.key): return self._floor(key, node.left) right_subtree_floor = self._floor(key, node.right) return right_subtree_floor if right_subtree_floor is not None else node
def _range(self, node, low, high): if node is None: return [] node_satisfy = (low is None or gt_or_eq(node.key, low)) \ and (high is None or lt_or_eq(node.key, high)) result = [node] if node_satisfy else [] if low is not None and lt(node.key, low): return result + self._range(node.right, low, high) if high is not None and gt(node.key, high): return result + self._range(node.left, low, high) return result + self._range(node.left, low, high) + self._range( node.right, low, high)
def ceiling(self, key: Key): if self.is_empty(): return None candidate = None for i in range(self.size_of_range()): current_item_key = self._items[i].key if current_item_key.equals(key): return current_item_key candidate = current_item_key \ if gt(current_item_key, key) and (candidate is None or lt(current_item_key, candidate)) \ else candidate return candidate
def _put(self, new_node: Node, position: Node): if position is None: return new_node if eq(new_node.key, position.key): position.value = new_node.value return position if lt(new_node.key, position.key): position.left = self._put(new_node, position.left) else: position.right = self._put(new_node, position.right) position.node_count = (position.left.node_count if position.left is not None else 0) \ + (position.right.node_count if position.right is not None else 0) \ + 1 return position
def rank(self, key: Key): low = 0 high = len(self._items) - 1 while high >= low: mid = int(floor(high + low) / 2) if eq(key, self._items[mid].key): return mid if mid + 1 < len(self._items) and gt( key, self._items[mid].key) and lt( key, self._items[mid + 1].key): return mid + 1 if gt(key, self._items[mid].key): low = mid + 1 else: high = mid - 1 if low >= len(self._items): return len(self._items) if high < 0: return 0
def rank(self, key: Key): result = 0 for item in self._items: if lt(item.key, key): result = result + 1 return result