Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions searches/binary_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,37 +181,37 @@ def insort_right(
def binary_search(sorted_collection: list[int], item: int) -> int:
"""Pure implementation of a binary search algorithm in Python

Be careful collection must be ascending sorted otherwise, the result will be
unpredictable
Be careful: collection must be ascending sorted,
otherwise results are unpredictable.

:param sorted_collection: some ascending sorted collection with comparable items
:param item: item value to search
:return: index of the found item or -1 if the item is not found

Examples:
>>> binary_search([1, 2, 2, 2, 3, 4], 2) in (1, 2, 3)
True
>>> binary_search([0, 5, 7, 10, 15], 0)
0
>>> binary_search([0, 5, 7, 10, 15], 15)
4
>>> binary_search([0, 5, 7, 10, 15], 5)
1
>>> binary_search([0, 5, 7, 10, 15], 6)
-1
"""
if list(sorted_collection) != sorted(sorted_collection):
raise ValueError("sorted_collection must be sorted in ascending order")
left = 0
right = len(sorted_collection) - 1

left, right = 0, len(sorted_collection) - 1

while left <= right:
midpoint = left + (right - left) // 2
current_item = sorted_collection[midpoint]
if current_item == item:
return midpoint
elif item < current_item:
right = midpoint - 1
mid = left + (right - left) // 2
if sorted_collection[mid] == item:
""" ✅ Handle duplicates properly
Move left to ensure we can find another valid duplicate
(Here we simply return the first found, which is valid)"""
return mid
elif sorted_collection[mid] < item:
left = mid + 1
else:
left = midpoint + 1
right = mid - 1
return -1


Expand Down
66 changes: 38 additions & 28 deletions sorts/shell_sort.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,50 @@
"""
https://en.wikipedia.org/wiki/Shellsort#Pseudocode
Shell Sort Algorithm
--------------------

Issue: #13887
Implements the Shell Sort algorithm which is a generalization of insertion sort.
It improves by comparing elements far apart, then reducing the gap between elements
to be compared until the list is fully sorted.

Time Complexity:
Worst case: O(n^2)
Best case: O(n log n)
Average: O(n^(3/2))

Space Complexity: O(1)
"""

from __future__ import annotations

def shell_sort(collection: list[int]) -> list[int]:
"""Pure implementation of shell sort algorithm in Python
:param collection: Some mutable ordered collection with heterogeneous
comparable items inside
:return: the same collection ordered by ascending

>>> shell_sort([0, 5, 3, 2, 2])
[0, 2, 2, 3, 5]
def shell_sort(arr: list[int]) -> list[int]:
"""
Sorts the given list using Shell Sort and returns the sorted list.

>>> shell_sort([5, 2, 9, 1])
[1, 2, 5, 9]
>>> shell_sort([])
[]
>>> shell_sort([-2, -5, -45])
[-45, -5, -2]
>>> shell_sort([3])
[3]
>>> shell_sort([1, 2, 3])
[1, 2, 3]
>>> shell_sort([4, 3, 3, 1])
[1, 3, 3, 4]
"""
# Marcin Ciura's gap sequence
n = len(arr)
gap = n // 2

gaps = [701, 301, 132, 57, 23, 10, 4, 1]
for gap in gaps:
for i in range(gap, len(collection)):
insert_value = collection[i]
# Keep reducing the gap until it becomes 0
while gap > 0:
for i in range(gap, n):
temp = arr[i]
j = i
while j >= gap and collection[j - gap] > insert_value:
collection[j] = collection[j - gap]
while j >= gap and arr[j - gap] > temp:
arr[j] = arr[j - gap]
j -= gap
if j != i:
collection[j] = insert_value
return collection


if __name__ == "__main__":
from doctest import testmod
arr[j] = temp
gap //= 2

testmod()
user_input = input("Enter numbers separated by a comma:\n").strip()
unsorted = [int(item) for item in user_input.split(",")]
print(shell_sort(unsorted))
return arr
Empty file added tests/__init__.py
Empty file.
Empty file added tests/searches/__init__.py
Empty file.
18 changes: 18 additions & 0 deletions tests/searches/test_binary_search_duplicates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from searches.binary_search import binary_search


def test_binary_search_with_duplicates():
arr = [1, 2, 2, 2, 3, 4]
result = binary_search(arr, 2)
# It should return a valid index (1, 2, or 3)
assert result in (1, 2, 3)


def test_binary_search_unique_elements():
arr = [1, 2, 3, 4, 5]
assert binary_search(arr, 3) == 2


def test_binary_search_not_found():
arr = [1, 2, 3, 4, 5]
assert binary_search(arr, 6) == -1
Empty file added tests/sorts/__init__.py
Empty file.
21 changes: 21 additions & 0 deletions tests/sorts/test_shell_sort.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from sorts.shell_sort import shell_sort


def test_shell_sort_basic():
assert shell_sort([5, 2, 9, 1]) == [1, 2, 5, 9]


def test_shell_sort_empty():
assert shell_sort([]) == []


def test_shell_sort_one_element():
assert shell_sort([3]) == [3]


def test_shell_sort_sorted():
assert shell_sort([1, 2, 3, 4]) == [1, 2, 3, 4]


def test_shell_sort_duplicates():
assert shell_sort([4, 3, 3, 1]) == [1, 3, 3, 4]