Skip to content
Open
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
102 changes: 102 additions & 0 deletions data_structures/linked_list/flatten_binary_tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""
Flatten a binary tree to a linked list in-place.

The algorithm modifies the given binary tree so that it becomes a right-skewed
linked list following preorder traversal order (root -> left -> right).

Time complexity: O(n)
Space complexity: O(1) (excluding recursion stack)

Doctest:
>>> # build tree: [1,2,5,3,4,None,6]
>>> root = TreeNode(1, TreeNode(2, TreeNode(3), TreeNode(4)), TreeNode(5, None, TreeNode(6)))

Check failure on line 12 in data_structures/linked_list/flatten_binary_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

data_structures/linked_list/flatten_binary_tree.py:12:89: E501 Line too long (93 > 88)
>>> flatten(root)
>>> to_list(root)
[1, 2, 3, 4, 5, 6]

>>> # single node
>>> root = TreeNode(0)
>>> flatten(root)
>>> to_list(root)
[0]
"""

from __future__ import annotations
from typing import Optional, List

Check failure on line 25 in data_structures/linked_list/flatten_binary_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (UP035)

data_structures/linked_list/flatten_binary_tree.py:25:1: UP035 `typing.List` is deprecated, use `list` instead
import doctest

Check failure on line 26 in data_structures/linked_list/flatten_binary_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

data_structures/linked_list/flatten_binary_tree.py:24:1: I001 Import block is un-sorted or un-formatted


class TreeNode:
"""
Node of a binary tree.

Attributes:
val (int): Node value.
left (Optional[TreeNode]): Left child.
right (Optional[TreeNode]): Right child.
"""

def __init__(
self,
val: int = 0,
left: Optional["TreeNode"] = None,

Check failure on line 42 in data_structures/linked_list/flatten_binary_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (UP045)

data_structures/linked_list/flatten_binary_tree.py:42:15: UP045 Use `X | None` for type annotations
right: Optional["TreeNode"] = None,

Check failure on line 43 in data_structures/linked_list/flatten_binary_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (UP045)

data_structures/linked_list/flatten_binary_tree.py:43:16: UP045 Use `X | None` for type annotations
) -> None:
self.val = val
self.left = left
self.right = right


def flatten(root: Optional[TreeNode]) -> None:

Check failure on line 50 in data_structures/linked_list/flatten_binary_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (UP045)

data_structures/linked_list/flatten_binary_tree.py:50:19: UP045 Use `X | None` for type annotations
"""
Flatten the binary tree rooted at `root` into a right-skewed linked list
following preorder traversal. Modifies the tree in-place.

Args:
root (Optional[TreeNode]]): Root of the binary tree.

Returns:
None
"""
current = root
while current:
if current.left:
# Find rightmost node of left subtree (predecessor)
predecessor = current.left
while predecessor.right:
predecessor = predecessor.right

# Move current.right after predecessor
predecessor.right = current.right
# Make left subtree the new right subtree
current.right = current.left
current.left = None
current = current.right


def to_list(root: Optional[TreeNode]) -> List[int]:

Check failure on line 77 in data_structures/linked_list/flatten_binary_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (UP006)

data_structures/linked_list/flatten_binary_tree.py:77:42: UP006 Use `list` instead of `List` for type annotation

Check failure on line 77 in data_structures/linked_list/flatten_binary_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (UP045)

data_structures/linked_list/flatten_binary_tree.py:77:19: UP045 Use `X | None` for type annotations
"""
Helper to collect values from the flattened tree following right pointers.

Args:
root (Optional[TreeNode]): Root of the (flattened) tree.

Returns:
List[int]: Values in order along right pointers.
"""
result: List[int] = []

Check failure on line 87 in data_structures/linked_list/flatten_binary_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (UP006)

data_structures/linked_list/flatten_binary_tree.py:87:13: UP006 Use `list` instead of `List` for type annotation
node = root
while node:
result.append(node.val)
node = node.right
return result


if __name__ == "__main__":
# Run doctests when executed as a script.
# This is intended for local checks only. Formal tests should go in tests/.
failures, tests = doctest.testmod(report=False)
if failures:
print(f"Doctest failures: {failures} out of {tests} tests.")
else:
print(f"All {tests} doctests passed.")
Empty file.
83 changes: 83 additions & 0 deletions sorts/sleep_sort.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""
An implementation of Sleep Sort.

Description
-----------
Sleep Sort is a highly unconventional algorithm that leverages timing delays
to produce a sorted sequence. Each element in the array is assigned to a thread
that sleeps for a duration proportional to its value. As threads "wake up,"
they output numbers in increasing order.

This algorithm only works for non-negative integers and is non-deterministic
in real systems due to thread scheduling and timing inaccuracies.

Time complexity: O(n) expected (but unreliable in practice)
Space complexity: O(n) for thread management
"""

