Skip to content

Commit

Permalink
Split sorting.py
Browse files Browse the repository at this point in the history
Signed-off-by: Michal Šoltis <[email protected]>
  • Loading branch information
slimreaper35 committed Sep 29, 2023
1 parent c2a1a3e commit 3bb4598
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 210 deletions.
3 changes: 1 addition & 2 deletions pysortlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

from .sorting import (
bubble_sort,
counting_sort,
heap_sort,
insert_sort,
merge_sort,
quick_sort,
radix_sort,
selection_sort,
shell_sort,
)
from .sorting_linear import counting_sort, radix_sort
224 changes: 74 additions & 150 deletions pysortlib/sorting.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
from __future__ import annotations

from itertools import chain, repeat

from pysortlib.utils import swap


def insert_sort(array: list[int]) -> None:
"""Sorts an array of integers using the insertion sort algorithm.
def bubble_sort(array: list[int]) -> None:
"""Sorts an array of integers using the bubble sort algorithm.
Time complexity: O(n^2), where n is the length of the array.
Extra space complexity: O(1)
Expand All @@ -17,19 +15,61 @@ def insert_sort(array: list[int]) -> None:
:param array: array of integers
:return: None
"""
for i in range(1, len(array)):
current = array[i]
j = i
length = len(array)
for i in range(length):
for j in range(length - i - 1):
if array[j] > array[j + 1]:
swap(array, j, j + 1)

while j > 0 and array[j - 1] > current:
array[j] = array[j - 1]
j -= 1

array[j] = current
def _left_child(index: int) -> int:
return 2 * index + 1


def bubble_sort(array: list[int]) -> None:
"""Sorts an array of integers using the bubble sort algorithm.
def _right_child(index: int) -> int:
return 2 * index + 2


def _heapify(array: list[int], size: int, index: int) -> None:
largest = index
left = _left_child(index)
right = _right_child(index)

if left < size and array[left] > array[largest]:
largest = left
if right < size and array[right] > array[largest]:
largest = right
if largest != index:
array[index], array[largest] = array[largest], array[index]
_heapify(array, size, largest)


