diff --git a/data_structures/binary_tree/floor_ceil_in_bst.py b/data_structures/binary_tree/floor_ceil_in_bst.py new file mode 100644 index 000000000000..18417a83fcc2 --- /dev/null +++ b/data_structures/binary_tree/floor_ceil_in_bst.py @@ -0,0 +1,82 @@ +""" +The floor of a key 'k' in a BST is the maximum +value that is smaller than or equal to 'k'. + +The ceiling of a key 'k' in a BST is the minimum +value that is greater than or equal to 'k'. + +Reference: +https://bit.ly/46uB0a2 + +Author : Arunkumar +Date : 14th October 2023 +""" + + +class TreeNode: + def __init__(self, key: int) -> None: + """ + Initialize a TreeNode with the given key. + + Args: + key (int): The key value for the node. + """ + self.key = key + self.left: TreeNode | None = None + self.right: TreeNode | None = None + + +def floor_ceiling(root: TreeNode | None, key: int) -> tuple[int | None, int | None]: + """ + Find the floor and ceiling values for a given key in a Binary Search Tree (BST). + + Args: + root (TreeNode): The root of the BST. + key (int): The key for which to find the floor and ceiling. + + Returns: + tuple[int | None, int | None]: + A tuple containing the floor and ceiling values, respectively. + + Examples: + >>> root = TreeNode(10) + >>> root.left = TreeNode(5) + >>> root.right = TreeNode(20) + >>> root.left.left = TreeNode(3) + >>> root.left.right = TreeNode(7) + >>> root.right.left = TreeNode(15) + >>> root.right.right = TreeNode(25) + >>> floor, ceiling = floor_ceiling(root, 8) + >>> floor + 7 + >>> ceiling + 10 + >>> floor, ceiling = floor_ceiling(root, 14) + >>> floor + 10 + >>> ceiling + 15 + """ + floor_val = None + ceiling_val = None + + while root is not None: + if root.key == key: + floor_val = root.key + ceiling_val = root.key + break + + if key < root.key: + ceiling_val = root.key + root = root.left + else: + floor_val = root.key + root = root.right + + return floor_val, ceiling_val + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/dynamic_programming/longest_increasing_subsequence.py b/dynamic_programming/longest_increasing_subsequence.py index d827893763c5..629799d4a46b 100644 --- a/dynamic_programming/longest_increasing_subsequence.py +++ b/dynamic_programming/longest_increasing_subsequence.py @@ -1,5 +1,6 @@ """ Author : Mehdi ALAOUI +Optimized : Arunkumar This is a pure Python implementation of Dynamic Programming solution to the longest increasing subsequence of a given sequence. @@ -13,46 +14,54 @@ from __future__ import annotations -def longest_subsequence(array: list[int]) -> list[int]: # This function is recursive +def longest_subsequence(array: list[int]) -> list[int]: """ - Some examples + Find the longest increasing subsequence in the given array + using dynamic programming. + + Args: + array (list[int]): The input array. + + Returns: + list[int]: The longest increasing subsequence. + + Examples: >>> longest_subsequence([10, 22, 9, 33, 21, 50, 41, 60, 80]) [10, 22, 33, 41, 60, 80] >>> longest_subsequence([4, 8, 7, 5, 1, 12, 2, 3, 9]) [1, 2, 3, 9] >>> longest_subsequence([9, 8, 7, 6, 5, 7]) - [8] + [5, 7] >>> longest_subsequence([1, 1, 1]) - [1, 1, 1] + [1] >>> longest_subsequence([]) [] """ - array_length = len(array) - # If the array contains only one element, we return it (it's the stop condition of - # recursion) - if array_length <= 1: - return array - # Else - pivot = array[0] - is_found = False - i = 1 - longest_subseq: list[int] = [] - while not is_found and i < array_length: - if array[i] < pivot: - is_found = True - temp_array = [element for element in array[i:] if element >= array[i]] - temp_array = longest_subsequence(temp_array) - if len(temp_array) > len(longest_subseq): - longest_subseq = temp_array - else: - i += 1 - - temp_array = [element for element in array[1:] if element >= pivot] - temp_array = [pivot, *longest_subsequence(temp_array)] - if len(temp_array) > len(longest_subseq): - return temp_array - else: - return longest_subseq + if not array: + return [] + + n = len(array) + # Initialize an array to store the length of the longest + # increasing subsequence ending at each position. + lis_lengths = [1] * n + + for i in range(1, n): + for j in range(i): + if array[i] > array[j]: + lis_lengths[i] = max(lis_lengths[i], lis_lengths[j] + 1) + + # Find the maximum length of the increasing subsequence. + max_length = max(lis_lengths) + + # Reconstruct the longest subsequence in reverse order. + subsequence = [] + current_length = max_length + for i in range(n - 1, -1, -1): + if lis_lengths[i] == current_length: + subsequence.append(array[i]) + current_length -= 1 + + return subsequence[::-1] # Reverse the subsequence to get the correct order. if __name__ == "__main__": diff --git a/scheduling/shortest_deadline_first.py b/scheduling/shortest_deadline_first.py new file mode 100644 index 000000000000..6201625c7374 --- /dev/null +++ b/scheduling/shortest_deadline_first.py @@ -0,0 +1,70 @@ +""" +Earliest Deadline First (EDF) Scheduling Algorithm + +This code implements the Earliest Deadline First (EDF) +scheduling algorithm, which schedules processes based on their deadlines. +If a process cannot meet its deadline, it is marked as "Idle." + +Reference: +https://www.geeksforgeeks.org/ +earliest-deadline-first-edf-cpu-scheduling-algorithm/ + +Author: Arunkumar +Date: 14th October 2023 +""" + + +def earliest_deadline_first_scheduling( + processes: list[tuple[str, int, int, int]] +) -> list[str]: + """ + Perform Earliest Deadline First (EDF) scheduling. + + Args: + processes (List[Tuple[str, int, int, int]]): A list of + processes with their names, + arrival times, deadlines, and execution times. + + Returns: + List[str]: A list of process names in the order they are executed. + + Examples: + >>> processes = [("A", 1, 5, 2), ("B", 2, 8, 3), ("C", 3, 4, 1)] + >>> execution_order = earliest_deadline_first_scheduling(processes) + >>> execution_order + ['Idle', 'A', 'C', 'B'] + + """ + result = [] + current_time = 0 + + while processes: + available_processes = [ + process for process in processes if process[1] <= current_time + ] + + if not available_processes: + result.append("Idle") + current_time += 1 + else: + next_process = min( + available_processes, key=lambda tuple_values: tuple_values[2] + ) + name, _, deadline, execution_time = next_process + + if current_time + execution_time <= deadline: + result.append(name) + current_time += execution_time + processes.remove(next_process) + else: + result.append("Idle") + current_time += 1 + + return result + + +if __name__ == "__main__": + processes = [("A", 1, 5, 2), ("B", 2, 8, 3), ("C", 3, 4, 1)] + execution_order = earliest_deadline_first_scheduling(processes) + for i, process in enumerate(execution_order): + print(f"Time {i}: Executing process {process}")