Beispiel #1
0
 def __new__(cls, elements=None, heap_property="min", d=4):
     obj = Heap.__new__(cls)
     obj.heap_property = heap_property
     obj.d = d
     if heap_property == "min":
         obj._comp = lambda key_parent, key_child: key_parent <= key_child
     elif heap_property == "max":
         obj._comp = lambda key_parent, key_child: key_parent >= key_child
     else:
         raise ValueError("%s is invalid heap property" % (heap_property))
     if elements is None:
         elements = DynamicOneDimensionalArray(TreeNode, 0)
     elif _check_type(elements, (list, tuple)):
         elements = DynamicOneDimensionalArray(TreeNode, len(elements),
                                               elements)
     elif _check_type(elements, Array):
         elements = DynamicOneDimensionalArray(TreeNode, len(elements),
                                               elements._data)
     else:
         raise ValueError(
             f'Expected a list/tuple/Array of TreeNode got {type(elements)}'
         )
     obj.heap = elements
     obj._last_pos_filled = obj.heap._last_pos_filled
     obj._build()
     return obj
Beispiel #2
0
def test_Stack():
    s = Stack(implementation='array')
    s1 = Stack()
    assert _check_type(s, ArrayStack) is True
    assert _check_type(s1, ArrayStack) is True
    s2 = Stack(implementation='linked_list')
    assert _check_type(s2, LinkedListStack) is True
    assert raises(NotImplementedError, lambda: Stack(implementation=''))