def _build_heap(array: list[int]) -> None:
for i in reversed(range(len(array) // 2)):
_heapify(array, len(array), i)


def heap_sort(array: list[int]) -> None:
"""Sorts an array of integers using the heap sort algorithm.
Time complexity: O(n*log(n)), where n is the length of the array.
Extra space complexity: O(1).
Stable: No (the relative order of equal elements is not preserved).
In-place: Yes (the input array is modified).
:param array: array of integers
:return: None
"""
_build_heap(array)

for index in reversed(range(len(array))):
array[index], array[0] = array[0], array[index]
_heapify(array, index, 0)


def insert_sort(array: list[int]) -> None:
"""Sorts an array of integers using the insertion sort algorithm.
Time complexity: O(n^2), where n is the length of the array.
Extra space complexity: O(1)
Expand All @@ -40,11 +80,15 @@ def bubble_sort(array: list[int]) -> None:
:param array: array of integers
:return: None
"""
length = len(array)
for i in range(length):
for j in range(length - i - 1):
if array[j] > array[j + 1]:
swap(array, j, j + 1)
for i in range(1, len(array)):
current = array[i]
j = i

while j > 0 and array[j - 1] > current:
array[j] = array[j - 1]
j -= 1

array[j] = current


def _merge(array_1: list[int], array_2: list[int]) -> list[int]:
Expand Down Expand Up @@ -90,36 +134,14 @@ def merge_sort(array: list[int]) -> list[int]:
return _merge(left_array_sorted, right_array_sorted)


def selection_sort(array: list[int]) -> None:
"""Sorts an array of integers using the selection sort algorithm.
Time complexity: O(n^2), where n is the length of the array.
Extra space complexity: O(1)
Stable: No (the relative order of equal elements is not preserved).
In-place: Yes (the input array is modified).
:param array: array of integers
:return: None
"""
length = len(array)
for i in range(length - 1):
minimum = i
for j in range(i + 1, length):
if array[j] < array[minimum]:
minimum = j

swap(array, i, minimum)


def _partition(array: list[int], start: int, end: int) -> int:
pivot = array[end]
pivot_index = start - 1

for i in range(start, end):
if array[i] <= pivot:
pivot_index += 1
array[pivot_index], array[i] = array[i], array[pivot_index]
swap(array, pivot_index, i)

swap(array, pivot_index + 1, end)
return pivot_index + 1
Expand All @@ -145,50 +167,26 @@ def quick_sort(array: list[int], left: int, right: int) -> None:
quick_sort(array, middle + 1, right)


def _left_child(index: int) -> int:
return 2 * index + 1


def _right_child(index: int) -> int:
return 2 * index + 2


def _heapify(array: list[int], size: int, index: int) -> None:
largest = index
left = _left_child(index)
right = _right_child(index)

if left < size and array[left] > array[largest]:
largest = left
if right < size and array[right] > array[largest]:
largest = right
if largest != index:
array[index], array[largest] = array[largest], array[index]
_heapify(array, size, largest)


def _build_heap(array: list[int]) -> None:
for i in reversed(range(len(array) // 2)):
_heapify(array, len(array), i)


def heap_sort(array: list[int]) -> None:
"""Sorts an array of integers using the heap sort algorithm.
def selection_sort(array: list[int]) -> None:
"""Sorts an array of integers using the selection sort algorithm.
Time complexity: O(n*log(n)), where n is the length of the array.
Extra space complexity: O(1).
Time complexity: O(n^2), where n is the length of the array.
Extra space complexity: O(1)
Stable: No (the relative order of equal elements is not preserved).
In-place: Yes (the input array is modified).
:param array: array of integers
:return: None
"""
_build_heap(array)
length = len(array)
for i in range(length - 1):
minimum = i
for j in range(i + 1, length):
if array[j] < array[minimum]:
minimum = j

for index in reversed(range(len(array))):
array[index], array[0] = array[0], array[index]
_heapify(array, index, 0)
swap(array, i, minimum)


def shell_sort(array: list[int]) -> None:
Expand Down Expand Up @@ -218,77 +216,3 @@ def shell_sort(array: list[int]) -> None:
array[j] = current

gap //= 2


def counting_sort(array: list[int], *, max_value: int) -> list[int]:
"""Sorts an array of integers using the counting sort algorithm.
Time complexity: O(n+k), where:
- n is the length of the array
- k is the maximum value in the array
Extra space complexity: O(n+k), where:
- n is the length of the array
- k is the maximum value in the array
Stable: Yes (the relative order of equal elements is preserved).
In-place: No (the input array is not modified).
:param array: array of integers
:param max_value: maximum value in the array
:raises: ValueError: if the array contains negative integers
:return: sorted array of integers
"""
if any(num < 0 for num in array):
msg = "The array should contain only non-negative integers"
raise ValueError(msg)

count_of_each_integer = list(repeat(0, max_value + 1))
for num in array:
count_of_each_integer[num] += 1

for i in range(1, max_value + 1):
count_of_each_integer[i] += count_of_each_integer[i - 1]

result = list(repeat(0, len(array)))
for num in reversed(array):
count_of_each_integer[num] -= 1
result[count_of_each_integer[num]] = num

return result


def radix_sort(array: list[int], *, max_digits: int, base: int = 10) -> list[int]:
"""Sorts an array of integers using the radix sort algorithm.
Time complexity: O(d*(n+k)), where:
- d is the number of digits in the largest number
- n is the length of the array
- k is the number of possible digits (the base)
Extra space complexity: O(n+k), where:
- n is the length of the array
- k is the number of possible digits (the base)
Stable: Yes (the relative order of equal elements is preserved).
In-place: No (the input array is not modified).
:param array: array of integers
:param max_digits: number of digits in the largest number
:param base: base of the numbers, default is 10
:raises: ValueError: if the array contains negative integers
:return: sorted array of integers
"""
if any(num < 0 for num in array):
msg = "The array should contain only non-negative integers"
raise ValueError(msg)

for i in range(max_digits):
bins: list[list[int]] = [[] for _ in range(base)]
for num in array:
digit = (num // base**i) % base
bins[digit].append(num)

array = list(chain.from_iterable(bins))

return array
75 changes: 75 additions & 0 deletions pysortlib/sorting_linear.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from __future__ import annotations

from itertools import chain, repeat

from .utils import check_negative_integers


def counting_sort(array: list[int], *, max_value: int) -> list[int]:
"""Sorts an array of integers using the counting sort algorithm.
Time complexity: O(n+k), where:
- n is the length of the array
- k is the maximum value in the array
Extra space complexity: O(n+k), where:
- n is the length of the array
- k is the maximum value in the array
Stable: Yes (the relative order of equal elements is preserved).
In-place: No (the input array is not modified).
:param array: array of integers
:param max_value: maximum value in the array
:raises: ValueError: if the array contains negative integers
:return: sorted array of integers
"""
check_negative_integers(array)

count_of_each_integer = list(repeat(0, max_value + 1))
for num in array:
count_of_each_integer[num] += 1

for i in range(1, max_value + 1):
count_of_each_integer[i] += count_of_each_integer[i - 1]

result = list(repeat(0, len(array)))
for num in reversed(array):
count_of_each_integer[num] -= 1
result[count_of_each_integer[num]] = num

return result


def radix_sort(array: list[int], *, max_digits: int, base: int = 10) -> list[int]:
"""Sorts an array of integers using the radix sort algorithm.
Time complexity: O(d*(n+k)), where:
- d is the number of digits in the largest number
- n is the length of the array
- k is the number of possible digits (the base)
Extra space complexity: O(n+k), where:
- n is the length of the array
- k is the number of possible digits (the base)
Stable: Yes (the relative order of equal elements is preserved).
In-place: No (the input array is not modified).
:param array: array of integers
:param max_digits: number of digits in the largest number
:param base: base of the numbers, default is 10
:raises: ValueError: if the array contains negative integers
:return: sorted array of integers
"""
check_negative_integers(array)

for i in range(max_digits):
bins: list[list[int]] = [[] for _ in range(base)]
for num in array:
digit = (num // base**i) % base
bins[digit].append(num)

array = list(chain.from_iterable(bins))

return array
7 changes: 7 additions & 0 deletions pysortlib/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@
def swap(array: list[int], index_1: int, index_2: int) -> None:
"""Swap two elements in the array."""
array[index_1], array[index_2] = array[index_2], array[index_1]


def check_negative_integers(array: list[int]) -> None:
"""Check if the array contains only non-negative integers."""
if any(num < 0 for num in array):
msg = "The array should contain only non-negative integers"
raise ValueError(msg)
Loading

0 comments on commit 3bb4598

Please sign in to comment.