import threading
import time
from typing import List

Check failure on line 20 in sorts/sleep_sort.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (UP035)

sorts/sleep_sort.py:20:1: UP035 `typing.List` is deprecated, use `list` instead


def sleep_sort(arr: List[int]) -> List[int]:
"""
Sorts a list of non-negative integers using the Sleep Sort algorithm.

Parameters
----------
arr : List[int]
A list of non-negative integers to be sorted.

Returns
-------
List[int]
A new list containing the elements in sorted order.

Example
-------
>>> sleep_sort([3, 1, 2])
[1, 2, 3]
"""
if not arr:
return []

result: List[int] = []
threads = []

def _sleep_and_append(n: int) -> None:
"""Sleeps for n * 0.01 seconds and appends n to the result."""
time.sleep(n * 0.01)
result.append(n)

for num in arr:
if num < 0:
raise ValueError("Sleep Sort only supports non-negative integers.")
thread = threading.Thread(target=_sleep_and_append, args=(num,))
threads.append(thread)
thread.start()

for thread in threads:
thread.join()

return result


def _test() -> None:
"""Basic test cases for sleep_sort."""
test_cases = [
[],
[1],
[3, 1, 2],
[0, 0, 1],
[5, 3, 9, 1, 4],
]

for case in test_cases:
sorted_case = sorted(case)
assert sleep_sort(case) == sorted_case, f"Failed on {case}"
print("All tests passed.")


if __name__ == "__main__":
_test()
80 changes: 80 additions & 0 deletions sorts/three_way_merge_sort.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""
In traditional Merge Sort, the array is recursively divided into halves until we reach subarrays of size 1. In 3-way Merge Sort, the array is recursively divided into three parts, reducing the depth of recursion and potentially improving efficiency.
"""


def merge(arr, left, mid1, mid2, right):
# Sizes of three subarrays
size1 = mid1 - left + 1
size2 = mid2 - mid1
size3 = right - mid2

# Temporary arrays for three parts
left_arr = arr[left : left + size1]
mid_arr = arr[mid1 + 1 : mid1 + 1 + size2]
right_arr = arr[mid2 + 1 : mid2 + 1 + size3]

# Merge three sorted subarrays
i = j = k = 0
index = left

while i < size1 or j < size2 or k < size3:
min_value = float("inf")
min_idx = -1

# Find the smallest among the three current elements
if i < size1 and left_arr[i] < min_value:
min_value = left_arr[i]
min_idx = 0
if j < size2 and mid_arr[j] < min_value:
min_value = mid_arr[j]
min_idx = 1
if k < size3 and right_arr[k] < min_value:
min_value = right_arr[k]
min_idx = 2

# Place the smallest element in the merged array
if min_idx == 0:
arr[index] = left_arr[i]
i += 1
elif min_idx == 1:
arr[index] = mid_arr[j]
j += 1
else:
arr[index] = right_arr[k]
k += 1

index += 1


def three_way_merge_sort(arr, left, right):
# Base case: If single element, return
if left >= right:
return

# Finding two midpoints for 3-way split
mid1 = left + (right - left) // 3
mid2 = left + 2 * (right - left) // 3

# Recursively sort first third
three_way_merge_sort(arr, left, mid1)

# Recursively sort second third
three_way_merge_sort(arr, mid1 + 1, mid2)

# Recursively sort last third
three_way_merge_sort(arr, mid2 + 1, right)

# Merge the sorted parts
merge(arr, left, mid1, mid2, right)


if __name__ == "__main__":
# Input array
arr = [5, 2, 9, 1, 6, 3, 8, 4, 7]

# Calling 3-way merge sort function
three_way_merge_sort(arr, 0, len(arr) - 1)

# Printing the sorted array
print(*arr)