diff --git a/DSA/activity_selection.py b/DSA/activity_selection.py new file mode 100644 index 0000000..5c4018a --- /dev/null +++ b/DSA/activity_selection.py @@ -0,0 +1,57 @@ +""" +Activity Selection Problem - Greedy Algorithm Implementation + +The activity selection problem is a classic example of a greedy algorithm. +Given a set of activities with start and finish times, find the maximum +number of activities that can be performed by a single person, assuming +that a person can only work on a single activity at a time. +""" + +def activity_selection(start_times, finish_times): + """ + Find the maximum number of activities that can be performed. + + Args: + start_times: List of start times for each activity + finish_times: List of finish times for each activity + + Returns: + List of indices of selected activities + """ + # Create a list of activities with their start and finish times + activities = list(zip(range(len(start_times)), start_times, finish_times)) + + # Sort activities by finish time + activities.sort(key=lambda x: x[2]) + + # Select the first activity + selected = [activities[0][0]] + last_finish_time = activities[0][2] + + # Consider the rest of the activities + for i in range(1, len(activities)): + # If the start time of this activity is greater than or equal to + # the finish time of the previously selected activity, select it + if activities[i][1] >= last_finish_time: + selected.append(activities[i][0]) + last_finish_time = activities[i][2] + + return selected + +# Test cases +if __name__ == "__main__": + # Example 1 + start_times1 = [1, 3, 0, 5, 8, 5] + finish_times1 = [2, 4, 6, 7, 9, 9] + + selected1 = activity_selection(start_times1, finish_times1) + print("Selected activities (indices):", selected1) + print("Number of activities selected:", len(selected1)) + + # Example 2 + start_times2 = [10, 12, 20] + finish_times2 = [20, 25, 30] + + selected2 = activity_selection(start_times2, finish_times2) + print("\nSelected activities (indices):", selected2) + print("Number of activities selected:", len(selected2)) \ No newline at end of file diff --git a/DSA/binary_search.py b/DSA/binary_search.py new file mode 100644 index 0000000..dd1da79 --- /dev/null +++ b/DSA/binary_search.py @@ -0,0 +1,46 @@ +""" +Binary Search Algorithm + +Implementation of Binary Search algorithm to find an element in a sorted array. +Time complexity: O(log n) +""" + +def binary_search(arr, target): + """ + Search for target in a sorted array using binary search. + + Args: + arr: Sorted list of elements + target: Element to search for + + Returns: + Index of target if found, -1 otherwise + """ + left = 0 + right = len(arr) - 1 + + while left <= right: + mid = (left + right) // 2 + + if arr[mid] == target: + return mid + elif arr[mid] < target: + left = mid + 1 + else: + right = mid - 1 + + return -1 + +# Test cases +if __name__ == "__main__": + # Test case 1 + arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + target1 = 7 + print(f"Array: {arr1}, Target: {target1}") + print(f"Found at index: {binary_search(arr1, target1)}") + + # Test case 2 + arr2 = [1, 3, 5, 7, 9] + target2 = 4 + print(f"Array: {arr2}, Target: {target2}") + print(f"Found at index: {binary_search(arr2, target2)}") \ No newline at end of file diff --git a/DSA/fibonacci.py b/DSA/fibonacci.py new file mode 100644 index 0000000..2cdae6c --- /dev/null +++ b/DSA/fibonacci.py @@ -0,0 +1,106 @@ +""" +Fibonacci Sequence - Dynamic Programming Implementation + +This file demonstrates different approaches to calculate Fibonacci numbers: +1. Naive recursive approach (exponential time) +2. Memoization approach (top-down dynamic programming) +3. Tabulation approach (bottom-up dynamic programming) +""" + +def fibonacci_recursive(n): + """ + Calculate the nth Fibonacci number using naive recursion. + Time Complexity: O(2^n) + Space Complexity: O(n) for recursion stack + + Args: + n: Position in Fibonacci sequence (0-indexed) + + Returns: + The nth Fibonacci number + """ + if n <= 1: + return n + return fibonacci_recursive(n-1) + fibonacci_recursive(n-2) + +def fibonacci_memoization(n, memo=None): + """ + Calculate the nth Fibonacci number using memoization (top-down DP). + Time Complexity: O(n) + Space Complexity: O(n) + + Args: + n: Position in Fibonacci sequence (0-indexed) + memo: Dictionary to store already computed values + + Returns: + The nth Fibonacci number + """ + if memo is None: + memo = {} + + if n in memo: + return memo[n] + + if n <= 1: + return n + + memo[n] = fibonacci_memoization(n-1, memo) + fibonacci_memoization(n-2, memo) + return memo[n] + +def fibonacci_tabulation(n): + """ + Calculate the nth Fibonacci number using tabulation (bottom-up DP). + Time Complexity: O(n) + Space Complexity: O(n) + + Args: + n: Position in Fibonacci sequence (0-indexed) + + Returns: + The nth Fibonacci number + """ + if n <= 1: + return n + + # Initialize table + dp = [0] * (n + 1) + dp[1] = 1 + + # Fill the table + for i in range(2, n + 1): + dp[i] = dp[i-1] + dp[i-2] + + return dp[n] + +def fibonacci_optimized(n): + """ + Calculate the nth Fibonacci number using optimized space. + Time Complexity: O(n) + Space Complexity: O(1) + + Args: + n: Position in Fibonacci sequence (0-indexed) + + Returns: + The nth Fibonacci number + """ + if n <= 1: + return n + + a, b = 0, 1 + for _ in range(2, n + 1): + a, b = b, a + b + + return b + +# Test cases +if __name__ == "__main__": + n = 10 # 10th Fibonacci number + + print(f"Fibonacci({n}) using recursive approach:", fibonacci_recursive(n)) + print(f"Fibonacci({n}) using memoization:", fibonacci_memoization(n)) + print(f"Fibonacci({n}) using tabulation:", fibonacci_tabulation(n)) + print(f"Fibonacci({n}) using optimized approach:", fibonacci_optimized(n)) + + # Expected output for n=10: 55 \ No newline at end of file diff --git a/DSA/graph_bfs.py b/DSA/graph_bfs.py new file mode 100644 index 0000000..3b9b6ec --- /dev/null +++ b/DSA/graph_bfs.py @@ -0,0 +1,103 @@ +""" +Graph Breadth-First Search (BFS) + +Implementation of Breadth-First Search algorithm for graph traversal. +""" + +from collections import deque + +def bfs(graph, start): + """ + Perform breadth-first search on a graph. + + Args: + graph: Dictionary representing adjacency list of the graph + start: Starting vertex + + Returns: + List of vertices in BFS order + """ + # Initialize visited set and result list + visited = set([start]) + result = [start] + + # Initialize queue with start vertex + queue = deque([start]) + + # Process vertices in queue + while queue: + # Dequeue a vertex + vertex = queue.popleft() + + # Process all neighbors + for neighbor in graph[vertex]: + if neighbor not in visited: + visited.add(neighbor) + result.append(neighbor) + queue.append(neighbor) + + return result + +def shortest_path_bfs(graph, start, end): + """ + Find shortest path between start and end vertices using BFS. + + Args: + graph: Dictionary representing adjacency list of the graph + start: Starting vertex + end: Ending vertex + + Returns: + Shortest path as a list of vertices, or None if no path exists + """ + # Check for edge cases + if start == end: + return [start] + + # Keep track of visited vertices and their parents + visited = {start: None} + queue = deque([start]) + + # BFS traversal + while queue: + vertex = queue.popleft() + + # Check all neighbors + for neighbor in graph[vertex]: + if neighbor not in visited: + visited[neighbor] = vertex + queue.append(neighbor) + + # If we found the end vertex, reconstruct the path + if neighbor == end: + path = [end] + while path[-1] != start: + path.append(visited[path[-1]]) + return path[::-1] # Reverse to get path from start to end + + # No path found + return None + +# Test cases +if __name__ == "__main__": + # Example graph represented as an adjacency list + graph = { + 'A': ['B', 'C'], + 'B': ['A', 'D', 'E'], + 'C': ['A', 'F'], + 'D': ['B'], + 'E': ['B', 'F'], + 'F': ['C', 'E'] + } + + print("BFS starting from vertex 'A':", bfs(graph, 'A')) + + # Test shortest path + start_vertex = 'A' + end_vertex = 'F' + path = shortest_path_bfs(graph, start_vertex, end_vertex) + + if path: + print(f"Shortest path from {start_vertex} to {end_vertex}:", ' -> '.join(path)) + else: + print(f"No path exists from {start_vertex} to {end_vertex}") \ No newline at end of file diff --git a/DSA/temp.txt b/DSA/temp.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/DSA/temp.txt @@ -0,0 +1 @@ +