Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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)