Beispiel #3
0
def test_Queue():
    q = Queue(implementation='array')
    q1 = Queue()
    assert _check_type(q, ArrayQueue) is True
    assert _check_type(q1, ArrayQueue) is True
    q2 = Queue(implementation='linked_list')
    assert _check_type(q2, LinkedListQueue) is True
    assert raises(NotImplementedError, lambda: Queue(implementation=''))
 def __new__(cls, root=None, order=None):
     if root is not None and not _check_type(root, BinomialTreeNode):
         raise TypeError("%s i.e., root should be of "
                         "type BinomialTreeNode." % (root))
     if order is not None and not _check_type(order, int):
         raise TypeError("%s i.e., order should be of "
                         "type int." % (order))
     obj = object.__new__(cls)
     if root is not None:
         root.is_root = True
     obj.root = root
     obj.order = order
     return obj
 def __new__(cls, root=None, order=None, **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     if root is not None and \
         not _check_type(root, BinomialTreeNode):
         raise TypeError("%s i.e., root should be of "
                         "type BinomialTreeNode." % (root))
     if order is not None and not _check_type(order, int):
         raise TypeError("%s i.e., order should be of "
                         "type int." % (order))
     obj = object.__new__(cls)
     if root is not None:
         root.is_root = True
     obj.root = root
     obj.order = order
     return obj
Beispiel #6
0
 def __setitem__(self, idx, elem):
     if elem is None:
         self._data[idx] = None
     else:
         if _check_type(elem, self._dtype) is False:
             elem = self._dtype(elem)
         self._data[idx] = elem
Beispiel #7
0
def merge_sort_parallel(array, num_threads, **kwargs):
    """
    Implements parallel merge sort.

    Parameters
    ==========

    array: Array
        The array which is to be sorted.
    num_threads: int
        The maximum number of threads
        to be used for sorting.
    start: int
        The starting index of the portion
        which is to be sorted.
        Optional, by default 0
    end: int
        The ending index of the portion which
        is to be sorted.
        Optional, by default the index
        of the last position filled.
    comp: lambda/function
        The comparator which is to be used
        for sorting. If the function returns
        False then only swapping is performed.
        Optional, by default, less than or
        equal to is used for comparing two
        values.

    Examples
    ========

    >>> from pydatastructs import OneDimensionalArray, merge_sort_parallel
    >>> arr = OneDimensionalArray(int,[3, 2, 1])
    >>> merge_sort_parallel(arr, 3)
    >>> [arr[0], arr[1], arr[2]]
    [1, 2, 3]
    >>> merge_sort_parallel(arr, 3, comp=lambda u, v: u > v)
    >>> [arr[0], arr[1], arr[2]]
    [3, 2, 1]

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Merge_sort
    """
    start = kwargs.get('start', 0)
    end = kwargs.get('end', len(array) - 1)
    comp = kwargs.get("comp", lambda u, v: u <= v)
    for size in range(floor(log(end - start + 1, 2)) + 1):
        pow_2 = 2**size
        with ThreadPoolExecutor(max_workers=num_threads) as Executor:
            i = start
            while i <= end:
                Executor.submit(_merge, array, i, i + pow_2 - 1, i + pow_2,
                                i + 2 * pow_2 - 1, end, comp).result()
                i = i + 2 * pow_2

    if _check_type(array, DynamicArray):
        array._modify(force=True)
Beispiel #8
0
 def __new__(cls, root_list=[]):
     if not all((_check_type(root, BinomialTree)) for root in root_list):
         raise TypeError("The root_list should contain "
                         "references to objects of BinomialTree.")
     obj = Heap.__new__(cls)
     obj.root_list = root_list
     return obj
Beispiel #9
0
def brick_sort(array, **kwargs):
    """
    Implements Brick Sort / Odd Even sorting algorithm

    Parameters
    ==========

    array: Array
        The array which is to be sorted.
    start: int
        The starting index of the portion
        which is to be sorted.
        Optional, by default 0
    end: int
        The ending index of the portion which
        is to be sorted.
        Optional, by default the index
        of the last position filled.
    comp: lambda/function
        The comparator which is to be used
        for sorting. If the function returns
        False then only swapping is performed.
        Optional, by default, less than or
        equal to is used for comparing two
        values.

    Examples
    ========
    >>> from pydatastructs import OneDimensionalArray, brick_sort
    >>> arr = OneDimensionalArray(int,[3, 2, 1])
    >>> brick_sort(arr)
    >>> [arr[0], arr[1], arr[2]]
    [1, 2, 3]
    >>> brick_sort(arr, comp=lambda u, v: u > v)
    >>> [arr[0], arr[1], arr[2]]
    [3, 2, 1]

    References
    ==========
    .. [1] https://www.geeksforgeeks.org/odd-even-sort-brick-sort/
    """
    start = kwargs.get('start', 0)
    end = kwargs.get('end', len(array) - 1)
    comp = kwargs.get("comp", lambda u, v: u <= v)

    is_sorted = False
    while is_sorted is False:
        is_sorted = True
        for i in range(start + 1, end, 2):
            if _comp(array[i + 1], array[i], comp):
                array[i], array[i + 1] = array[i + 1], array[i]
                is_sorted = False
        for i in range(start, end, 2):
            if _comp(array[i + 1], array[i], comp):
                array[i], array[i + 1] = array[i + 1], array[i]
                is_sorted = False

    if _check_type(array, DynamicArray):
        array._modify(force=True)
Beispiel #10
0
def heapsort(array, **kwargs):
    """
    Implements Heapsort algorithm.

    Parameters
    ==========

    array: Array
        The array which is to be sorted.
    start: int
        The starting index of the portion
        which is to be sorted.
        Optional, by default 0
    end: int
        The ending index of the portion which
        is to be sorted.
        Optional, by default the index
        of the last position filled.

    Examples
    ========

    >>> from pydatastructs import OneDimensionalArray, heapsort
    >>> arr = OneDimensionalArray(int,[3, 2, 1])
    >>> heapsort(arr)
    >>> [arr[0], arr[1], arr[2]]
    [1, 2, 3]

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Heapsort

    Note
    ====

    This function does not support custom comparators as is the case with
    other sorting functions in this file.
    """
    from pydatastructs.trees.heaps import BinaryHeap

    start = kwargs.get('start', 0)
    end = kwargs.get('end', len(array) - 1)

    h = BinaryHeap(heap_property="min")
    for i in range(start, end + 1):
        if array[i] is not None:
            h.insert(array[i])
        array[i] = None

    i = start
    while not h.is_empty:
        array[i] = h.extract().key
        i += 1

    if _check_type(array, DynamicArray):
        array._modify(force=True)
Beispiel #11
0
    def __new__(cls, dtype=NoneType, *args, **kwargs):
        backend = kwargs.get('backend', Backend.PYTHON)
        if backend == Backend.CPP:
            return _arrays.OneDimensionalArray(dtype, *args, **kwargs)
        if dtype is NoneType:
            raise ValueError("Data type is not defined.")
        if len(args) not in (1, 2):
            raise ValueError("Too few arguments to create a 1D array,"
                                " pass either size of the array"
                                " or list of elements or both.")
        obj = Array.__new__(cls)
        obj._dtype = dtype
        if len(args) == 2:
            if _check_type(args[0], list) and \
                _check_type(args[1], int):
                for i in range(len(args[0])):
                    if _check_type(args[0][i], dtype) is False:
                        args[0][i] = dtype(args[0][i])
                size, data = args[1], list(args[0])
            elif _check_type(args[1], list) and \
                _check_type(args[0], int):
                for i in range(len(args[1])):
                    if _check_type(args[1][i], dtype) is False:
                        args[1][i] = dtype(args[1][i])
                size, data = args[0], list(args[1])
            else:
                raise TypeError("Expected type of size is int and "
                                "expected type of data is list/tuple.")
            if size != len(data):
                raise ValueError("Conflict in the size, %s and length of data, %s"
                                 %(size, len(data)))
            obj._size, obj._data = size, data

        elif len(args) == 1:
            if _check_type(args[0], int):
                obj._size = args[0]
                init = kwargs.get('init', None)
                obj._data = [init for i in range(args[0])]
            elif _check_type(args[0], (list, tuple)):
                for i in range(len(args[0])):
                    if _check_type(args[0][i], dtype) is False:
                        args[0][i] = dtype(args[0][i])
                obj._size, obj._data = len(args[0]), \
                                        list(args[0])
            else:
                raise TypeError("Expected type of size is int and "
                                "expected type of data is list/tuple.")

        return obj
Beispiel #12
0
 def __new__(cls, root_list=None, **kwargs):
     raise_if_backend_is_not_python(cls,
                                    kwargs.get('backend', Backend.PYTHON))
     if root_list is None:
         root_list = []
     if not all((_check_type(root, BinomialTree)) for root in root_list):
         raise TypeError("The root_list should contain "
                         "references to objects of BinomialTree.")
     obj = Heap.__new__(cls)
     obj.root_list = root_list
     return obj
Beispiel #13
0
    def merge_tree(self, tree1, tree2):
        """
        Merges two BinomialTree objects.

        Parameters
        ==========

        tree1: BinomialTree

        tree2: BinomialTree
        """
        if (not _check_type(tree1, BinomialTree)) or (not _check_type(
                tree2, BinomialTree)):
            raise TypeError("Both the trees should be of type " "BinomalTree.")
        if tree1.root.key <= tree2.root.key:
            tree1.add_sub_tree(tree2)
            ret_value = tree1
        else:
            tree2.add_sub_tree(tree1)
            ret_value = tree2
        return ret_value
Beispiel #14
0
    def __new__(cls, dtype=NoneType, *args, **kwargs):
        if dtype == NoneType or len(args) not in (1, 2):
            raise ValueError("1D array cannot be created due to incorrect"
                             " information.")
        obj = object.__new__(cls)
        obj._dtype = dtype
        if len(args) == 2:
            if _check_type(args[0], (list, tuple)) and \
                _check_type(args[1], int):
                size, data = args[1], [dtype(arg) for arg in args[0]]
            elif _check_type(args[1], (list, tuple)) and \
                _check_type(args[0], int):
                size, data = args[0], [dtype(arg) for arg in args[1]]
            else:
                raise TypeError("Expected type of size is int and "
                                "expected type of data is list/tuple.")
            if size != len(data):
                raise ValueError(
                    "Conflict in the size %s and length of data %s" %
                    (size, len(data)))
            obj._size, obj._data = size, data

        elif len(args) == 1:
            if _check_type(args[0], int):
                obj._size = args[0]
                init = kwargs.get('init', None)
                obj._data = [init for i in range(args[0])]
            elif _check_type(args[0], (list, tuple)):
                obj._size, obj._data = len(args[0]), \
                                        [dtype(arg) for arg in args[0]]
            else:
                raise TypeError("Expected type of size is int and "
                                "expected type of data is list/tuple.")

        return obj
def merge_sort_parallel(array, num_threads, **kwargs):
    """
    Implements parallel merge sort.

    Parameters
    ==========

    array: Array
        The array which is to be sorted.
    num_threads: int
        The maximum number of threads
        to be used for sorting.
    start: int
        The starting index of the portion
        which is to be sorted.
        Optional, by default 0
    end: int
        The ending index of the portion which
        is to be sorted.
        Optional, by default the index
        of the last position filled.

    Examples
    ========

    >>> from pydatastructs import OneDimensionalArray, merge_sort_parallel
    >>> arr = OneDimensionalArray(int,[3, 2, 1])
    >>> merge_sort_parallel(arr, 3)
    >>> [arr[0], arr[1], arr[2]]
    [1, 2, 3]

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Merge_sort
    """
    start = kwargs.get('start', 0)
    end = kwargs.get('end', array._size - 1)
    for size in range(floor(log(end - start + 1, 2)) + 1):
        pow_2 = 2**size
        with ThreadPoolExecutor(max_workers=num_threads) as Executor:
            i = start
            while i <= end:
                Executor.submit(_merge, array, i, i + pow_2 - 1, i + pow_2,
                                i + 2 * pow_2 - 1, end).result()
                i = i + 2 * pow_2

    if _check_type(array, DynamicArray):
        array._modify(force=True)
Beispiel #16
0
    def merge(self, other_heap):
        """
        Merges current binomial heap with the given binomial heap.

        Parameters
        ==========

        other_heap: BinomialHeap
        """
        if not _check_type(other_heap, BinomialHeap):
            raise TypeError("Other heap is not of type BinomialHeap.")
        new_root_list = []
        i, j = 0, 0
        while (i < len(self.root_list)) and \
              (j < len(other_heap.root_list)):
            new_tree = None
            while self.root_list[i] is None:
                i += 1
            while other_heap.root_list[j] is None:
                j += 1
            if self.root_list[i].order == other_heap.root_list[j].order:
                new_tree = self.merge_tree(self.root_list[i],
                                           other_heap.root_list[j])
                i += 1
                j += 1
            else:
                if self.root_list[i].order < other_heap.root_list[j].order:
                    new_tree = self.root_list[i]
                    i += 1
                else:
                    new_tree = other_heap.root_list[j]
                    j += 1
            self._merge_heap_last_new_tree(new_root_list, new_tree)

        while i < len(self.root_list):
            new_tree = self.root_list[i]
            self._merge_heap_last_new_tree(new_root_list, new_tree)
            i += 1
        while j < len(other_heap.root_list):
            new_tree = other_heap.root_list[j]
            self._merge_heap_last_new_tree(new_root_list, new_tree)
            j += 1
        self.root_list = new_root_list
Beispiel #17
0
    def merge(self, other_heap):
        """
        Merges current binomial heap with the given binomial heap.

        Parameters
        ==========

        other_heap: BinomialHeap
        """
        if not _check_type(other_heap, BinomialHeap):
            raise TypeError("Other heap is not of type BinomialHeap.")
        new_root_list = DynamicOneDimensionalArray(BinomialTree, 0)
        i, j = 0, 0
        while ((i <= self.root_list._last_pos_filled)
               and (j <= other_heap.root_list._last_pos_filled)):
            new_tree = None
            while self.root_list[i] is None:
                i += 1
            while other_heap.root_list[j] is None:
                j += 1
            if self.root_list[i].order == other_heap.root_list[j].order:
                new_tree = self.merge_tree(self.root_list[i],
                                           other_heap.root_list[j])
                i += 1
                j += 1
            else:
                if self.root_list[i].order < other_heap.root_list[j].order:
                    new_tree = self.root_list[i]
                    i += 1
                else:
                    new_tree = other_heap.root_list[j]
                    j += 1
            self._merge_heap_last_new_tree(new_root_list, new_tree)

        while i <= self.root_list._last_pos_filled:
            new_tree = self.root_list[i]
            self._merge_heap_last_new_tree(new_root_list, new_tree)
            i += 1
        while j <= other_heap.root_list._last_pos_filled:
            new_tree = other_heap.root_list[j]
            self._merge_heap_last_new_tree(new_root_list, new_tree)
            j += 1
        self.root_list = new_root_list
Beispiel #18
0
 def __new__(cls, elements=None, heap_property="min", d=4):
     obj = Heap.__new__(cls)
     obj.heap_property = heap_property
     obj.d = d
     if heap_property == "min":
         obj._comp = lambda key_parent, key_child: key_parent <= key_child
     elif heap_property == "max":
         obj._comp = lambda key_parent, key_child: key_parent >= key_child
     else:
         raise ValueError("%s is invalid heap property" % (heap_property))
     if elements is None:
         elements = DynamicOneDimensionalArray(TreeNode, 0)
     else:
         if not all(map(lambda x: _check_type(x, TreeNode), elements)):
             raise ValueError("Expect a list/tuple of TreeNode got %s" %
                              (elements))
     obj.heap = elements
     obj._last_pos_filled = obj.heap._last_pos_filled
     obj._build()
     return obj
    def query(self, qx, init_node=None):
        """
        Queries the segment tree.

        Parameters
        ==========

        qx: int/float
            The query point

        init_node: int
            The index of the node from which the query process
            is to be started.

        Returns
        =======

        intervals: set
            The set of the intervals which contain the query
            point.

        References
        ==========

        .. [1] https://en.wikipedia.org/wiki/Segment_tree
        """
        if not self.cache:
            self.build()
        if init_node is None:
            init_node = self.root_idx
        qn = TreeNode([True, qx, qx, True], None)
        intervals = []
        calls = [init_node]
        while calls:
            idx = calls.pop()
            if _check_type(self.tree[idx].data, list):
                intervals.extend(self.tree[idx].data)
            calls = self._iterate(calls, qn, idx)
        return set(intervals)
Beispiel #20
0
    def add_sub_tree(self, other_tree):
        """
        Adds a sub tree to current tree.

        Parameters
        ==========

        other_tree: BinomialTree

        Raises
        ======

        ValueError: If order of the two trees
                    are different.
        """
        if not _check_type(other_tree, BinomialTree):
            raise TypeError("%s i.e., other_tree should be of "
                            "type BinomialTree" % (other_tree))
        if self.order != other_tree.order:
            raise ValueError("Orders of both the trees should be same.")
        self.root.children.append(other_tree.root)
        other_tree.root.parent = self.root
        other_tree.root.is_root = False
        self.order += 1
Beispiel #21
0
def timsort(array : Array,start,end)-> Array:
    """

    The Timsort algorithm is considered a hybrid sorting algorithm because
    it employs a best-of-both-worlds combination of insertion sort and
    merge sort. The main characteristic of Timsort is that it takes
    advantage of already-sorted elements that exist in most real-world
    datasets.These are called natural runs. The algorithm then iterates
    over the list, collecting the elements into runs and merging them into
    a single sorted list.


    """
    """
      Parameters
      ========

      array: Array
          The required array to be sorted
      start: int
          The starting index of the portion
          which is to be sorted.
          Optional, by default 0
      end: int
          The ending index of the portion which
          is to be sorted.
          Optional, by default the index
          of the last position filled.

      Returns
      =======

      output: Array
          The sorted list or array

      Examples
      ========

      >>> from pydatastructs import OneDimensionalArray, timsort
      >>> arr = OneDimensionalArray(int,[-2, 7, 15, -14, 0, 15, 0] )
      >>> timsort(arr, 0, 14)
      >>> [arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6]]
      [ -14, -2, 0, 0, 7, 15, 15]


      >>> from pydatastructs import OneDimensionalArray, timsort
      >>> arr = OneDimensionalArray(int,[3, 2, 1])
      >>> timsort(arr, 0, 2)
      >>> [arr[0], arr[1], arr[2]]
      [1, 2, 3]



      References
      ==========

      .. [1] https://en.wikipedia.org/wiki/Timsort
      """

    min_run = 32
    n = len(array)

    # Start by slicing and sorting small portions of the
    # input array. The size of these slices is defined by
    # your `min_run` size.
    for i in range(0, n, min_run):
        insertion_sort(array, i, min((i + min_run - 1), n - 1))

    # Now you can start merging the sorted slices.
    # Start from `min_run`, doubling the size on
    # each iteration until you surpass the length of
    # the array.
    size = min_run
    while size < n:
        # Determine the arrays that will
        # be merged together
        for start in range(0, n, size * 2):
            # Compute the `midpoint` (where the first array ends
            # and the second starts) and the `endpoint` (where
            # the second array ends)
            midpoint = start + size - 1
            end = min((start + size * 2 - 1), (n-1))

            # Merge the two subarrays.
            # The `left` array should go from `start` to
            # `midpoint + 1`, while the `right` array should
            # go from `midpoint + 1` to `end + 1`.
            merged_array = _merge(
               left= array[start:midpoint + 1],
               right= array[midpoint + 1:end + 1]
            )

            # Finally, put the merged array back into
            # your array
            array[start:start + len(merged_array)] = merged_array

        # Each iteration should double the size of your arrays
        size *= 2

    if _check_type(array, DynamicArray):
        array._modify(force=True)
Beispiel #22
0
def insertion_sort(array, **kwargs):
    """
    Implements insertion sort algorithm.

    Parameters
    ==========

    array: Array
        The array which is to be sorted.
    start: int
        The starting index of the portion
        which is to be sorted.
        Optional, by default 0
    end: int
        The ending index of the portion which
        is to be sorted.
        Optional, by default the index
        of the last position filled.
    comp: lambda/function
        The comparator which is to be used
        for sorting. If the function returns
        False then only swapping is performed.
        Optional, by default, less than or
        equal to is used for comparing two
        values.
    backend: pydatastructs.Backend
        The backend to be used.
        Optional, by default, the best available
        backend is used.

    Returns
    =======

    output: Array
        The sorted array.

    Examples
    ========

    >>> from pydatastructs import OneDimensionalArray, insertion_sort
    >>> arr = OneDimensionalArray(int,[3, 2, 1])
    >>> out = insertion_sort(arr)
    >>> str(out)
    '[1, 2, 3]'
    >>> out = insertion_sort(arr, comp=lambda u, v: u > v)
    >>> str(out)
    '[3, 2, 1]'

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Insertion_sort
    """
    raise_if_backend_is_not_python(insertion_sort,
                                   kwargs.get('backend', Backend.PYTHON))
    start = kwargs.get('start', 0)
    end = kwargs.get('end', len(array) - 1)
    comp = kwargs.get('comp', lambda u, v: u <= v)

    for i in range(start + 1, end + 1):
        temp = array[i]
        j = i
        while j > start and not _comp(array[j - 1], temp, comp):
            array[j] = array[j - 1]
            j -= 1
        array[j] = temp

    if _check_type(array, DynamicArray):
        array._modify(force=True)

    return array
Beispiel #23
0
def test_PriorityQueue():
    pq1 = PriorityQueue(implementation='linked_list')
    assert _check_type(pq1, LinkedListPriorityQueue) is True
    assert raises(NotImplementedError, lambda: Queue(implementation=''))
Beispiel #24
0
def bucket_sort(array: Array, **kwargs) -> Array:
    """
    Performs bucket sort on the given array.

    Parameters
    ==========

    array: Array
        The array which is to be sorted.
    start: int
        The starting index of the portion
        which is to be sorted.
        Optional, by default 0
    end: int
        The ending index of the portion which
        is to be sorted.
        Optional, by default the index
        of the last position filled.

    Returns
    =======

    output: Array
        The sorted array.

    Examples
    ========

    >>> from pydatastructs import DynamicOneDimensionalArray as DODA, bucket_sort
    >>> arr = DODA(int, [5, 78, 1, 0])
    >>> out = bucket_sort(arr)
    >>> str(out)
    "['0', '1', '5', '78']"
    >>> arr.delete(2)
    >>> out = bucket_sort(arr)
    >>> str(out)
    "['0', '1', '78']"

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Bucket_sort

    Note
    ====

    This function does not support custom comparators as is the case with
    other sorting functions in this file.
    The ouput array doesn't contain any `None` value.
    """
    start = kwargs.get('start', 0)
    end = kwargs.get('end', len(array) - 1)

    #Find maximum value in the list and use length of the list to determine which value in the list goes into which bucket
    max_value = None
    for i in range(start, end + 1):
        if array[i] is not None:
            max_value = array[i]

    count = 0
    for i in range(start, end + 1):
        if array[i] is not None:
            count += 1
            if array[i] > max_value:
                max_value = array[i]

    number_of_null_values = end - start + 1 - count
    size = max_value // count

    # Create n empty buckets where n is equal to the length of the input list
    buckets_list = [[] for _ in range(count)]

    # Put list elements into different buckets based on the size
    for i in range(start, end + 1):
        if array[i] is not None:
            j = array[i] // size
            if j is not count:
                buckets_list[j].append(array[i])
            else:
                buckets_list[count - 1].append(array[i])

    # Sort elements within the buckets using Insertion Sort
    for z in range(count):
        _bucket_sort_helper(buckets_list[z])

    # Concatenate buckets with sorted elements into a single array
    sorted_list = []
    for x in range(count):
        sorted_list.extend(buckets_list[x])
    for i in range(end, end - number_of_null_values, -1):
        array[i] = None
    for i in range(start, end - number_of_null_values + 1):
        array[i] = sorted_list[i - start]
    if _check_type(array, DynamicArray):
        array._modify(force=True)
    return array
Beispiel #25
0
def counting_sort(array: Array) -> Array:
    """
    Performs counting sort on the given array.

    Parameters
    ==========

    array: Array
        The array which is to be sorted.

    Returns
    =======

    output: Array
        The sorted array.

    Examples
    ========

    >>> from pydatastructs import DynamicOneDimensionalArray as DODA, counting_sort
    >>> arr = DODA(int, [5, 78, 1, 0])
    >>> out = counting_sort(arr)
    >>> str(out)
    "['0', '1', '5', '78']"
    >>> arr.delete(2)
    >>> out = counting_sort(arr)
    >>> str(out)
    "['0', '5', '78']"

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Counting_sort

    Note
    ====

    Since, counting sort is a non-comparison sorting algorithm,
    custom comparators aren't allowed.
    The ouput array doesn't contain any `None` value.
    """
    max_val, min_val = array[0], array[0]
    none_count = 0
    for i in range(len(array)):
        if array[i] is not None:
            if max_val is None or max_val < array[i]:
                max_val = array[i]
            if min_val is None or array[i] < min_val:
                min_val = array[i]
        else:
            none_count += 1
    if min_val is None or max_val is None:
        return array

    count = [0 for _ in range(max_val - min_val + 1)]
    for i in range(len(array)):
        if array[i] is not None:
            count[array[i] - min_val] += 1

    total = 0
    for i in range(max_val - min_val + 1):
        count[i], total = total, count[i] + total

    output = type(array)(
        array._dtype,
        [array[i] for i in range(len(array)) if array[i] is not None])
    if _check_type(output, DynamicArray):
        output._modify(force=True)

    for i in range(len(array)):
        x = array[i]
        if x is not None:
            output[count[x - min_val]] = x
            count[x - min_val] += 1

    return output
Beispiel #26
0
def brick_sort_parallel(array, num_threads, **kwargs):
    """
    Implements Concurrent Brick Sort / Odd Even sorting algorithm

    Parameters
    ==========

    array: Array/list
        The array which is to be sorted.
    num_threads: int
        The maximum number of threads
        to be used for sorting.
    start: int
        The starting index of the portion
        which is to be sorted.
        Optional, by default 0
    end: int
        The ending index of the portion which
        is to be sorted.
        Optional, by default the index
        of the last position filled.
    comp: lambda/function
        The comparator which is to be used
        for sorting. If the function returns
        False then only swapping is performed.
        Optional, by default, less than or
        equal to is used for comparing two
        values.

    Examples
    ========

    >>> from pydatastructs import OneDimensionalArray, brick_sort_parallel
    >>> arr = OneDimensionalArray(int,[3, 2, 1])
    >>> brick_sort_parallel(arr, num_threads=5)
    >>> [arr[0], arr[1], arr[2]]
    [1, 2, 3]
    >>> brick_sort_parallel(arr, num_threads=5, comp=lambda u, v: u > v)
    >>> [arr[0], arr[1], arr[2]]
    [3, 2, 1]

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Odd%E2%80%93even_sort
    """

    start = kwargs.get('start', 0)
    end = kwargs.get('end', len(array) - 1)
    comp = kwargs.get("comp", lambda u, v: u <= v)

    is_sorted = [False]
    with ThreadPoolExecutor(max_workers=num_threads) as Executor:
        while is_sorted[0] is False:
            is_sorted[0] = True
            for i in range(start + 1, end, 2):
                Executor.submit(_brick_sort_swap, array, i, i + 1, comp,
                                is_sorted).result()

            for i in range(start, end, 2):
                Executor.submit(_brick_sort_swap, array, i, i + 1, comp,
                                is_sorted).result()

    if _check_type(array, DynamicArray):
        array._modify(force=True)
Beispiel #27
0
def quick_sort(array: Array, **kwargs) -> Array:
    """
    Performs quick sort on the given array.

    Parameters
    ==========

    array: Array
        The array which is to be sorted.
    start: int
        The starting index of the portion
        which is to be sorted.
        Optional, by default 0
    end: int
        The ending index of the portion which
        is to be sorted.
        Optional, by default the index
        of the last position filled.
    comp: lambda/function
        The comparator which is to be used
        for sorting. If the function returns
        False then only swapping is performed.
        Optional, by default, less than or
        equal to is used for comparing two
        values.
    pick_pivot_element: lambda/function
        The function implementing the pivot picking
        logic for quick sort. Should accept, `low`,
        `high`, and `array` in this order, where `low`
        represents the left end of the current partition,
        `high` represents the right end, and `array` is
        the original input array to `quick_sort` function.
        Optional, by default, picks the element at `high`
        index of the current partition as pivot.

    Returns
    =======

    output: Array
        The sorted array.

    Examples
    ========

    >>> from pydatastructs import OneDimensionalArray as ODA, quick_sort
    >>> arr = ODA(int, [5, 78, 1, 0])
    >>> out = quick_sort(arr)
    >>> str(out)
    '[0, 1, 5, 78]'
    >>> arr = ODA(int, [21, 37, 5])
    >>> out = quick_sort(arr)
    >>> str(out)
    '[5, 21, 37]'

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Quicksort
    """
    from pydatastructs import Stack
    comp = kwargs.get("comp", lambda u, v: u <= v)
    pick_pivot_element = kwargs.get("pick_pivot_element",
                                    lambda low, high, array: array[high])

    def partition(low, high, pick_pivot_element):
        i = (low - 1)
        x = pick_pivot_element(low, high, array)
        for j in range(low, high):
            if _comp(array[j], x, comp) is True:
                i = i + 1
                array[i], array[j] = array[j], array[i]
        array[i + 1], array[high] = array[high], array[i + 1]
        return (i + 1)

    lower = kwargs.get('start', 0)
    upper = kwargs.get('end', len(array) - 1)
    stack = Stack()

    stack.push(lower)
    stack.push(upper)

    while stack.is_empty is False:
        high = stack.pop()
        low = stack.pop()
        p = partition(low, high, pick_pivot_element)
        if p - 1 > low:
            stack.push(low)
            stack.push(p - 1)
        if p + 1 < high:
            stack.push(p + 1)
            stack.push(high)

    if _check_type(array, DynamicArray):
        array._modify(force=True)

    return array
Beispiel #28
0
def cocktail_shaker_sort(array: Array, **kwargs) -> Array:
    """
    Performs cocktail sort on the given array.

    Parameters
    ==========

    array: Array
        The array which is to be sorted.
    start: int
        The starting index of the portion
        which is to be sorted.
        Optional, by default 0
    end: int
        The ending index of the portion which
        is to be sorted.
        Optional, by default the index
        of the last position filled.
    comp: lambda/function
        The comparator which is to be used
        for sorting. If the function returns
        False then only swapping is performed.
        Optional, by default, less than or
        equal to is used for comparing two
        values.

    Returns
    =======

    output: Array
        The sorted array.

    Examples
    ========

    >>> from pydatastructs import OneDimensionalArray as ODA, cocktail_shaker_sort
    >>> arr = ODA(int, [5, 78, 1, 0])
    >>> out = cocktail_shaker_sort(arr)
    >>> str(out)
    '[0, 1, 5, 78]'
    >>> arr = ODA(int, [21, 37, 5])
    >>> out = cocktail_shaker_sort(arr)
    >>> str(out)
    '[5, 21, 37]'

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Cocktail_shaker_sort
    """
    def swap(i, j):
        array[i], array[j] = array[j], array[i]

    lower = kwargs.get('start', 0)
    upper = kwargs.get('end', len(array) - 1)
    comp = kwargs.get("comp", lambda u, v: u <= v)

    swapping = False
    while (not swapping and upper - lower >= 1):

        swapping = True
        for j in range(lower, upper):
            if _comp(array[j], array[j + 1], comp) is False:
                swap(j + 1, j)
                swapping = False

        upper = upper - 1
        for j in range(upper, lower, -1):
            if _comp(array[j - 1], array[j], comp) is False:
                swap(j, j - 1)
                swapping = False
        lower = lower + 1

    if _check_type(array, DynamicArray):
        array._modify(force=True)

    return array