def _add_extra_level(self): """ Creates a new level at the top of the `SkipList()`, a new level is a new `LinkedList()` instance attached to the `SkipList()`. Returns ------- LinkedList(): The newly-created `LinkedList()` instance after being attached to the other `LinkedList()` object in the lower level. """ top_list = self._level_lists[self._num_levels - 1] new_llist = LinkedList() new_llist._insert_node(new_llist._head, self._basic_node(float("-inf"))) # connect the head of the new linked list to the lower linked list new_llist._head.set_down(top_list._head) # add new linked list to the SkipList self._level_lists.append(new_llist) self._num_levels += 1 return new_llist
def __init__(self, iterable=None): """ Initializes a `SkipList()` instance using an optional iterable object in time-complexity of O(n) where **n** is the number of elements inside the given `iterable`. Parameters ---------- iterable: iterable, optional An iterable python object that implements the `__iter__` method. For example, `list` and `tuple` are both iterables. Raises ------ TypeError: It can be raised in three cases 1. In case the given object isn't iterable. 2. If one of the elements in the iterable is an `Extra` object. 3. If one of the elements in the iterable is NOT a number. ValueError: If one of the iterable elements is `None`. Note ----- Inserting values into different levels in the skip list is completely random. So, running the following example will return different values each time you run it. So, in order to obtain the same result as before you need to set `random.seed(1)` before running any of the previous example. Examples -------- >>> import random; random.seed(1) >>> sl = SkipList([10, -5, 7, 9]) >>> sl ┌────┐ ┌────┐ | -∞ │⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶| 10 │⟶ ├────┤ ┌────┐ ├────┤ | -∞ │⟶| -5 │⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶⟶| 10 │⟶ ├────┤ ├────┤ ┌───┐ ┌───┐ ├────┤ | -∞ │⟶| -5 │⟶| 7 │⟶| 9 │⟶| 10 │⟶ └────┘ └────┘ └───┘ └───┘ └────┘ Using an iterable object with `None` as one of its elements will raise `ValueError` >>> sl = SkipList([2, None]) ValueError: Can't use `None` as an element within `extra.SkipList()`!! Using a non-iterable object will raise `TypeError` >>> sl = SkipList(2) TypeError: The given object isn't iterable!! Using nested `SkipList` objects will raise `TypeError` as well >>> sl_1 = SkipList([1]) >>> sl_2 = SkipList([1, sl_1]) TypeError: Can't create `extra.SkipList()` using `extra.SkipList()`!! """ # NOTE: `_level_lists` is an array of LinkedList() objects if iterable is None: ll = LinkedList() ll._insert_node(ll._head, self._basic_node(float("-inf"))) self._level_lists = [ll] self._num_levels = 1 elif not hasattr(iterable, "__iter__"): raise TypeError("The given object isn't iterable!!") elif isinstance(iterable, self.__class__): self._level_lists = iterable._level_lists self._num_levels = iterable._num_levels else: ll = LinkedList() ll._insert_node(ll._head, self._basic_node(float("-inf"))) self._level_lists = [ll] self._num_levels = 1 for item in iterable: self.insert(item)