Skip to content
Closed
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
119 changes: 99 additions & 20 deletions sorts/patience_sort.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
from typing import Protocol

"""
A pure Python implementation of the patience sort algorithm
A pure Python implementation of the patience sort algorithm.

For more information: https://en.wikipedia.org/wiki/Patience_sorting

This algorithm is based on the card game patience
This algorithm is based on the card game patience.

For doctests run following command:
This version can sort integers, floats, and strings using
mixed_patience_sort().

For doctests run the following command:
python3 -m doctest -v patience_sort.py

For manual testing run:
Expand All @@ -35,13 +38,10 @@ def __eq__(self, other: object) -> bool:
return self[-1] == other[-1]


def patience_sort[T: Comparable](collection: list[T]) -> list[T]:
"""A pure implementation of patience sort algorithm in Python

:param collection: some mutable ordered collection with heterogeneous
comparable items inside
:return: the same collection ordered by ascending

def patience_sort(collection: list) -> list:
"""Sort a collection using the patience sort algorithm.
:param collection: A mutable ordered collection containing comparable items.
:return: The same collection ordered in ascending order.
Examples:
>>> patience_sort([1, 9, 5, 21, 17, 6])
[1, 5, 6, 9, 17, 21]
Expand All @@ -64,21 +64,100 @@ def patience_sort[T: Comparable](collection: list[T]) -> list[T]:
TypeError: '<' not supported between instances of 'str' and 'int'
"""
stacks: list[Stack] = []
# sort into stacks

# Place each element on the first suitable stack, following
# the rules of the patience sort algorithm.
for element in collection:
new_stacks = Stack([element])
i = bisect_left(stacks, new_stacks)
if i != len(stacks):
stacks[i].append(element)
new_stack: list = Stack([element])
stack_index: list = bisect_left(stacks, new_stack)

if stack_index != len(stacks):
stacks[stack_index].append(element)
else:
stacks.append(new_stacks)
stacks.append(new_stack)

# use a heap-based merge to merge stack efficiently
# Merge the stacks to produce the final sorted collection.
collection[:] = merge(*(reversed(stack) for stack in stacks))
return collection


def mixed_patience_sort(collection: list) -> list:
"""Sort integers, floats, and strings using patience sort.

Numbers are sorted by magnitude and placed before strings.
Strings are sorted alphabetically according to Python's
standard string ordering.

:param collection: A mutable collection containing integers,
floats, and strings.
:return: The same collection with numbers followed by strings.

Examples:
>>> mixed_patience_sort([1, 9, 3.64, "Apple", "letter", 23.65])
[1, 3.64, 9, 23.65, 'Apple', 'letter']

>>> mixed_patience_sort(["banana", "Apple", "letter"])
['Apple', 'banana', 'letter']

>>> mixed_patience_sort([3.5, 1, 8.2])
[1, 3.5, 8.2]

>>> mixed_patience_sort([])
[]

>>> mixed_patience_sort([1, {"name": "Obed"}])
Traceback (most recent call last):
ValueError: Only integers, floats, and strings are allowed.
"""
number_items: list[int | float] = []
string_items: list[str] = []

for element in collection:
match element:
case int() | float():
number_items.append(element)
case str():
string_items.append(element)
case _:
raise ValueError("Only integers, floats, and strings are allowed.")

sorted_numbers: int = patience_sort(number_items)
sorted_strings: str = patience_sort(string_items)

collection[:] = sorted_numbers + sorted_strings
return collection


def convert_item(item: str) -> int | float | str:
"""Convert a string into an integer, float, or string.

:param item: The input string to convert.
:return: An integer, float, or the original string.

Examples:
>>> convert_item("25")
25

>>> convert_item("3.14")
3.14

>>> convert_item("Apple")
'Apple'
"""
item = item.strip()

try:
return int(item)
except ValueError:
try:
return float(item)
except ValueError:
return item


if __name__ == "__main__":
user_input = input("Enter numbers separated by a comma:\n").strip()
unsorted = [int(item) for item in user_input.split(",")]
print(patience_sort(unsorted))
user_input: str | int | float = input(
"Enter integers, floats, and strings separated by commas:\n"
).strip()
unsorted: str | int | float = [convert_item(item) for item in user_input.split(",")]
print(mixed_patience_sort(unsorted))
Loading