Skip to content
Open
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
71 changes: 71 additions & 0 deletions sorts/tim_sort.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,33 @@


def binary_search(lst: list[Any], item: Any, start: int, end: int) -> int:
""">>> binary_search([1, 3, 5], 4, 0, 2)
2
>>> binary_search([1, 3, 5], 0, 0, 2)
0
>>> binary_search([1, 3, 5], 6, 0, 2)
3

Find the insertion index for ``item`` in a sorted sublist.

It performs a recursive binary search on ``lst`` between indices
``start`` and ``end`` (inclusive) and returns the index showing
where to insert the item so the list stays sorted.

Args:
lst: A list of comparable items.
The sublist from ``start`` to ``end`` must already be sorted.
item: The value to locate an insertion index for.
start: Left-most index of the sorted sublist to search.
end: Right-most index of the sorted sublist to search.

Returns:
The index at which ``item`` should be inserted.

Complexity:
Time: ``O(log n)`` for the searched sublist.
Space: ``O(log n)`` due to recursion depth.
"""
if start == end:
return start if lst[start] > item else start + 1
if start > end:
Expand All @@ -17,6 +44,26 @@ def binary_search(lst: list[Any], item: Any, start: int, end: int) -> int:


def insertion_sort(lst: list[Any]) -> list[Any]:
""">>> insertion_sort([3, 2, 1])
[1, 2, 3]

Return a sorted copy of ``lst`` using insertion sort.

Uses ``binary_search`` to find where to insert each item. The
input list is not modified; a new sorted list is returned.

Args:
lst: The list to sort. A new list is returned; the input list is
not modified in-place.

Returns:
A new list containing the elements of ``lst`` in ascending order.

Complexity:
Time: ``O(n^2)`` in the worst case because each insertion may
shift many elements.
Space: ``O(n)`` for the reconstructed list copies.
"""
length = len(lst)

for index in range(1, length):
Expand All @@ -28,6 +75,23 @@ def insertion_sort(lst: list[Any]) -> list[Any]:


def merge(left: list[Any], right: list[Any]) -> list[Any]:
""">>> merge([1, 4], [2, 3])
[1, 2, 3, 4]

Merge two sorted lists and return a new sorted list.

Args:
left: A list sorted in ascending order.
right: A list sorted in ascending order.

Returns:
A new list containing all elements from ``left`` and ``right`` in
ascending order.

Complexity:
Time: ``O(n + m)`` where ``n`` and ``m`` are the input lengths.
Space: ``O(n + m)`` because recursive slicing creates new lists.
"""
if not left:
return right

Expand All @@ -52,6 +116,13 @@ def tim_sort(lst: list[Any] | tuple[Any, ...] | str) -> list[Any]:
True
>>> tim_sort([3, 2, 1]) == sorted([3, 2, 1])
True

Sort and return the input using a TimSort-like approach: detect
runs, sort each run with insertion sort, then merge the runs.

Complexity:
Time: ``O(n log n)`` in the common case.
Space: ``O(n)`` for the extra lists used during sorting.
"""
length = len(lst)
runs, sorted_runs = [], []
Expand Down