diff --git a/Heaps/Hard/MaxHeapCRUD.java b/Heaps/Hard/MaxHeapCRUD.java new file mode 100644 index 00000000..33cc206e --- /dev/null +++ b/Heaps/Hard/MaxHeapCRUD.java @@ -0,0 +1,144 @@ +import java.util.ArrayList; +import java.util.InputMismatchException; +import java.util.List; +import java.util.Scanner; + +// MaxHeapCRUD class representing a max heap with CRUD operations +public class MaxHeapCRUD { + private List heap; + + // Constructor initializes an empty list for the heap + public MaxHeapCRUD() { + this.heap = new ArrayList<>(); + } + + // Move the element up the heap until the heap property is restored + private void percolateUp(int index) { + while (index > 0) { + int parent = (index - 1) / 2; + if (heap.get(parent) >= heap.get(index)) { + break; + } + // Swap the current element with its parent + int temp = heap.get(parent); + heap.set(parent, heap.get(index)); + heap.set(index, temp); + index = parent; + } + } + + // Ensure the heap property is maintained starting from the given index + private void maxHeapify(int index) { + int left = 2 * index + 1; + int right = 2 * index + 2; + int largest = index; + + // Check if the left child is larger than the current largest element + if (left < heap.size() && heap.get(left) > heap.get(largest)) { + largest = left; + } + // Check if the right child is larger than the current largest element + if (right < heap.size() && heap.get(right) > heap.get(largest)) { + largest = right; + } + + // If the largest element is not the current index, swap and continue heapifying + if (largest != index) { + int temp = heap.get(largest); + heap.set(largest, heap.get(index)); + heap.set(index, temp); + maxHeapify(largest); + } + } + + // Add a new element to the heap and percolate it up to maintain the heap property + public void insert(int val) { + heap.add(val); + percolateUp(heap.size() - 1); + } + + // Return the maximum element in the heap (root of the heap) + public int getMax() { + if (heap.isEmpty()) { + throw new RuntimeException("Heap is empty"); + } + return heap.get(0); + } + + // Delete the element at the specified index from the heap + public void delete(int index) { + if (index < 0 || index >= heap.size()) { + throw new RuntimeException("Invalid index"); + } + // Swap the element with the last element + heap.set(index, heap.get(heap.size() - 1)); + heap.remove(heap.size() - 1); + // Max heapify to maintain the heap property + maxHeapify(index); + } + + // Print all values in the heap + public void printHeap() { + for (int i : heap) { + System.out.print(i + " "); + } + System.out.println(); + } + + // Main method demonstrating usage of the MaxHeapCRUD class + public static void main(String[] args) { + MaxHeapCRUD heap = new MaxHeapCRUD(); + + // Insert, delete, fetch, and print elements into/from the heap using user input + try (Scanner scanner = new Scanner(System.in)) { + while (true) { + System.out.println("1. Insert element"); + System.out.println("2. Get max element"); + System.out.println("3. Delete element at index"); + System.out.println("4. Print heap"); + System.out.println("5. Exit"); + System.out.print("Enter your choice: "); + + try { + int choice = scanner.nextInt(); + switch (choice) { + case 1: + System.out.print("Enter an integer to insert into the heap: "); + heap.insert(scanner.nextInt()); + break; + case 2: + try { + System.out.println("Max Value: " + heap.getMax()); + } catch (RuntimeException e) { + System.err.println(e.getMessage()); + } + break; + case 3: + System.out.print("Enter the index to delete: "); + int deleteIndex = scanner.nextInt(); + try { + heap.delete(deleteIndex); + System.out.println("Element at index " + deleteIndex + " deleted"); + } catch (RuntimeException e) { + System.err.println(e.getMessage()); + } + break; + case 4: + System.out.print("All values in heap: "); + heap.printHeap(); + break; + case 5: + System.out.println("Exiting..."); + System.exit(0); + default: + System.out.println("Invalid choice. Please enter a number between 1 and 5."); + } + } catch (InputMismatchException e) { + System.err.println("Invalid input. Please enter a number."); + // Clear the scanner buffer to avoid an infinite loop on invalid input + scanner.next(); + } + } + } + } +} diff --git a/Heaps/Hard/Readme.md b/Heaps/Hard/Readme.md new file mode 100644 index 00000000..233b5857 --- /dev/null +++ b/Heaps/Hard/Readme.md @@ -0,0 +1,594 @@ +# Exploring Max-Heap using CRUD operations : + +## Introduction to Heaps: + +Heaps are a fundamental data structure used in computer science for efficient implementation of priority queues and various graph algorithms. A heap is a specialized tree-based structure that satisfies the heap property. + +A binary heap is a complete binary tree where every parent node has a value greater than or equal to its children. This structure allows for quick retrieval and removal of the maximum element, making it an ideal choice for priority queue implementations. + +## Introduction to Max-Heap CRUD Operations: + +In the context of heaps, CRUD operations refer to Create, Read, Update, and Delete. Max-Heap CRUD operations involve manipulating a max heap, ensuring that it maintains the heap property at all times. + +Let's dive into each CRUD operation: + +### Overview of Max-Heap CRUD Operations: + +1. **Create (Insertion):** + - **Description:** Adds a new element to the max heap while maintaining the heap property. + - **Implementation:** The new element is appended to the end of the heap, and then the `percolate_up` method is called to ensure that the heap property is restored. + +2. **Read (Get Maximum):** + - **Description:** Returns the maximum element in the max heap, which is always the root. + - **Implementation:** Simply retrieves the element at the root of the heap. + +3. **Update (Modify Element):** + - **Description:** Updates an existing value in the max heap while preserving the heap property. + - **Implementation:** Finds the index of the old value, updates it with the new value, and then calls `percolate_up` and `max_heapify` to maintain the heap property. + +4. **Delete (Remove Maximum):** + - **Description:** Removes the maximum element (root) from the max heap while maintaining the heap property. + - **Implementation:** Replaces the root with the last element, pops the last element, and then calls `max_heapify` to reorder the heap. + +These CRUD operations provide a comprehensive set of functionalities for working with a max heap. They ensure that the heap property is consistently upheld, allowing for efficient management of data in various applications. + + +# PYTHON +# code + +```python +# Copyrights to venkys.io +# For more information, visit https://venkys.io + +# python program for Max-Heap CRUD +# Stable: Yes +# Inplace: Yes +# Adaptive: Yes + +# Space complexity:O(n) +# Time complexity: +# Insertion: O(log N) +# Get Max: O(1) +# Update: O(log N) +# Delete Max: O(log N) + +class MaxHeap: + def __init__(self): + # Initialize an empty list to represent the heap + self.heap = [] + + def percolate_up(self, index): + # Move the element up the heap until the heap property is restored + while index > 0: + parent = (index - 1) // 2 + if self.heap[parent] >= self.heap[index]: + break + # Swap the current element with its parent + self.heap[parent], self.heap[index] = self.heap[index], self.heap[parent] + index = parent + + def max_heapify(self, index): + # Ensure the heap property is maintained starting from the given index + left = 2 * index + 1 + right = 2 * index + 2 + largest = index + + # Check if the left child is larger than the current largest element + if left < len(self.heap) and self.heap[left] > self.heap[largest]: + largest = left + # Check if the right child is larger than the current largest element + if right < len(self.heap) and self.heap[right] > self.heap[largest]: + largest = right + + # If the largest element is not the current index, swap and continue heapifying + if largest != index: + self.heap[largest], self.heap[index] = self.heap[index], self.heap[largest] + self.max_heapify(largest) + + def insert(self, val): + # Add a new element to the heap and percolate it up to maintain the heap property + self.heap.append(val) + self.percolate_up(len(self.heap) - 1) + + def get_max(self): + # Return the maximum element in the heap (root of the heap) + if len(self.heap) == 0: + raise Exception("Heap is empty") + return self.heap[0] + + def update(self, old_val, new_val): + try: + # Find the index of the old value in the heap + index = self.heap.index(old_val) + # Update the value, percolate up, and max heapify to maintain the heap property + self.heap[index] = new_val + self.percolate_up(index) + self.max_heapify(index) + except ValueError: + print("Value not in heap") + + def delete_max(self): + if len(self.heap) == 0: + raise Exception("Heap is empty") + # Replace the root with the last element, pop the last element, and max heapify + self.heap[0] = self.heap.pop() + self.max_heapify(0) + + def print_heap(self): + # Print all values in the heap + print(" ".join(map(str, self.heap))) + + +def main(): + # Initialize max heap + heap = MaxHeap() + + # Insert elements into heap + heap.insert(12) + heap.insert(10) + heap.insert(-10) + heap.insert(100) + + # Print all values in heap + heap.print_heap() + + # Get max value in heap + print("Max Value:", heap.get_max()) + + # Update value in heap + heap.update(12, 5) + print("Max Value after update:", heap.get_max()) + + # Delete max value from heap + heap.delete_max() + print("Max Value after deletion:", heap.get_max()) + + # Print all values in heap after deletion + heap.print_heap() + +``` +# Code Explanation + +The provided code implements a MaxHeap class in Python, allowing users to perform CRUD operations (Create, Read, Update, Delete) on a max heap data structure. Let's break down the code and provide a clear explanation: + + +# Step 1 - Initialization: +``` + self.heap = [] +``` +The `MaxHeap` class is initialized with an empty list, `self.heap`, which will store the elements of the max heap. + +# Step 2 - Percolate Up: +```python +def percolate_up(self, index): +``` +The `percolate_up` method is used during insertion to move a newly added element up the heap until the heap property is restored. It compares the element with its parent and swaps them if needed. + +# Step 3 - Max Heapify: + +```python +def max_heapify(self, index): +``` +The `max_heapify` method is used to maintain the heap property from a given index. It compares the element with its left and right children, swaps with the largest child if needed, and recursively continues the process. + +# Step 4 - Insertion: + +```python +def insert(self, val): +``` +The `insert` method adds a new element to the heap and then calls `percolate_up` to ensure the heap property is maintained. + +# Step 5 - Get Max: + +```python +def get_max(self): +``` +The `get_max` method returns the maximum element in the heap, which is the root. + +# Step 6 - Update: + +```python +def update(self, old_val, new_val): +``` +The `update` method updates an existing value in the heap. It finds the index, updates the value, and then calls `percolate_up` and `max_heapify` to maintain the heap property. + +# Step 7 - Delete Max: + +```python +def delete_max(self): +``` +The `delete_max` method removes the maximum element (root) from the heap. It replaces the root with the last element, pops the last element, and then calls `max_heapify` to reorder the heap. + +# Step 8 - Print Heap: + +```python +def print_heap(self): +``` +The `print_heap` method prints all values in the heap. + +# Step 9 - Main Function: + +```python +def main(): + # Initialize max heap + heap = MaxHeap() +``` +The `main` function demonstrates the usage of the `MaxHeap` class by initializing a heap, performing insertions, updates, deletions, and printing the heap at different stages. + +This implementation provides a clear and organized way to work with Max-Heaps in Python. + +# C++ +# code + +```C++ +/* Copyrights to venkys.io +For more information, visit https://venkys.io */ + +// C++ program for Max_Heap CRUD +// Stable: No +// Inplace: Yes +// Adaptive: No + +// Space complexity: O(N) +// Time complexity: +// Insertion: O(log N) +// Get Max: O(1) +// Update: O(log N) +// Delete Max: O(log N) + +#include +#include +#include + +class MaxHeap { +private: + std::vector heap; + + // Move the element up the heap until the heap property is restored + void percolateUp(int index) { + while (index > 0) { + int parent = (index - 1) / 2; + if (heap[parent] >= heap[index]) { + break; + } + std::swap(heap[parent], heap[index]); + index = parent; + } + } + + // Ensure the heap property is maintained starting from the given index + void maxHeapify(int index) { + int left = 2 * index + 1; + int right = 2 * index + 2; + int largest = index; + + // Check if the left child is larger than the current largest element + if (left < heap.size() && heap[left] > heap[largest]) { + largest = left; + } + // Check if the right child is larger than the current largest element + if (right < heap.size() && heap[right] > heap[largest]) { + largest = right; + } + + // If the largest element is not the current index, swap and continue heapifying + if (largest != index) { + std::swap(heap[largest], heap[index]); + maxHeapify(largest); + } + } + +public: + // Add a new element to the heap and percolate it up to maintain the heap property + void insert(int val) { + heap.push_back(val); + percolateUp(heap.size() - 1); + } + + // Return the maximum element in the heap (root of the heap) + int getMax() { + if (heap.size() == 0) { + throw "Heap is empty"; + } + return heap[0]; + } + + // Update the value, percolate up, and max heapify to maintain the heap property + void update(int old_val, int new_val) { + auto it = std::find(heap.begin(), heap.end(), old_val); + if (it != heap.end()) { + *it = new_val; + percolateUp(it - heap.begin()); + maxHeapify(it - heap.begin()); + } else { + std::cout << "Value not in heap" << std::endl; + } + } + + // Replace the root with the last element, pop the last element, and max heapify + void deleteMax() { + if (heap.size() == 0) { + throw "Heap is empty"; + } + heap[0] = heap.back(); + heap.pop_back(); + maxHeapify(0); + } + + // Print all values in the heap + void printHeap() { + for (int i : heap) { + std::cout << i << " "; + } + std::cout << std::endl; + } +}; + +int main() { + // Example usage of the MaxHeap class + MaxHeap heap; + + heap.insert(12); + heap.insert(10); + heap.insert(-10); + heap.insert(100); + + std::cout << "All values in heap: "; + heap.printHeap(); + + std::cout << "Max Value: " << heap.getMax() << std::endl; + + heap.update(12, 5); + std::cout << "Max Value after update: " << heap.getMax() << std::endl; + + heap.deleteMax(); + std::cout << "Max Value after deletion: " << heap.getMax() << std::endl; + + std::cout << "All values in heap: "; + heap.printHeap(); + + return 0; +} +``` +# java +# code + +```java +/* Copyrights to venkys.io +For more information, visit https://venkys.io */ + +// java program for Max-Heap CRUD +// Stable: No +// Inplace: Yes +// Adaptive: No + +// Space complexity: O(N) +// Time complexity: + // Insertion: O(log N) + // Get Max: O(1) + // Update: O(log N) + // Delete Max: O(log N) + +import java.util.ArrayList; +import java.util.List; + +// Class representing a max heap with CRUD operations +class MaxHeapCRUD { + private List heap; + + // Constructor initializes an empty list for the heap + public MaxHeapCRUD() { + this.heap = new ArrayList<>(); + } + + // Move the element up the heap until the heap property is restored + private void percolateUp(int index) { + while (index > 0) { + int parent = (index - 1) / 2; + if (heap.get(parent) >= heap.get(index)) { + break; + } + // Swap the current element with its parent + int temp = heap.get(parent); + heap.set(parent, heap.get(index)); + heap.set(index, temp); + index = parent; + } + } + + // Ensure the heap property is maintained starting from the given index + private void maxHeapify(int index) { + int left = 2 * index + 1; + int right = 2 * index + 2; + int largest = index; + + // Check if the left child is larger than the current largest element + if (left < heap.size() && heap.get(left) > heap.get(largest)) { + largest = left; + } + // Check if the right child is larger than the current largest element + if (right < heap.size() && heap.get(right) > heap.get(largest)) { + largest = right; + } + + // If the largest element is not the current index, swap and continue heapifying + if (largest != index) { + int temp = heap.get(largest); + heap.set(largest, heap.get(index)); + heap.set(index, temp); + maxHeapify(largest); + } + } + + // Add a new element to the heap and percolate it up to maintain the heap property + public void insert(int val) { + heap.add(val); + percolateUp(heap.size() - 1); + } + + // Return the maximum element in the heap (root of the heap) + public int getMax() { + if (heap.size() == 0) { + throw new RuntimeException("Heap is empty"); + } + return heap.get(0); + } + + // Update the value, percolate up, and max heapify to maintain the heap property + public void update(int oldVal, int newVal) { + int index = heap.indexOf(oldVal); + if (index != -1) { + heap.set(index, newVal); + percolateUp(index); + maxHeapify(index); + } else { + System.out.println("Value not in heap"); + } + } + + // Replace the root with the last element, pop the last element, and max heapify + public void deleteMax() { + if (heap.size() == 0) { + throw new RuntimeException("Heap is empty"); + } + heap.set(0, heap.remove(heap.size() - 1)); + maxHeapify(0); + } + + // Print all values in the heap + public void printHeap() { + for (int i : heap) { + System.out.print(i + " "); + } + System.out.println(); + } + + // Main method demonstrating usage of the MaxHeapCRUD class + public static void main(String[] args) { + MaxHeapCRUD heap = new MaxHeapCRUD(); + + heap.insert(12); + heap.insert(10); + heap.insert(52); + heap.insert(100); + heap.insert(50); + + System.out.print("All values in heap: "); + heap.printHeap(); + + System.out.println("Max Value: " + heap.getMax()); + + heap.update(12, 5); + + System.out.println("Max Value after update: " + heap.getMax()); + + heap.deleteMax(); + + System.out.println("Max Value after deletion: " + heap.getMax()); + + System.out.print("All values in heap: "); + heap.printHeap(); + } +} +``` +# Code Explanation + Step by step explanation for Java and C++ : + +1. **Class Definition (MaxHeap):** + - The code defines a class named `MaxHeap` representing a Max Heap data structure. + - Private member for c++: `std::vector heap` is used to store the elements of the heap. + -It uses a private list (heap) to store the elements of the heap in java. + - The class has a constructor in java (MaxHeapCRUD()) that initializes an empty list for the heap. + + +2. **percolateUp Function:** + - Private method `percolateUp` moves an element up the heap until the heap property is restored. + - It takes an index as a parameter and swaps the element with its parent until the heap property is satisfied. + +3. **maxHeapify Function:** + - Private method `maxHeapify` ensures the heap property is maintained starting from a given index. + - It compares the element with its left and right children, swapping it with the largest child if needed. + +4. **insert Function:** + - Public method `insert` adds a new element to the heap and ensures the heap property by calling `percolateUp`. + +5. **getMax Function:** + - Public method `getMax` returns the maximum element in the heap (the root). + +6. **update Function:** + - Public method `update` updates a specified element in the heap with a new value. + - It uses `std::find` to locate the element, updates it, and then calls `percolateUp` and `maxHeapify` to maintain the heap property. + +7. **deleteMax Function:** + - Public method `deleteMax` removes the maximum element (root) from the heap. + - It replaces the root with the last element, pops the last element, and calls `maxHeapify` to restore the heap property. + +8. **printHeap Function:** + - Public method `printHeap` prints all values in the heap. + +9. **main Function:** + - In the `main` function: + - An instance of `MaxHeap` named `heap` is created in c++.An instance of MaxHeapCRUD named heap is created in java + - Elements (12, 10, -10, 100,50) are inserted into the heap using `insert`. + - All values in the heap are printed using `printHeap`. + - The maximum value in the heap is printed using `getMax`. + - An update is performed, changing the value 12 to 5 using `update`. + - The maximum value after the update is printed. + - The maximum element is deleted using `deleteMax`. + - The maximum value after deletion is printed. + - Finally, all values in the heap after deletion are printed. + +This code demonstrates the basic operations (insertion, update, deletion) on a Max Heap. + + + +**Time and Space Complexity Analysis**: + +# Time Complexity: +The time complexities for the Max Heap CRUD (Create, Read, Update, Delete) operations are as follows: + +1. **Insertion (Create):** + - Time Complexity: O(log n) + - Explanation: The worst-case time complexity for inserting an element into a max heap is logarithmic in the number of elements, as the element needs to be percolated up the height of the heap. The height of a binary heap is log(n) where n is the number of elements. + +2. **GetMax (Read):** + - Time Complexity: O(1) + - Explanation: Retrieving the maximum element (root) in a max heap can be done in constant time as the maximum element is always at the root. + +3. **Update:** + - Time Complexity: O(log n) + - Explanation: Updating an element in a max heap involves two operations: percolating up (up to log n steps) and max heapifying (up to log n steps). Therefore, the overall time complexity is logarithmic. + +4. **DeleteMax (Delete):** + - Time Complexity: O(log n) + - Explanation: Deleting the maximum element involves replacing the root with the last element, which may require percolating down the height of the heap to maintain the heap property. The worst-case time complexity is logarithmic. + +These time complexities assume that the underlying data structure is a binary heap. The actual performance may vary based on implementation details and the specific operations performed. + +# Space Complexity: +The space complexity for Max Heap CRUD (Create, Read, Update, Delete) operations is O(n), where n is the number of elements in the heap. + +1. **Insertion (Create):** + - Explanation: The insertion operation generally doesn't require additional space proportional to the number of elements. The new element is added to the existing heap in-place. + +2. **GetMax (Read):** + - Explanation: Reading the maximum element involves accessing the root of the heap. It doesn't require additional space proportional to the number of elements. + +3. **Update:** + - Explanation: The update operation is performed in-place without requiring additional space. + +4. **DeleteMax (Delete):** + - Explanation: The delete operation involves replacing the root with the last element and then adjusting the heap structure. It is performed in-place without requiring additional space. + +# Real-World Applications of MAX-HEAP CRUD(Create,Read,Update,Delete) + +1. **Task Scheduling:** Priority scheduling based on task priority levels. +2. **Job Scheduling:** Allocating jobs to machines based on priority. +3. **Dijkstra's Algorithm:** Finding the shortest path in a graph. +4. **Huffman Coding:** Data compression algorithm. +5. **Order Processing:** Managing orders based on their priority in e-commerce systems. +6. **Operating System Task Scheduling:** Assigning priority to tasks for execution. +7. **Network Routing Algorithms:** Determining the optimal path for data packets. +8. **Emergency Room Triage:** Prioritizing patients based on the severity of their condition. +9. **Database Indexing:** Managing indexes to speed up query performance. +10. **Wireless Sensor Networks:** Energy-efficient data aggregation in sensor networks. diff --git a/Heaps/Hard/max heap CRUD.py b/Heaps/Hard/max heap CRUD.py new file mode 100644 index 00000000..8b2d4d09 --- /dev/null +++ b/Heaps/Hard/max heap CRUD.py @@ -0,0 +1,107 @@ +class MaxHeap: + def __init__(self): + # Initialize an empty list to represent the heap + self.heap = [] + + def percolate_up(self, index): + # Move the element up the heap until the heap property is restored + while index > 0: + parent = (index - 1) // 2 + if self.heap[parent] >= self.heap[index]: + break + # Swap the current element with its parent + self.heap[parent], self.heap[index] = self.heap[index], self.heap[parent] + index = parent + + def max_heapify(self, index): + # Ensure the heap property is maintained starting from the given index + left = 2 * index + 1 + right = 2 * index + 2 + largest = index + + # Check if the left child is larger than the current largest element + if left < len(self.heap) and self.heap[left] > self.heap[largest]: + largest = left + # Check if the right child is larger than the current largest element + if right < len(self.heap) and self.heap[right] > self.heap[largest]: + largest = right + + # If the largest element is not the current index, swap and continue heapifying + if largest != index: + self.heap[largest], self.heap[index] = self.heap[index], self.heap[largest] + self.max_heapify(largest) + + def insert(self, val): + # Add a new element to the heap and percolate it up to maintain the heap property + self.heap.append(val) + self.percolate_up(len(self.heap) - 1) + + def get_max(self): + # Return the maximum element in the heap (root of the heap) + if len(self.heap) == 0: + raise Exception("Heap is empty") + return self.heap[0] + + def update(self, old_val, new_val): + try: + # Find the index of the old value in the heap + index = self.heap.index(old_val) + # Update the value, percolate up, and max heapify to maintain the heap property + self.heap[index] = new_val + self.percolate_up(index) + self.max_heapify(index) + except ValueError: + print("Value not in heap") + + def delete_max(self): + if len(self.heap) == 0: + raise Exception("Heap is empty") + # Replace the root with the last element, pop the last element, and max heapify + self.heap[0] = self.heap.pop() + self.max_heapify(0) + + def print_heap(self): + # Print all values in the heap + print(" ".join(map(str, self.heap))) + +def main(): + # Initialize max heap + heap = MaxHeap() + + # Insert elements into heap + try: + while True: + value = int(input("Enter an integer to insert into the heap (enter a non-integer to stop): ")) + heap.insert(value) + heap.print_heap() + except ValueError: + pass # Continue to the next step when a non-integer is entered + + # Print all values in heap + print("Heap after insertion:") + heap.print_heap() + + # Get max value in heap + print("Max Value:", heap.get_max()) + + # Update value in heap + try: + old_value = int(input("Enter the value to update: ")) + new_value = int(input("Enter the new value: ")) + heap.update(old_value, new_value) + print("Max Value after update:", heap.get_max()) + except ValueError: + print("Invalid input. Please enter integers.") + + # Delete max value from heap + try: + heap.delete_max() + print("Max Value after deletion:", heap.get_max()) + except Exception as e: + print(e) + + # Print all values in heap after deletion + heap.print_heap() + +if __name__ == "__main__": + main() diff --git a/Heaps/Hard/max_heap_CRUD.c++ b/Heaps/Hard/max_heap_CRUD.c++ new file mode 100644 index 00000000..0e6af123 --- /dev/null +++ b/Heaps/Hard/max_heap_CRUD.c++ @@ -0,0 +1,142 @@ +#include +#include +#include + +class MaxHeap { +private: + std::vector heap; + + // Move the element up the heap until the heap property is restored + void percolateUp(int index) { + while (index > 0) { + int parent = (index - 1) / 2; + if (heap[parent] >= heap[index]) { + break; + } + std::swap(heap[parent], heap[index]); + index = parent; + } + } + + // Ensure the heap property is maintained starting from the given index + void maxHeapify(int index) { + int left = 2 * index + 1; + int right = 2 * index + 2; + int largest = index; + + // Check if the left child is larger than the current largest element + if (left < static_cast(heap.size()) && heap[left] > heap[largest]) { + largest = left; + } + // Check if the right child is larger than the current largest element + if (right < static_cast(heap.size()) && heap[right] > heap[largest]) { + largest = right; + } + + // If the largest element is not the current index, swap and continue heapifying + if (largest != index) { + std::swap(heap[largest], heap[index]); + maxHeapify(largest); + } + } + +public: + // Add a new element to the heap and percolate it up to maintain the heap property + void insert(int val) { + heap.push_back(val); + percolateUp(heap.size() - 1); + } + + // Return the maximum element in the heap (root of the heap) + int getMax() { + if (heap.size() == 0) { + throw std::runtime_error("Heap is empty"); + } + return heap[0]; + } + + // Update the value, percolate up, and max heapify to maintain the heap property + void update(int old_val, int new_val) { + auto it = std::find(heap.begin(), heap.end(), old_val); + if (it != heap.end()) { + *it = new_val; + percolateUp(it - heap.begin()); + maxHeapify(it - heap.begin()); + } else { + std::cout << "Value not in heap" << std::endl; + } + } + + // Replace the root with the last element, pop the last element, and max heapify + void deleteMax() { + if (heap.size() == 0) { + throw std::runtime_error("Heap is empty"); + } + heap[0] = heap.back(); + heap.pop_back(); + maxHeapify(0); + } + + // Print all values in the heap + void printHeap() { + for (int i : heap) { + std::cout << i << " "; + } + std::cout << std::endl; + } +}; + +int main() { + // Example usage of the MaxHeap class + MaxHeap heap; + + // Insert elements into the heap using user input + while (true) { + int value; + std::cout << "Enter an integer to insert into the heap (enter a non-integer to stop): "; + if (!(std::cin >> value)) { + break; + } + heap.insert(value); + std::cout << "Heap after insertion: "; + heap.printHeap(); + } + + // Print all values in heap + std::cout << "All values in heap: "; + heap.printHeap(); + + // Get max value in heap + try { + std::cout << "Max Value: " << heap.getMax() << std::endl; + } catch (const std::exception& e) { + std::cerr << e.what() << std::endl; + } + + // Update value in heap using user input + try { + int old_value, new_value; + std::cout << "Enter the value to update: "; + std::cin >> old_value; + std::cout << "Enter the new value: "; + std::cin >> new_value; + heap.update(old_value, new_value); + std::cout << "Max Value after update: " << heap.getMax() << std::endl; + } catch (const std::exception& e) { + std::cerr << e.what() << std::endl; + } + + // Delete max value from heap + try { + heap.deleteMax(); + std::cout << "Max Value after deletion: " << heap.getMax() << std::endl; + } catch (const std::exception& e) { + std::cerr << e.what() << std::endl; + } + + // Print all values in heap after deletion + std::cout << "All values in heap: "; + heap.printHeap(); + + return 0; +} diff --git a/NQueen.md b/NQueen.md deleted file mode 100644 index 63e25db6..00000000 --- a/NQueen.md +++ /dev/null @@ -1,431 +0,0 @@ -# Exploring Backtracking Algorithm:N Queen Problem - -Today, we dive into the fascinating world of backtracking algorithms and uncover one of their most captivating mysteries – backtracking algorithms. Get ready to embark on an adventure that will challenge your mind and leave you with newfound insights into the power of backtracking! - -## **Introduction to Backtracking Algorithm** - -The backtracking algorithm is a powerful technique used to solve problems that can be broken down into a series of decisions. It is commonly employed in constraint satisfaction problems, combinatorial optimization, and other algorithmic challenges where exhaustive search is necessary. Backtracking involves a systematic search through the space of all possible solutions. - -Problems associated with backtracking can be categorized into 3 categories: - -- **Decision Problems:** Here, we search for a feasible solution. -- **Optimization Problems:** For this type of problems, we search for the best solution. -- **Enumeration Problems:** We find set of all possible feasible solutions to the problems of this type. - -The backtracking algorithm can be used to solve various problems such as the N-Queens problem, the Sudoku puzzle, graph coloring problems, and other constraint satisfaction problems. Its ability to systematically explore all possible solutions makes it a fundamental technique in the field of algorithm design and optimization. However, it is important to note that the efficiency of the backtracking algorithm depends on the reducing of the search space and the identification of constraints to reduce unnecessary exploration. - -## **N Queen Problem** - -The N-Queens problem is a classic puzzle that involves placing N chess queens on an N×N chessboard in such a way that no two queens can threaten each other. In chess, a queen can move horizontally, vertically, and diagonally, which means that no two queens should share the same row, column, or diagonal on the chessboard. - -The primary goal of the N-Queens problem is to find a placement of N queens on an N×N chessboard so that no two queens can attack each other. This requires a systematic approach to exploring possible configurations while adhering to the constraints imposed by the rules of chess. One of the key challenges is to find an efficient algorithm to solve the problem, especially for larger values of N, as the search space grows exponentially with N. - -The N-Queens problem serves as a benchmark for evaluating various algorithms, particularly those involving backtracking, constraint satisfaction, and combinatorial optimization. - -Solving the N-Queens problem efficiently demonstrates the ability to design algorithms that can systematically explore and navigate complex search spaces, making it a fundamental problem in the field of algorithm design and optimization. - -## Overview Of Backtracking Algorithm - -The N-Queens problem algorithm is a classic example of a backtracking algorithm, and it is used to solve the N-Queens puzzle. The goal of the N-Queens puzzle is to place N chess queens on an N×N chessboard in such a way that no two queens can attack each other, i.e., no two queens can share the same row, column, or diagonal. In this section, we will provide an overview of the backtracking algorithm and discuss its implementation. - -Backtracking involves constructing a solution incrementally, making a series of choices to build a candidate solution one step at a time. - -The algorithm systematically explores the solution space by making a decision at each step and recursively exploring all possible choices, while maintaining a record of the choices made. - -At each step, the algorithm checks whether the current partial solution violates any constraints. If the constraints are violated, it abandons that branch of the search space and backtracks to the previous decision point. - -Backtracking is typically implemented using recursion. The recursive nature allows the algorithm to go deeper into the search space and backtrack to explore alternative choices when needed. - -## Code - -```python -# Copyrights to venkys.io -# For more programs visit venkys.io -# Python program for NQeens - -# Function to take user input -def getBoard(n): - result=[] - board=[[0 for i in range(n)] for j in range(n)] - return board,result -# result=[] - -# Function to print board -def printboard(board,n): - for i in range(n): - for j in range(n): - if board[i][j]: - print("Q",end=" ") - else: - print("_",end=" ") - print() - -# Function to check if the given box is safe or not -def isSafe(board,row,col): - - # For same row - for i in range(col): - if board[row][i]: - return False - - # For same column - for i in range(row): - if board[i][col]: - return False - - - # For left upper diagonal - i=row - j=col - while i>=0 and j>=0: - if board[i][j]: - return False - i-=1 - j-=1 - - # For right upper diagonal - i=row - j=col - while j>=0 and i= 0 && r >= 0; c--, r--) { - if (board[r][c] == 'Q') { - return false; - } - } - // upper right - r = row; - for (int c = col; c < board.length && r >= 0; c++, r--) { - if (board[r][c] == 'Q') { - return false; - } - } - // lower left - r = row; - for (int c = col; c >= 0 && r < board.length; c--, r++) { - if (board[r][c] == 'Q') { - return false; - } - } - // lower right - r = row; - for (int c = col; r < board.length && c < board.length; r++, c++) { - if (board[r][c] == 'Q') { - return false; - } - } - return true; - } - - // updating the queen position in board - public static void saveBoard(char[][] board, List> allBoards) { - String row; - List newBoard = new ArrayList<>(); - for (int i = 0; i < board.length; i++) { - row = ""; - for (int j = 0; j < board[0].length; j++) { - if (board[i][j] == 'Q') { - row += 'Q'; - System.out.print("Q"+" "); - } else { - row += '.'; - System.out.print("_"+" "); - } - } - System.out.println(); - newBoard.add(row); - } - allBoards.add(newBoard); - System.out.println("------------------------"); - } - - // placing the queen in correct position in board - public static void helper(char[][] board, List> allBoards, int col) { - if (col == board.length) { - saveBoard(board, allBoards); - return; - } - for (int row = 0; row < board.length; row++) { - if (isSafe(row, col, board)) { - board[row][col] = 'Q'; - helper(board, allBoards, col + 1); - board[row][col] = '.'; - } - } - } - - // return board after placing the queen - public static List> solveQueens(int n) { - List> allBoards = new ArrayList<>(); - char[][] board = new char[n][n]; - helper(board, allBoards, 0); - return allBoards; - } - - public static void main(String[] args) { - - int n = 4; - - List> lst = solveQueens(n); - System.out.println("Queen can be placed in chess board by the following code:"); - System.out.println(lst); - } -} -``` - -## Explanation - -Now we have the basic understanding of backtracking algorithm and the concept of N queen problem. - -Let’s dive into the step-by-step process of placing N queens on a board using backtracking. - -[1.Is](http://1.Is)safe Function: - -In the first step the ‘isSafe’ function checks whether placing a queen at a given position is safe, considering the row, column, and both diagonals. - -2.saveBoard Function - -In the next step the ‘saveBoard’ function prints the current chessboard configuration and saves it in a list. - -3.helper Function - -In this step the ‘helper’ function is a recursive backtracking function that tries to place queens column-wise, ensuring that they do not attack each other. It calls ‘saveBoard’ when a valid placement is found. - -4.solveQueen Function - -In this step the ‘solveQueens’ function initializes the chessboard and calls the ‘helper’ function to find all valid configurations. - -5.main Function - -In the ‘main’ function, the program sets ‘n’ to 4 (you can change it to solve for different board sizes) and then calls ‘solveQueens’. The resulting configurations are printed. - -## Code - -```cpp -// Copyrights to venkys.io -// For more programs visit venkys.io -// CPP program for NQeens - -#include - -using namespace std; - -bool isSafe(int row,int col,vector> &board){ - for(int j=0;j=0 && r>=0; c--,r--){ - if(board[r][c] == 'Q'){ - return false; - } - } - - for(int r=row,c=col;c>=0 && r< board.size();c--,r++){ - if(board[r][c] == 'Q') return false; - } - return true; - -} - -void saveBoard(vector> board,vector> allBoards){ - string row; - vector newBoard; - for(int i=0;i> board,vector> allBoards,int col){ - if(col == board.size()){ - saveBoard(board,allBoards); - return ; - } - for(int row=0;row> solveQueens(int n){ - vector> allBoards; - vector> board(n,vector(n,'.')); - helper(board,allBoards,0); - return allBoards; - -} - -int main(){ - - int n=4; - vector> lst = solveQueens(n); - cout<<"Queen can be placed in chess board by the following code"< - - - -## Instructions - -Git setup instructions to make contributions to the repository. - - - -## Install Git - -Download the latest version of `Git`. - -https://git-scm.com/download - - - -## Clone this repository - -Create a folder to clone the repository. - -Open the terminal and run the following git command: -```bash -git clone https://github.com/venkys-io/Code-Blogs.git -``` - - - -If this is the first time using git. A browser tab pops up to login into your GitHub account. - -and set the default config username and email. This is necessary to make any commits on the repository. - -```bash -git config --global user.name "yourUserName" -git config --global user.email "YourEmailId@gmail.com" -``` - -## Create a branch - -Change to the repository directory on your computer (if you are not already there): - -```bash -cd Code-Blogs -``` - -make sure you are in the correct directory. -```bash -C:\Users\....\Code-blogs\> -``` - -Now create a branch using `git checkout` command: - -``` -git checkout -b "yourBranchName" -``` - -you will be switched to your branch. - -## Make changes or Add your files - -Now write your blogs. - -Open vs-code in the repository. - -Shortcut: -```bash - C:\Users\....\Code-blogs\> code . -``` - - -To create a blog for the program `programs-prod/algorithms/Arrays/Easy/Palindrome` - -Create a `README.md` file as specified format. - -Example: - -
-Code-Blogs
-  └───Arrays
-        ├───Easy
-        │   └───Palindrome
-        |       └───README.md
-        └───Medium
-
-
- - -Now write the blog in `markdown`. - -To know more about markdown. visit - -[Cheatsheet1](https://www.freecodecamp.org/news/markdown-cheatsheet/)
-[Cheatsheet2](https://www.markdownguide.org/cheat-sheet/) - - - - - - - -## Commit those changes. - -Even though you have created a file. `Git` actually doesn't track those files. - -you can check the status of your branch. using `git status` command. - -```bash -git status -``` - -It displays all the changes you made on the branch. - - -Add those changes to the branch you just created using the `git add` command: - -```bash -git add . -``` - -Now commit those changes using the git commit command: - -```bash -git commit -m "your commit message" -``` - - -## Push changes to GitHub. - -push the changes using the command `git push` - -```bash -git push -u origin your-branch-name -``` - -replace your-branch-name with the name of the branch you created earlier. - - -- Note: You might get Authentication errors if it was your first time. - - - -## Raise a Pull Request for review. - -Now open GitHub to see those changes. - -Now open `pull request` tab on GitHub repository. - -Hit on Compare & Pull request. - -Mention your changes. - -Hit on create pull request. - - - - -## Suggestions - -It is good a have knowledge on Git, GitHub. - -Here are few free resources. You can try. - -[Youtube1](https://youtu.be/vwj89i2FmG0?si=C79oYDwj2A7Iqhta)
-[Youtube2](https://youtu.be/Ez8F0nW6S-w?si=FH0luDqtp9WiWqgp)
-[Youtube3](https://youtu.be/CvUiKWv2-C0?si=6Nx71vP7WXtAAbqe)
-[GitHub-Resource](https://github.com/firstcontributions/first-contributions) \ No newline at end of file diff --git a/Strings/Hard/KMPAlgorithm.java b/Strings/Hard/KMPAlgorithm.java new file mode 100644 index 00000000..8a23076c --- /dev/null +++ b/Strings/Hard/KMPAlgorithm.java @@ -0,0 +1,70 @@ +import java.util.Scanner; + +public class KMPAlgorithm { + + // Function to compute the prefix function (π) for the pattern 'p' + public static int[] computePrefixFunction(String p) { + int m = p.length(); + int[] π = new int[m]; // Initialize an array to store the prefix function values + int k = 0; // Initialize a variable for matching characters + + // Iterate through the pattern to compute the prefix function + for (int q = 1; q < m; ++q) { + // Update the matching character index while it's not zero and characters mismatch + while (k > 0 && p.charAt(k) != p.charAt(q)) { + k = π[k - 1]; + } + // If characters match, increment the matching character index + if (p.charAt(k) == p.charAt(q)) { + k += 1; + } + // Store the current matching character index in the prefix function array + π[q] = k; + } + + return π; + } + + // Function to perform pattern matching using the Knuth-Morris-Pratt (KMP) algorithm + public static void kmpMatcher(String t, String p) { + int n = t.length(); // Length of the text + int m = p.length(); // Length of the pattern + int[] π = computePrefixFunction(p); // Compute the prefix function for the pattern + int q = 0; // Initialize a variable for matching characters + + // Iterate through the text to find occurrences of the pattern + for (int i = 0; i < n; ++i) { + // Update the matching character index while it's not zero and characters mismatch + while (q > 0 && p.charAt(q) != t.charAt(i)) { + q = π[q - 1]; + } + // If characters match, increment the matching character index + if (p.charAt(q) == t.charAt(i)) { + q += 1; + } + // If the entire pattern is matched, print the occurrence + if (q == m) { + System.out.println("Pattern occurs with shift " + (i - m + 1)); + q = π[q - 1]; // Look for the next match + } + } + } + + public static void main(String[] args) { + try (Scanner scanner = new Scanner(System.in)) { + // Get user input with null safety + System.out.print("Enter the text: "); + String text = scanner.nextLine(); + System.out.print("Enter the pattern: "); + String pattern = scanner.nextLine(); + + // Check if input is not empty and contains only alphabetical characters + if (!text.isEmpty() && !pattern.isEmpty() && Character.isLetter(text.charAt(0)) + && Character.isLetter(pattern.charAt(0))) { + kmpMatcher(text, pattern); + } else { + System.out.println("Please enter valid alphabetic text and pattern."); + } + } + } +} diff --git a/Strings/Hard/Readme.md b/Strings/Hard/Readme.md new file mode 100644 index 00000000..561d62eb --- /dev/null +++ b/Strings/Hard/Readme.md @@ -0,0 +1,503 @@ +# Exploring string searching algorithm : + +String searching algorithms are techniques used to find occurrences of a specific pattern within a larger text or string. These algorithms are crucial in various applications, including text processing, data mining, bioinformatics, and many others. + +# Introduction to Strings: + +A string is a sequence of characters enclosed in double or single quotes. It can contain letters, digits, special symbols, spaces, punctuation. +Strings, an indispensable data type in programming, serve as a versatile container for sequences of characters. One of the distinctive features of strings is their ability to store and process textual information. Whether it's a single word, a sentence, or even a larger body of text, strings provide a means to work with this data programmatically. In most programming languages, strings are typically enclosed within quotation marks, such as single (' ') or double (" ") quotes, allowing the interpreter or compiler to recognize them as string literals. + +# Introduction to the Knuth-Morris-Pratt (KMP) : + +The Knuth-Morris-Pratt (KMP) algorithm is a powerful string searching algorithm that efficiently finds occurrences of a pattern within a text. Developed by Donald Knuth, Vaughan Pratt, and James H. Morris, the KMP algorithm was introduced in 1977 as a solution to the problem of searching for a pattern in linear time without unnecessary backtracking. + +The primary innovation of the KMP algorithm lies in its ability to preprocess the pattern before searching, creating a "prefix function" that helps determine the optimal positions to resume the search when a mismatch occurs. This preprocessing allows the algorithm to avoid redundant character comparisons, making it more efficient than traditional brute-force methods. + +The Knuth-Morris-Pratt algorithm stands out for its ability to achieve linear time complexity, making it a powerful tool for efficient string searching in various applications. + +# Overview of Knuth-Morris-Pratt (KMP) : + + The KMP algorithm addresses the inefficiencies of brute-force methods by using a clever precomputation technique. + + Compute Prefix Function: + The prefix function is computed by iterating through the pattern and updating values based on matching prefixes and suffixes. + + Search Using Prefix Function: + The text is scanned with the pattern, and when a mismatch occurs, the prefix function guides the algorithm to determine the optimal position to resume the search. + + Efficient Backtracking: + The KMP algorithm efficiently backtracks by using the information stored in the prefix function, eliminating redundant character comparisons. + +# PYTHON +# code +```python +/* Copyrights to venkys.io +For more information, visit https://venkys.io */ + +# python program for Knuth-Morris-Pratt algorithm +# Stable: Yes +# Inplace: Yes +# Adaptive: No + +# Space complexity:O(m) +# Time complexity:O(n+m) + +def compute_prefix_function(p): + # Function to compute the prefix function (π) for the pattern 'p' + m = len(p) + π = [0] * m # Initialize an array to store the prefix function values + k = 0 # Initialize a variable for matching characters + + # Iterate through the pattern to compute the prefix function + for q in range(1, m): + # Update the matching character index while it's not zero and characters mismatch + while k > 0 and p[k] != p[q]: + k = π[k - 1] + # If characters match, increment the matching character index + if p[k] == p[q]: + k += 1 + # Store the current matching character index in the prefix function array + π[q] = k + + return π + +def kmp_matcher(t, p): + # Function to perform pattern matching using the Knuth-Morris-Pratt (KMP) algorithm + n = len(t) # Length of the text + m = len(p) # Length of the pattern + π = compute_prefix_function(p) # Compute the prefix function for the pattern + q = 0 # Initialize a variable for matching characters + + # Iterate through the text to find occurrences of the pattern + for i in range(n): + # Update the matching character index while it's not zero and characters mismatch + while q > 0 and p[q] != t[i]: + q = π[q - 1] + # If characters match, increment the matching character index + if p[q] == t[i]: + q += 1 + # If the entire pattern is matched, print the occurrence + if q == m: + print(f"Pattern occurs with shift {i - m + 1}") + q = π[q - 1] # Look for the next match + +# Get user input with null safety +text = input("Enter the text: ") +pattern = input("Enter the pattern: ") + +# Check if input is not empty and contains only alphabetical characters +if text and pattern and text.isalpha() and pattern.isalpha(): + kmp_matcher(text, pattern) +else: + print("Please enter valid text and pattern.") + +``` +# Code Explanation + +This Python code implements the Knuth-Morris-Pratt (KMP) algorithm, a string searching algorithm used to find occurrences of a pattern within a given text. Here's an explanation for each section of the code: + +1. **`compute_prefix_function(p)` Function:** + - This function computes the prefix function (π) for the given pattern `p`. + - `m = len(p)`: Calculate the length of the pattern. + - `π = [0] * m`: Initialize an array `π` to store the prefix function values, and set all values to 0. + - `k = 0`: Initialize a variable for tracking matching characters. + - The function iterates through the pattern and updates the prefix function values based on matching prefixes and suffixes. + +2. **`kmp_matcher(t, p)` Function:** + - This function performs pattern matching using the Knuth-Morris-Pratt (KMP) algorithm. + - `n = len(t)`: Calculate the length of the text. + - `m = len(p)`: Calculate the length of the pattern. + - `π = compute_prefix_function(p)`: Compute the prefix function for the pattern. + - `q = 0`: Initialize a variable for tracking matching characters. + +3. **Pattern Matching Loop:** + - The function iterates through each character in the text. + - A nested while loop updates the matching character index `q` based on the prefix function values and mismatches between characters in the pattern and text. + - If characters match, `q` is incremented. + - If the entire pattern is matched (`q == m`), it prints the occurrence with the shift index and updates `q` to continue looking for the next match. + +4. **User Input and Validation:** + - The code prompts the user to input the text and pattern. + - It checks if the input is not empty and contains only alphabetical characters using `text.isalpha()` and `pattern.isalpha()`. + - If the input is valid, it calls the `kmp_matcher` function; otherwise, it prints an error message. + +5. **Output:** + - If the user input is valid and there are pattern occurrences in the text, it prints the positions (shifts) where the pattern is found. + +Overall, this code provides a simple and functional implementation of the KMP algorithm, allowing users to find occurrences of a pattern within a given text. The use of functions enhances modularity and readability. + +# C++ + +code +```c++ +/* Copyrights to venkys.io +For more information, visit https://venkys.io */ + +// C++ program for Knuth-Morris-Pratt algorithm +// Stable: Yes +// In-Place: Yes +// Adaptive: No +// Space Complexity: O(m) +// Time Complexity: O(n + m) + +#include +#include +#include + +// Function to compute the prefix function (π) for the pattern 'p' +std::vector compute_prefix_function(const std::string& p) { + int m = p.length(); // Length of the pattern + std::vector π(m, 0); // Initialize an array to store the prefix function values + int k = 0; // Initialize a variable for matching characters + + // Iterate through the pattern to compute the prefix function + for (int q = 1; q < m; ++q) { + // Update the matching character index while it's not zero and characters mismatch + while (k > 0 && p[k] != p[q]) { + k = π[k - 1]; + } + // If characters match, increment the matching character index + if (p[k] == p[q]) { + k += 1; + } + // Store the current matching character index in the prefix function array + π[q] = k; + } + + return π; +} + +// Function to perform pattern matching using the Knuth-Morris-Pratt (KMP) algorithm +void kmp_matcher(const std::string& t, const std::string& p) { + int n = t.length(); // Length of the text + int m = p.length(); // Length of the pattern + std::vector π = compute_prefix_function(p); // Compute the prefix function for the pattern + int q = 0; // Initialize a variable for matching characters + + // Iterate through the text to find occurrences of the pattern + for (int i = 0; i < n; ++i) { + // Update the matching character index while it's not zero and characters mismatch + while (q > 0 && p[q] != t[i]) { + q = π[q - 1]; + } + // If characters match, increment the matching character index + if (p[q] == t[i]) { + q += 1; + } + // If the entire pattern is matched, print the occurrence + if (q == m) { + std::cout << "Pattern occurs with shift " << i - m + 1 << std::endl; + q = π[q - 1]; // Look for the next match + } + } +} + +int main() { + // Get user input with null safety + std::string text, pattern; + std::cout << "Enter the text: "; + std::cin >> text; + std::cout << "Enter the pattern: "; + std::cin >> pattern; + + // Check if input is not empty and contains only alphabetical characters + if (!text.empty() && !pattern.empty() && std::isalpha(text[0]) && std::isalpha(pattern[0])) { + kmp_matcher(text, pattern); + } + else { + std::cout << "Please enter valid alphabetic text and pattern." << std::endl; + } + + return 0; +} + +``` +# Code Explanation +Here's an explanation for the provided C++ code that implements the Knuth-Morris-Pratt (KMP) algorithm for string searching: + +1. **`compute_prefix_function` Function:** + - This function computes the prefix function (π) for the given pattern `p`. + - `int m = p.length();`: Calculate the length of the pattern. + - `std::vector π(m, 0);`: Initialize a vector to store the prefix function values, and set all values to 0. + - `int k = 0;`: Initialize a variable for tracking matching characters. + - The function iterates through the pattern and updates the prefix function values based on matching prefixes and suffixes. + +2. **`kmp_matcher` Function:** + - This function performs pattern matching using the KMP algorithm. + - `int n = t.length();`: Calculate the length of the text. + - `int m = p.length();`: Calculate the length of the pattern. + - `std::vector π = compute_prefix_function(p);`: Compute the prefix function for the pattern. + - `int q = 0;`: Initialize a variable for tracking matching characters. + +3. **Pattern Matching Loop:** + - The function iterates through each character in the text. + - A nested while loop updates the matching character index `q` based on the prefix function values and mismatches between characters in the pattern and text. + - If characters match, `q` is incremented. + - If the entire pattern is matched (`q == m`), it prints the occurrence with the shift index and updates `q` to continue looking for the next match. + +4. **User Input and Validation:** + - The code prompts the user to input the text and pattern. + - It checks if the input is not empty and contains only alphabetical characters using `std::isalpha()` similar to the Python code. + - If the input is valid, it calls the `kmp_matcher` function; otherwise, it prints an error message. + +The code is designed to efficiently find occurrences of a pattern within a given text, providing a powerful and widely used string searching algorithm. + +# java +# code +```java +/* Copyrights to venkys.io +For more information, visit https://venkys.io */ + +// C++ program for Knuth-Morris-Pratt algorithm +// Stable: Yes +// In-Place: Yes +// Adaptive: No +// Space Complexity: O(m) +// Time Complexity: O(n + m) + +import java.util.Scanner; + +public class KMPAlgorithm { + + // Function to compute the prefix function (π) for the pattern 'p' + public static int[] computePrefixFunction(String p) { + int m = p.length(); + int[] π = new int[m]; // Initialize an array to store the prefix function values + int k = 0; // Initialize a variable for matching characters + + // Iterate through the pattern to compute the prefix function + for (int q = 1; q < m; ++q) { + // Update the matching character index while it's not zero and characters mismatch + while (k > 0 && p.charAt(k) != p.charAt(q)) { + k = π[k - 1]; + } + // If characters match, increment the matching character index + if (p.charAt(k) == p.charAt(q)) { + k += 1; + } + // Store the current matching character index in the prefix function array + π[q] = k; + } + + return π; + } + + // Function to perform pattern matching using the Knuth-Morris-Pratt (KMP) algorithm + public static void kmpMatcher(String t, String p) { + int n = t.length(); // Length of the text + int m = p.length(); // Length of the pattern + int[] π = computePrefixFunction(p); // Compute the prefix function for the pattern + int q = 0; // Initialize a variable for matching characters + + // Iterate through the text to find occurrences of the pattern + for (int i = 0; i < n; ++i) { + // Update the matching character index while it's not zero and characters mismatch + while (q > 0 && p.charAt(q) != t.charAt(i)) { + q = π[q - 1]; + } + // If characters match, increment the matching character index + if (p.charAt(q) == t.charAt(i)) { + q += 1; + } + // If the entire pattern is matched, print the occurrence + if (q == m) { + System.out.println("Pattern occurs with shift " + (i - m + 1)); + q = π[q - 1]; // Look for the next match + } + } + } + + public static void main(String[] args) { + try (Scanner scanner = new Scanner(System.in)) { + // Get user input with null safety + System.out.print("Enter the text: "); + String text = scanner.nextLine(); + System.out.print("Enter the pattern: "); + String pattern = scanner.nextLine(); + + // Check if input is not empty and contains only alphabetical characters + if (!text.isEmpty() && !pattern.isEmpty() && Character.isLetter(text.charAt(0)) + && Character.isLetter(pattern.charAt(0))) { + kmpMatcher(text, pattern); + } else { + System.out.println("Please enter valid alphabetic text and pattern."); + } + } + } +} + +``` +# Code Explanation + +KMP Algorithm Explanation: + +1. **computePrefixFunction Method:** + - This method computes the prefix function (π) for the given pattern p. + - int m = p.length();: Calculate the length of the pattern. + - int[] π = new int[m];: Initialize an array to store the prefix function values. + - int k = 0;: Initialize a variable for tracking matching characters. + - The method iterates through the pattern and updates the prefix function values based on matching prefixes and suffixes. + +2. **kmpMatcher Method:** + - This method performs pattern matching using the KMP algorithm. + - int n = t.length();: Calculate the length of the text. + - int m = p.length();: Calculate the length of the pattern. + - int[] π = computePrefixFunction(p);: Compute the prefix function for the pattern + - int q = 0;: Initialize a variable for tracking matching characters. + +3. **Pattern Matching Loop:** + - The method iterates through each character in the text. + - A nested while loop updates the matching character index q based on the prefix function values and mismatches between characters in the pattern and text. + If characters match, q is incremented. + - If the entire pattern is matched (q == m), it prints the occurrence with the shift index and updates q to continue looking for the next match. + +4. **User Input and Validation:** + - The main method uses a Scanner to get user input for the text and pattern. + - It checks if the input is not empty and contains only alphabetical characters using Character.isLetter() similar to the C++ code. + - If the input is valid, it calls the kmpMatcher method; otherwise, it prints an error message. + +Overall, this Java implementation of the KMP algorithm achieves the same goal as the C++ code, providing an efficient way to find occurrences of a pattern within a given text. The core algorithm remains consistent across different programming languages. + +**Difference** + +Here are some key differences between the provided KMP algorithm implementations in Python, C++, and Java: + +### 1. **Syntax and Language Features:** + - **Python:** + - Dynamically typed language. + - Uses indentation for code blocks. + - **C++:** + - Statically typed language. + - Requires explicit type declarations. + - Uses curly braces for code blocks. + - **Java:** + - Statically typed language. + - Requires explicit type declarations. + - Uses curly braces for code blocks. + +### 2. **Data Structures:** + - **Python:** + - Uses lists for arrays. + - **C++:** + - Uses `std::vector` for dynamic arrays. + - **Java:** + - Uses arrays (`int[]`, `char[]`) for dynamic arrays. + +### 3. **Input/Output Handling:** + - **Python:** + - Uses `input()` and `print()` for user input/output. + - **C++:** + - Uses `std::cin` and `std::cout` for user input/output. + - **Java:** + - Uses `Scanner` and `System.out.println` for user input/output. + +### 4. **String Handling:** + - **Python:** + - Strings are immutable. + - Indexing starts from 0. + - **C++:** + - Uses `std::string` for string handling. + - **Java:** + - Uses `String` class for string handling. + - Strings are immutable. + +### 5. **Array Initialization:** + - **Python:** + - Uses list comprehension for array initialization. + - **C++:** + - Uses `std::vector` and array initialization. + - **Java:** + - Uses array initialization (`new int[m]`). + +### 6. **Exception Handling:** + - **Python:** + - Exception handling is more implicit. + - **C++:** + - Uses `try`, `catch` for exception handling. + - **Java:** + - Uses `try`, `catch` for exception handling. + +### 7. **Type Checking:** + - **Python:** + - Dynamically typed; type checking is done at runtime. + - **C++:** + - Statically typed; type checking is done at compile time. + - **Java:** + - Statically typed; type checking is done at compile time. + +### 8. **Memory Management:** + - **Python:** + - Automatic memory management (garbage collection). + - **C++:** + - Manual memory management using `new` and `delete`. + - **Java:** + - Automatic memory management (garbage collection). + +### 9. **Exception Handling for User Input:** + - **Python:** + - Uses `try`, `except` blocks for error handling. + - **C++ and Java:** + - Use `try`, `catch` blocks for error handling. + +### 10. **Array Indexing:** + - **Python and Java:** + - Indexing starts from 0. + - **C++:** + - Indexing starts from 0. + +These differences highlight the specific syntax and language features of each programming language. While the core logic of the KMP algorithm remains consistent, the implementation details vary to accommodate the unique aspects of each language. + +# Time and Space Complexity: + +The time and space complexity of the Knuth-Morris-Pratt (KMP) algorithm are crucial factors that determine its efficiency in solving the string matching problem. + +### Time Complexity: + +The time complexity of the KMP algorithm is O(n + m), where: +- n is the length of the text, +- m is the length of the pattern. + +This linear time complexity is achieved because, in the worst case, the algorithm only needs to iterate through each character in the text and the pattern once. The preprocessing step, which involves computing the prefix function, takes O(m) time. The actual pattern matching then takes O(n) time in the worst case. + +### Space Complexity: + +The space complexity of the KMP algorithm is O(m), where m is the length of the pattern. This space complexity is due to the storage requirements of the prefix function array (π) used during the pattern matching process. The array is of size m and is used to store information about the longest proper prefix that is also a suffix at each position in the pattern. + +In summary: +- Time Complexity: O(n + m) +- Space Complexity: O(m) + +These complexities make the KMP algorithm an efficient choice for string matching, particularly when the length of the pattern is significantly smaller than the length of the text. The linear time complexity ensures that the algorithm remains practical for large datasets. + +# Real-World Applications of KMP algorithm + +The Knuth-Morris-Pratt (KMP) algorithm, known for its efficient string matching capabilities, finds applications in various real-world scenarios. Some notable applications include: + +1. **Text Search Engines:** + - KMP is used in text search engines to quickly locate patterns within large documents or text corpora. Search engines often need to efficiently find occurrences of keywords or phrases in documents, and KMP aids in speeding up this process. + +2. **Code Editors and IDEs:** + - Integrated Development Environments (IDEs) and code editors use string matching algorithms like KMP to implement features such as code highlighting, code completion, and refactoring tools. These features often involve identifying patterns within source code efficiently. + +3. **Data Compression:** + - KMP can be applied in certain data compression algorithms, particularly those that involve searching for repeated patterns within the data. By efficiently identifying and compressing repeated patterns, data compression algorithms can achieve higher compression ratios. + +4. **Bioinformatics:** + - In bioinformatics, DNA and protein sequence analysis may involve searching for specific patterns or motifs. KMP can be utilized to efficiently find occurrences of these patterns within biological sequences, aiding researchers in genetic analysis. + +5. **Network Security:** + - KMP is used in network security applications for pattern matching in intrusion detection systems. It helps in quickly identifying specific patterns associated with malicious activities or known attack signatures in network traffic. + +6. **Spell Checking:** + - Spell-checking algorithms use string matching techniques to identify and suggest corrections for misspelled words. KMP can be employed to efficiently search for similar patterns within a dictionary of correctly spelled words. + +7. **Data Mining and Pattern Recognition:** + - KMP can be employed in data mining applications for identifying recurring patterns or trends within large datasets. It aids in efficiently searching for specific patterns of interest in data streams. + +8. **Genome Sequencing:** + - In genomics, the KMP algorithm can be used to search for specific genetic sequences or motifs within large DNA datasets. This is valuable for tasks such as genome sequencing and identifying genes associated with certain traits or diseases. + +9. **Compilers:** + - Compilers use string matching algorithms for lexical analysis, where they identify and tokenize specific patterns in the source code. KMP can contribute to efficient pattern matching during this phase of compilation. + +The KMP algorithm's efficiency in handling pattern matching makes it a versatile tool in various fields where finding occurrences of specific patterns in large datasets is a fundamental task. Its linear time complexity ensures practical applicability in scenarios involving sizable datasets. \ No newline at end of file diff --git a/Strings/Hard/kmp algorithm.cpp b/Strings/Hard/kmp algorithm.cpp new file mode 100644 index 00000000..c4d28cf1 --- /dev/null +++ b/Strings/Hard/kmp algorithm.cpp @@ -0,0 +1,70 @@ +#include +#include +#include + +// Function to compute the prefix function (π) for the pattern 'p' +std::vector compute_prefix_function(const std::string& p) { + int m = p.length(); // Length of the pattern + std::vector π(m, 0); // Initialize an array to store the prefix function values + int k = 0; // Initialize a variable for matching characters + + // Iterate through the pattern to compute the prefix function + for (int q = 1; q < m; ++q) { + // Update the matching character index while it's not zero and characters mismatch + while (k > 0 && p[k] != p[q]) { + k = π[k - 1]; + } + // If characters match, increment the matching character index + if (p[k] == p[q]) { + k += 1; + } + // Store the current matching character index in the prefix function array + π[q] = k; + } + + return π; +} + +// Function to perform pattern matching using the Knuth-Morris-Pratt (KMP) algorithm +void kmp_matcher(const std::string& t, const std::string& p) { + int n = t.length(); // Length of the text + int m = p.length(); // Length of the pattern + std::vector π = compute_prefix_function(p); // Compute the prefix function for the pattern + int q = 0; // Initialize a variable for matching characters + + // Iterate through the text to find occurrences of the pattern + for (int i = 0; i < n; ++i) { + // Update the matching character index while it's not zero and characters mismatch + while (q > 0 && p[q] != t[i]) { + q = π[q - 1]; + } + // If characters match, increment the matching character index + if (p[q] == t[i]) { + q += 1; + } + // If the entire pattern is matched, print the occurrence + if (q == m) { + std::cout << "Pattern occurs with shift " << i - m + 1 << std::endl; + q = π[q - 1]; // Look for the next match + } + } +} + +int main() { + // Get user input with null safety + std::string text, pattern; + std::cout << "Enter the text: "; + std::cin >> text; + std::cout << "Enter the pattern: "; + std::cin >> pattern; + + // Check if input is not empty and contains only alphabetical characters + if (!text.empty() && !pattern.empty() && std::isalpha(text[0]) && std::isalpha(pattern[0])) { + kmp_matcher(text, pattern); + } + else { + std::cout << "Please enter valid alphabetic text and pattern." << std::endl; + } + + return 0; +} \ No newline at end of file diff --git a/Strings/Hard/kmp algorithm.py b/Strings/Hard/kmp algorithm.py new file mode 100644 index 00000000..20d242aa --- /dev/null +++ b/Strings/Hard/kmp algorithm.py @@ -0,0 +1,48 @@ +def compute_prefix_function(p): + # Function to compute the prefix function (π) for the pattern 'p' + m = len(p) + π = [0] * m # Initialize an array to store the prefix function values + k = 0 # Initialize a variable for matching characters + + # Iterate through the pattern to compute the prefix function + for q in range(1, m): + # Update the matching character index while it's not zero and characters mismatch + while k > 0 and p[k] != p[q]: + k = π[k - 1] + # If characters match, increment the matching character index + if p[k] == p[q]: + k += 1 + # Store the current matching character index in the prefix function array + π[q] = k + + return π + +def kmp_matcher(t, p): + # Function to perform pattern matching using the Knuth-Morris-Pratt (KMP) algorithm + n = len(t) # Length of the text + m = len(p) # Length of the pattern + π = compute_prefix_function(p) # Compute the prefix function for the pattern + q = 0 # Initialize a variable for matching characters + + # Iterate through the text to find occurrences of the pattern + for i in range(n): + # Update the matching character index while it's not zero and characters mismatch + while q > 0 and p[q] != t[i]: + q = π[q - 1] + # If characters match, increment the matching character index + if p[q] == t[i]: + q += 1 + # If the entire pattern is matched, print the occurrence + if q == m: + print(f"Pattern occurs with shift {i - m + 1}") + q = π[q - 1] # Look for the next match + +# Get user input with null safety +text = input("Enter the text: ") +pattern = input("Enter the pattern: ") + +# Check if input is not empty and contains only alphabetical characters +if text and pattern and text.isalpha() and pattern.isalpha(): + kmp_matcher(text, pattern) +else: + print("Please enter valid text and pattern.") diff --git a/Strings/Medium/String to Integer/Readme.md b/Strings/Medium/String to Integer/Readme.md new file mode 100644 index 00000000..56469eeb --- /dev/null +++ b/Strings/Medium/String to Integer/Readme.md @@ -0,0 +1,529 @@ +# Exploring Strings and Converting them to Integers : + +"Exploring Strings and Converting them to Integers" concept likely involves working with strings in a programming context and performing operations that involve converting these strings into integer values. + +**Exploring Strings** : This suggests that we will be working with strings, which are sequences of characters. Exploring strings may involve tasks such as accessing individual characters, finding the length of a string, or manipulating the content in various ways. +**Converting Strings to Integers** : The main task is likely to involve converting strings into integer values. This conversion is common in programming when dealing with user input or data read from external sources, where the input is initially in the form of text (string) but needs to be processed as numerical values (integers). + +# Introduction to Strings: + +A string is a sequence of characters enclosed in double or single quotes. It can contain letters, digits, special symbols, spaces, punctuation. +Strings, an indispensable data type in programming, serve as a versatile container for sequences of characters. One of the distinctive features of strings is their ability to store and process textual information. Whether it's a single word, a sentence, or even a larger body of text, strings provide a means to work with this data programmatically. In most programming languages, strings are typically enclosed within quotation marks, such as single (' ') or double (" ") quotes, allowing the interpreter or compiler to recognize them as string literals. + +# Introduction to the String to Integer Theory: + +One of the frequent challenges encountered in programming involves converting strings to integers. This operation is particularly crucial when dealing with user inputs, file parsing, or scenarios where numeric values need extraction from a textual context. The process entails considering signs, handling leading whitespaces, and ensuring that the resulting integer falls within the appropriate range. + +# Overview of String to Integer: + +The problem involves working with strings, extracting numerical information from them, converting this information to integers, and potentially performing operations on these integers. The specific details would depend on the context and requirements of the problem as defined in the prompt or task description. +some of the common tasks that might encounter within this problem statement: + +**Parsing Integers** : Extracting numerical values from a string. This involves identifying and extracting the sequence of characters that represents an integer. + +**Handling Edge Cases** : Dealing with situations where the string may not represent a valid integer (e.g., contains non-numeric characters). Error handling may be required. + +**Performing Arithmetic Operations** : Once the strings are converted to integers, you might be asked to perform various arithmetic operations (addition, subtraction, multiplication, etc.) on these values. + +**Output Formatting** : Displaying the results in a specified format, which may involve converting integers back to strings for output. + +# PYTHON +# code +```python +/* Copyrights to venkys.io +For more information, visit https://venkys.io */ + +// python program to convert string to integer +// Stable: Yes +// Inplace: No +// Adaptive: Yes + +// Space complexity:O(n) +// Time complexity:O(1) + +def convertStringToInteger(s): + # Define constants for the 32-bit signed integer range + INT_MIN, INT_MAX = -2**31, 2**31 - 1 + + # Initialize variables for the result, sign, and position in the string + answer, sign, i = 0, 1, 0 + + # Skip leading whitespaces + while i < len(s) and s[i] == " ": + i += 1 + + # Check for empty string or non-numeric input + if i == len(s) or (s[i] not in ["+", "-"] and not s[i].isdigit()): + print("Error: Invalid input. Please enter a valid numeric string.") + return None + + # Handle sign + if s[i] in ["+", "-"]: + # Set the sign based on the presence of a positive or negative sign + sign = -1 if s[i] == "-" else 1 + i += 1 + + # Process digits + while i < len(s) and s[i].isdigit(): + digit = int(s[i]) + # Check for overflow + if (INT_MAX - digit) // 10 < answer: + return INT_MAX if sign == 1 else INT_MIN + # Update the result by multiplying by 10 and adding the current digit + answer = answer * 10 + digit + i += 1 + + # Return the final result multiplied by the sign + return sign * answer + +if __name__ == "__main__": + # Example usage + input_string = input("Enter a string: ") + result = convertStringToInteger(input_string) + + # Check for None to handle cases of invalid input + if result is not None: + print("Converted integer:", result) + +``` +# Code Explanation + +1. **`check` function:** + ```python + def check(answer): + if answer < -2**31: + return -2**31 + elif answer >= 2**31: + return 2**31 - 1 + else: + return answer + ``` + - This function takes an `answer` as input and checks if it is within the range of a 32-bit signed integer. If it's less than the minimum value, it returns the minimum value. If it's greater than or equal to the maximum value, it returns the maximum value. Otherwise, it returns the original answer. + +2. **`myAtoi` function:** + ```python + def myAtoi(string): + answer = 0 + sign = 0 + i = 0 + + # Skip leading whitespaces + while i < len(string) and string[i] == " ": + i += 1 + if i == len(string): + return answer + + # Check for positive sign + if string[i] == "+" and sign == 0: + sign = 1 + i += 1 + + # Check for negative sign + if i < len(string) and string[i] == "-" and sign == 0: + sign = -1 + i += 1 + + # Process the digits of the string + while i < len(string): + if string[i].isdigit(): + # Update the answer by multiplying it by 10 and adding the current digit + answer = answer * 10 + int(string[i]) + i += 1 + else: + # If a non-digit character is encountered, apply the sign if present and return the result after checking for overflow + if sign != 0: + answer = sign * answer + return check(answer) + + # Apply the sign if present and return the result after checking for overflow + if sign != 0: + answer = sign * answer + return check(answer) + ``` + - This function takes a string `string` as input and converts it to an integer according to the rules specified. + - It initializes `answer` to store the result, `sign` to store the sign of the number, and `i` as an index to iterate through the string. + - It skips leading whitespaces in the string. + - It checks for positive or negative signs and sets the `sign` accordingly. + - It iterates through the string, processes digits, and updates the `answer`. + - If a non-digit character is encountered, it applies the sign if present and returns the result after checking for overflow using the `check` function. + - Finally, it returns the checked and signed result. + +3. **Test the function:** + ```python + if __name__ == "__main__": + s = "42" + print(myAtoi(s)) + ``` + - It tests the `myAtoi` function with the input string "42" and prints the result. + +The code essentially converts a given string to an integer, considering positive and negative signs, and handles overflow by checking against the limits of a 32-bit signed integer. The `check` function ensures that the result does not exceed the specified integer limits. + +# C++ +code +```c++ +/* Copyrights to venkys.io +For more information, visit https://venkys.io */ + +// C++ program to convert string to integer +// Stable: Yes +// Inplace: No +// Adaptive: No + +// Space complexity: O(n) +// Time complexity: O(1) + +#include +#include +#include +#include + +// Function to convert a string to an integer +std::optional atoi(const std::string& s) { + // Check if the input string is empty + if (s.empty()) { + std::cerr << "Error: Input string is empty." << std::endl; + return std::nullopt; // Using std::optional to represent the absence of a value + } + + int num = 0, i = 0, sign = 1; + + // Skip leading whitespaces + while (i < s.length() && s[i] == ' ') { + i++; + } + + // Check for sign + if (i < s.length() && (s[i] == '-' || s[i] == '+')) { + // Determine the sign of the number + sign = (s[i] == '+') ? 1 : -1; + i++; + } + + // Process digits + while (i < s.length() && std::isdigit(s[i])) { + // Check for overflow + if ((num > std::numeric_limits::max() / 10) || + ((num == std::numeric_limits::max() / 10) && (s[i] - '0' > 7))) { + std::cerr << "Error: Integer overflow." << std::endl; + return std::nullopt; + } + + // Update the result by multiplying by 10 and adding the current digit + num = ((num * 10) + (s[i] - '0')); + i++; + } + + // Return the final result multiplied by the sign + return num * sign; +} + +int main() { + // Prompt the user for input + std::string input; + std::cout << "Enter a string: "; + std::getline(std::cin, input); + + // Call the atoi function and print the result + auto result = atoi(input); + if (result.has_value()) { + std::cout << "Converted integer: " << result.value() << std::endl; + } + + return 0; +} +``` +# Code Explanation + +1. **Function Definition:** + ```cpp + int myAtoi(const std::string &str) { + ``` + - The function `myAtoi` is defined, taking a constant reference to a `std::string` as input and returning an integer. + +2. **Variable Initialization:** + ```cpp + long result = 0; + int sign = 1; // 1 represents positive sign, -1 represents negative sign + size_t i = 0; + ``` + - Initialize `result` to store the converted integer. + - Initialize `sign` to 1 by default (positive sign). + - Initialize `i` as an index to iterate through the string. + +3. **Skip Leading Whitespaces:** + ```cpp + while (i < str.length() && isspace(str[i])) { + i++; + } + ``` + - Use a `while` loop to skip leading whitespaces in the input string. + +4. **Check for Positive or Negative Sign:** + ```cpp + if (i < str.length() && (str[i] == '+' || str[i] == '-')) { + sign = (str[i++] == '-') ? -1 : 1; + } + ``` + - Check for the presence of a positive or negative sign. + - Update the `sign` accordingly and increment `i` if a sign is found. + +5. **Process Digits:** + ```cpp + while (i < str.length() && isdigit(str[i])) { + result = result * 10 + (str[i++] - '0'); + + // Check for overflow + if (result * sign > INT_MAX) { + return INT_MAX; + } else if (result * sign < INT_MIN) { + return INT_MIN; + } + } + ``` + - Iterate through the string, processing digits and updating the `result`. + - Check for overflow by comparing against `INT_MAX` and `INT_MIN`. Return appropriate values if overflow is detected. + +6. **Apply Sign and Return Result:** + ```cpp + return static_cast(result * sign); + } + ``` + - Apply the sign to the result and return the final converted integer. + +7. **Main Function:** + ```cpp + int main() { + std::string s = "42"; + std::cout << myAtoi(s) << std::endl; + return 0; + } + ``` + - In the `main` function, a test string "42" is provided, and the result of the `myAtoi` function is printed. + +The code essentially converts a given string to an integer, considering positive and negative signs, and handles overflow by checking against the limits of an integer. The `myAtoi` function returns the converted integer, and the `main` function tests it with the string "42". + +# java +# code +```java +import java.util.Scanner; + +public class Main { + + /** + * Converts a string to an integer. + * + * @param s The input string. + * @return The converted integer, or Integer.MIN_VALUE/Integer.MAX_VALUE in case of overflow. + */ + static int atoi(String s) { + // Check for null or empty string + if (s == null || s.isEmpty()) { + System.err.println("Error: Input string is null or empty."); + return 0; // You can choose an appropriate value in case of an error. + } + + int num = 0, i = 0, sign = 1; + + // Skip leading whitespaces + while (i < s.length() && s.charAt(i) == ' ') { + i++; + } + + // Check for sign + if (i < s.length() && (s.charAt(i) == '-' || s.charAt(i) == '+')) { + sign = (s.charAt(i) == '+') ? 1 : -1; + i++; + } + + // Process digits + while (i < s.length() && Character.isDigit(s.charAt(i))) { + int digit = s.charAt(i) - '0'; + // Check for overflow + if ((num > Integer.MAX_VALUE / 10) || ((num == Integer.MAX_VALUE / 10) && (digit > 7))) { + return (sign == 1) ? Integer.MAX_VALUE : Integer.MIN_VALUE; + } + + // Update the result + num = (num * 10) + digit; + i++; + } + + return num * sign; + } + + public static void main(String[] args) { + // Prompt the user for input + Scanner scanner = new Scanner(System.in); + System.out.print("Enter a string: "); + String input = scanner.nextLine(); + + // Call the atoi function and print the result + int result = atoi(input); + System.out.println("Converted integer: " + result); + } +} + +``` +# Code Explanation + +1. **Class Definition:** + ```java + public class Main { + ``` + - The class is named `Main`. + +2. **Static `atoi` Method:** + ```java + static int atoi(String s) { + ``` + - A static method named `atoi` is defined, which takes a `String` as input and returns an `int`. + +3. **Variable Initialization:** + ```java + int num = 0, i = 0, sign = 1; + ``` + - Initialize `num` to store the converted integer. + - Initialize `i` as an index to iterate through the string. + - Initialize `sign` to 1 by default (positive sign). + +4. **Skip Leading Whitespaces:** + ```java + while (i < s.length() && s.charAt(i) == ' ') { + i++; + } + ``` + - Use a `while` loop to skip leading whitespaces in the input string. + +5. **Check for Positive or Negative Sign:** + ```java + if (i < s.length() && (s.charAt(i) == '-' || s.charAt(i) == '+')) { + sign = s.charAt(i) == '+' ? 1 : -1; + i++; + } + ``` + - Check for the presence of a positive or negative sign. + - Update the `sign` accordingly and increment `i` if a sign is found. + +6. **Process Digits:** + ```java + while (i < s.length() && Character.isDigit(s.charAt(i))) { + int digit = s.charAt(i) - '0'; + if ((num > Integer.MAX_VALUE / 10) || ((num == Integer.MAX_VALUE / 10) && (digit > 7))) { + return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE; + } + num = (num * 10) + digit; + i++; + } + ``` + - Iterate through the string, processing digits and updating the `num`. + - Check for overflow by comparing against `Integer.MAX_VALUE`. + - Return `Integer.MAX_VALUE` or `Integer.MIN_VALUE` if overflow is detected. + +7. **Return Result:** + ```java + return num * sign; + } + ``` + - Apply the sign to the result (`num`) and return the final converted integer. + +8. **Main Method:** + ```java + public static void main(String[] args) { + String s = "42"; + System.out.println(atoi(s)); + } + ``` + - The `main` method tests the `atoi` method with the string "42" and prints the result. + +The code essentially converts a given string to an integer, considering positive and negative signs, and handles overflow by checking against the limits of an integer. The `atoi` method returns the converted integer, and the `main` method tests it with the string "42". + +**Time and Space Complexity Analysis**: + +The time and space complexity for the atoi method, which converts a string to an integer. + +# Time Complexity: + +The time complexity of the atoi method is O(n), where n is the length of the input string. + + The while loop for skipping leading whitespaces runs in O(n) time because, in the worst case, it may have to iterate through the entire string. + The subsequent while loop for processing digits also runs in O(n) time, where n is the length of the string. This loop iterates through the digits of the string, and each iteration takes constant time. + +Therefore, the overall time complexity is O(n). + +# Space Complexity: + +The space complexity of the atoi method is O(1), constant space. + + The variables num, i, and sign are integers and require constant space regardless of the size of the input string. + No additional data structures are used that scale with the input size. + +The space complexity is constant or O(1). + +# Real-World Applications of string to integer + +Converting a string to an integer is a common operation in various real-world applications. Here are some examples: + +1. **User Input Processing:** + - In applications that accept user input, such as command-line interfaces or forms on websites, string-to-integer conversion is necessary to interpret numeric inputs provided by users. + +2. **Data Validation:** + - When dealing with external data sources, such as files or databases, where data is often represented as strings, converting relevant string values to integers is crucial for data validation and ensuring that the data meets expected numeric formats. + +3. **Parsing and Interpreting Configuration Files:** + - Many applications use configuration files to store settings. When reading these files, string-to-integer conversion is employed to interpret numeric configurations, such as specifying the number of threads, timeouts, or buffer sizes. + +4. **Network Protocols:** + - In network programming, especially when dealing with protocols that transmit data in string format, converting certain fields to integers is common. For instance, parsing HTTP headers or handling custom network protocols may involve converting string representations of numerical values to integers. + +5. **Database Operations:** + - In database applications, where data is often retrieved in string format, converting relevant data to integers is necessary for performing numerical operations or ensuring consistency in data types. + +6. **Mathematical Computations:** + - In scientific computing or applications involving mathematical calculations, converting strings to integers is essential when processing mathematical expressions or user-provided numerical data. + +7. **Financial Applications:** + - In financial software, handling monetary values often involves converting string representations of amounts to integers (or floating-point numbers) for accurate calculations. + +8. **Gaming and Graphics Programming:** + - Game development and graphics programming may require converting string representations of parameters, such as screen dimensions or frame rates, to integers for configuring the game environment or rendering settings. + +9. **Command-Line Interfaces (CLI):** + - CLI applications often take user inputs as strings, and converting these inputs to integers is necessary for executing numeric commands or configuring the behavior of the application. + +10. **Sensor Data Processing:** + - In embedded systems or IoT applications, sensor data received in string format may need to be converted to integers for further analysis or decision-making based on numeric thresholds. + +In essence, string-to-integer conversion is a fundamental operation in software development, as it facilitates the integration and processing of numerical information in various domains. +In addition to the real-world applications mentioned earlier, let's explore some theoretical scenarios or examples where string-to-integer conversion plays a crucial role: + +1. **Algorithmic Problem Solving:** + - In algorithmic problems, you may encounter scenarios where input is provided as strings, and converting these strings to integers is a common step. For example, when implementing algorithms that involve mathematical computations, sorting, or searching, converting string inputs to integers allows for efficient processing. + +2. **String Matching Algorithms:** + - String matching algorithms, such as the Knuth-Morris-Pratt algorithm or the Boyer-Moore algorithm, may involve comparing and manipulating string representations of integers. Converting these strings to integers allows for efficient pattern matching and searching. + +3. **Abstract Data Types (ADTs):** + - In the design and implementation of abstract data types, converting string representations of numerical data to actual integers is often necessary. For instance, when implementing a priority queue where elements are associated with numeric priorities provided as strings, conversion to integers is crucial for proper ordering. + +4. **Parsing and Interpretation of Domain-Specific Languages:** + - When working with domain-specific languages or custom file formats, interpreting and parsing string representations of numeric values is fundamental. Converting these strings to integers allows for the proper interpretation of data and execution of language-specific commands. + +5. **Code Analysis and Transformation:** + - In static code analysis or transformation tools, understanding and manipulating numeric constants within code may require converting string representations to integers. This is useful for performing optimizations, refactoring, or analyzing code patterns. + +6. **Symbolic Computation:** + - In symbolic computation systems, where algebraic expressions are manipulated symbolically, converting string representations of numeric coefficients or constants to actual integers is necessary for performing mathematical operations. + +7. **Formal Language Theory:** + - In formal language theory, automata, and compilers, parsing numeric literals from strings is a common operation. Converting these literals to integers is crucial for generating executable code or performing language analysis. + +8. **Graph Theory and Network Analysis:** + - When working with graph representations, especially in scenarios where nodes or edges are labeled with numeric identifiers provided as strings, converting these identifiers to integers facilitates efficient graph algorithms and analysis. + +9. **Cryptography:** + - In cryptographic applications, handling cryptographic keys or parameters, often represented as strings, may require converting these string representations to integers for cryptographic operations. + +10. **Educational Examples:** + - In educational contexts, when teaching programming or algorithms, string-to-integer conversion serves as a fundamental concept. Exercises and examples often involve converting user inputs or string data to integers for various purposes. + +In theoretical scenarios, the importance of string-to-integer conversion arises in the context of solving abstract problems, designing algorithms, and implementing various computational concepts. diff --git a/Strings/Medium/String to Integer/main.java b/Strings/Medium/String to Integer/main.java new file mode 100644 index 00000000..267d143f --- /dev/null +++ b/Strings/Medium/String to Integer/main.java @@ -0,0 +1,57 @@ +import java.util.Scanner; + +public class Main { + + /** + * Converts a string to an integer. + * + * @param s The input string. + * @return The converted integer, or Integer.MIN_VALUE/Integer.MAX_VALUE in case of overflow. + */ + static int atoi(String s) { + // Check for null or empty string + if (s == null || s.isEmpty()) { + System.err.println("Error: Input string is null or empty."); + return 0; // You can choose an appropriate value in case of an error. + } + + int num = 0, i = 0, sign = 1; + + // Skip leading whitespaces + while (i < s.length() && s.charAt(i) == ' ') { + i++; + } + + // Check for sign + if (i < s.length() && (s.charAt(i) == '-' || s.charAt(i) == '+')) { + sign = (s.charAt(i) == '+') ? 1 : -1; + i++; + } + + // Process digits + while (i < s.length() && Character.isDigit(s.charAt(i))) { + int digit = s.charAt(i) - '0'; + // Check for overflow + if ((num > Integer.MAX_VALUE / 10) || ((num == Integer.MAX_VALUE / 10) && (digit > 7))) { + return (sign == 1) ? Integer.MAX_VALUE : Integer.MIN_VALUE; + } + + // Update the result + num = (num * 10) + digit; + i++; + } + + return num * sign; + } + + public static void main(String[] args) { + // Prompt the user for input + Scanner scanner = new Scanner(System.in); + System.out.print("Enter a string: "); + String input = scanner.nextLine(); + + // Call the atoi function and print the result + int result = atoi(input); + System.out.println("Converted integer: " + result); + } +} diff --git a/Strings/Medium/String to Integer/stringtointeger.cpp b/Strings/Medium/String to Integer/stringtointeger.cpp new file mode 100644 index 00000000..0dc06676 --- /dev/null +++ b/Strings/Medium/String to Integer/stringtointeger.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include + +// Function to convert a string to an integer +std::optional atoi(const std::string& s) { + // Check if the input string is empty + if (s.empty()) { + std::cerr << "Error: Input string is empty." << std::endl; + return std::nullopt; // Using std::optional to represent the absence of a value + } + + int num = 0, i = 0, sign = 1; + + // Skip leading whitespaces + while (i < s.length() && s[i] == ' ') { + i++; + } + + // Check for sign + if (i < s.length() && (s[i] == '-' || s[i] == '+')) { + // Determine the sign of the number + sign = (s[i] == '+') ? 1 : -1; + i++; + } + + // Process digits + while (i < s.length() && std::isdigit(s[i])) { + // Check for overflow + if ((num > std::numeric_limits::max() / 10) || + ((num == std::numeric_limits::max() / 10) && (s[i] - '0' > 7))) { + std::cerr << "Error: Integer overflow." << std::endl; + return std::nullopt; + } + + // Update the result by multiplying by 10 and adding the current digit + num = ((num * 10) + (s[i] - '0')); + i++; + } + + // Return the final result multiplied by the sign + return num * sign; +} + +int main() { + // Prompt the user for input + std::string input; + std::cout << "Enter a string: "; + std::getline(std::cin, input); + + // Call the atoi function and print the result + auto result = atoi(input); + if (result.has_value()) { + std::cout << "Converted integer: " << result.value() << std::endl; + } + + return 0; +} diff --git a/Strings/Medium/String to Integer/stringtointeger.py b/Strings/Medium/String to Integer/stringtointeger.py new file mode 100644 index 00000000..6b421639 --- /dev/null +++ b/Strings/Medium/String to Integer/stringtointeger.py @@ -0,0 +1,43 @@ +def convertStringToInteger(s): + # Define constants for the 32-bit signed integer range + INT_MIN, INT_MAX = -2**31, 2**31 - 1 + + # Initialize variables for the result, sign, and position in the string + answer, sign, i = 0, 1, 0 + + # Skip leading whitespaces + while i < len(s) and s[i] == " ": + i += 1 + + # Check for empty string or non-numeric input + if i == len(s) or (s[i] not in ["+", "-"] and not s[i].isdigit()): + print("Error: Invalid input. Please enter a valid numeric string.") + return None + + # Handle sign + if s[i] in ["+", "-"]: + # Set the sign based on the presence of a positive or negative sign + sign = -1 if s[i] == "-" else 1 + i += 1 + + # Process digits + while i < len(s) and s[i].isdigit(): + digit = int(s[i]) + # Check for overflow + if (INT_MAX - digit) // 10 < answer: + return INT_MAX if sign == 1 else INT_MIN + # Update the result by multiplying by 10 and adding the current digit + answer = answer * 10 + digit + i += 1 + + # Return the final result multiplied by the sign + return sign * answer + +if __name__ == "__main__": + # Example usage + input_string = input("Enter a string: ") + result = convertStringToInteger(input_string) + + # Check for None to handle cases of invalid input + if result is not None: + print("Converted integer:", result) diff --git a/Strings/Medium/palindrome partitioning/PalindromePartitioning.java b/Strings/Medium/palindrome partitioning/PalindromePartitioning.java new file mode 100644 index 00000000..d7a87445 --- /dev/null +++ b/Strings/Medium/palindrome partitioning/PalindromePartitioning.java @@ -0,0 +1,39 @@ +import java.util.ArrayList; +import java.util.List; + +class PalindromePartitioning { + static boolean checkPalindrome(String str, int startIndex, int lastIndex){ + while(startIndex <= lastIndex){ + if(str.charAt(startIndex) != str.charAt(lastIndex)) + return false; + startIndex++; + lastIndex--; + } + return true; + } + static void palindromePartition(int index, List ds, List> output, String str){ + if(index == str.length()){ + output.add(new ArrayList<>(ds)); + return; + } + for(int i = index; i < str.length(); i++){ + if(checkPalindrome(str, index, i)){ + ds.add(str.substring(index, i + 1)); + palindromePartition(i+1, ds, output, str); + ds.remove(ds.size()-1); + } + } + } + static List> partition(String s) { + List> output = new ArrayList<>(); + List ds = new ArrayList<>(); + palindromePartition(0, ds, output, s); + return output; + } + + public static void main(String[] args) { + String s="aab"; + System.out.println(partition(s)); + } + +} \ No newline at end of file diff --git a/Strings/Medium/palindrome partitioning/Readme.md b/Strings/Medium/palindrome partitioning/Readme.md new file mode 100644 index 00000000..4788ed4d --- /dev/null +++ b/Strings/Medium/palindrome partitioning/Readme.md @@ -0,0 +1,720 @@ +# Exploring palindrome partitioning using Strings: + +Exploring palindrome partitioning with strings often involves dynamic programming. The idea is to build a table or array to store intermediate results, helping to avoid redundant computations and improve the overall efficiency of the algorithm. + +# Introduction to Strings: + +A string is a sequence of characters enclosed in double or single quotes. It can contain letters, digits, special symbols, spaces, punctuation. +Strings, an indispensable data type in programming, serve as a versatile container for sequences of characters. One of the distinctive features of strings is their ability to store and process textual information. Whether it's a single word, a sentence, or even a larger body of text, strings provide a means to work with this data programmatically. In most programming languages, strings are typically enclosed within quotation marks, such as single (' ') or double (" ") quotes, allowing the interpreter or compiler to recognize them as string literals. + +# Introduction to the Palindrome Partitioning Theory: + +A palindrome is a sequence of characters that reads the same backward as forward. Examples include "level," "radar," and "madam". Palindrome partitioning is a concept in theoretical computer science and mathematics that involves breaking down a string into substrings, with the condition that each substring is a palindrome.The goal of palindrome partitioning is to find all possible ways to split a given string into palindromic substrings. + +# Overview of Palindrome Partitioning: + +Given a string, the problem is to find all possible ways to partition it into palindromic substrings. + +# PYTHON +# code + +```python +# Copyrights to venkys.io +# For more information, visit https://venkys.io + +# python program for Palindrome Partitioning +# Stable: No +# Inplace: No +# Adaptive: Not Applicable (Adaptivity is a characteristic more associated with sorting algorithms and may not directly apply to palindrome partitioning.) + +# Space complexity:O(n^2) +# Time complexity:O(n^2) + +def partition(string): + if string is None or not string.strip(): + return [[]] + + # Initialize dynamic programming array + dp = [[] for _ in range(len(string) + 1)] + + # Initialize the first state with an empty partition + dp[0] = [[]] + + # Iterate over each character of the string + for j in range(1, len(string) + 1): + # Iterate through each previous character + for i in range(j): + # Check if the substring is a palindrome + if string[i:j] == string[i:j][::-1]: + # If so, extend the partitions ending at i with the palindrome substring + for each in dp[i]: + dp[j].append(each + [string[i:j]]) + # Return the final state, which contains all valid partitions + return dp[-1] + +def main(): + # Read input from standard input + string = input("Enter a string: ") + + # Check for None or empty string + if string is None or not string.strip(): + print("Empty or None input. Exiting.") + return + + # Call the partition function and get the result + result = partition(string) + + # Print the result + print("Partitions:") + for partition_set in result: + print(partition_set) + + # Print the count of partitions + print(f"\nNumber of Partitions: {len(result)}") + +if __name__ == "__main__": + main() + +``` +# Code Explanation + +Let's go through each line of code in detail: + +```python +def partition(string): + # Check if the input string is None or empty + if string is None or not string.strip(): + return [[]] + + # Initialize dynamic programming array + + +```python + dp = [[] for _ in range(len(string) + 1)] +``` + +Here, a dynamic programming array `dp` is initialized. The list comprehension creates a list of empty lists. The length of this list is set to `len(string) + 1`. The array will be used to store intermediate results during the computation of palindrome partitions. + +```python + # Initialize the first state with an empty partition + dp[0] = [[]] +``` + +The first state of the dynamic programming array is initialized to contain a single empty list. This represents the partition of an empty string. + +```python + # Iterate over each character of the string + for j in range(1, len(string) + 1): +``` + +This loop iterates over each character in the input string. The variable `j` represents the current index being considered. + +```python + # Iterate through each previous character + for i in range(j): +``` + +Within the outer loop, there is another loop that iterates over each character from the beginning of the string up to the current index (`j`). The variable `i` represents the starting index of the substring. + +```python + # Check if the substring is a palindrome + if string[i:j] == string[i:j][::-1]: +``` + +This line checks whether the substring from index `i` to `j` is a palindrome. The slicing `string[i:j]` extracts the substring, and `string[i:j][::-1]` reverses it. If the reversed substring is equal to the original substring, it is a palindrome. + +```python + # If so, extend the partitions ending at i with the palindrome substring + for each in dp[i]: + dp[j].append(each + [string[i:j]]) +``` + +If the substring is a palindrome, this code extends the partitions that end at index `i` with the palindrome substring. It iterates over each partition in `dp[i]` and appends a new partition formed by adding the palindrome substring. + +```python + # Return the final state, which contains all valid partitions + return dp[-1] +``` + +Finally, the function returns the last state of the dynamic programming array, which contains all valid partitions for the entire input string. + +```python +def main(): + # Read input from standard input + string = input("Enter a string: ") +``` + +The `main` function prompts the user to input a string. + +```python + # Check for None or empty string + if string is None or not string.strip(): + print("Empty or None input. Exiting.") + return +``` + +This checks if the input string is None or empty. If so, it prints a message and exits the program. + +```python + # Call the partition function and get the result + result = partition(string) +``` + +The `partition` function is called with the input string, and the result is stored in the variable `result`. + +```python + # Print the result + print("Partitions:") + for partition_set in result: + print(partition_set) +``` + +The code then prints each partition set in the result. + +```python + # Print the count of partitions + print(f"\nNumber of Partitions: {len(result)}") +``` + +Finally, it prints the total number of partitions. The `len(result)` gives the count of valid partitions. + + +The algorithm employs dynamic programming to efficiently find and extend palindrome partitions in the input string. + +# C++ +# code + +```C++ +/* Copyrights to venkys.io +For more information, visit https://venkys.io */ + +// C++ program Palindrome Partitioning +// Stable: No +// Inplace: No +// Adaptive: Not applicable (Adaptivity is a characteristic more associated with sorting algorithms and may not directly apply to palindrome partitioning.) + +// Space complexity:O(n^2) +// Time complexity:O(n^2) + +/* Copyrights to venkys.io +For more information, visit https://venkys.io */ + +#include +#include +#include + +// Function to find all palindrome partitions of a given string +std::vector> partition(const std::string& str) { + // Check if the input string is empty + if (str.empty()) { + return {{}}; + } + + // Initialize dynamic programming array + std::vector>> dp(str.size() + 1); + + // Initialize the first state with an empty partition + dp[0] = {{}}; + + // Iterate over each character of the string + for (size_t j = 1; j <= str.size(); ++j) { + // Iterate through each previous character + for (size_t i = 0; i < j; ++i) { + // Check if the substring is a palindrome + if (str.substr(i, j - i) == std::string(str.rbegin() + (str.size() - j), str.rend())) { + // If so, extend the partitions ending at i with the palindrome substring + for (const auto& each : dp[i]) { + dp[j].push_back(each); + dp[j].back().push_back(str.substr(i, j - i)); + } + } + } + } + + // Return the final state, which contains all valid partitions + return dp.back(); +} + +int main() { + // Read input from standard input + std::cout << "Enter a string: "; + std::string inputString; + std::getline(std::cin, inputString); + + // Check for empty string + if (inputString.empty()) { + std::cout << "Empty input. Exiting." << std::endl; + return 0; + } + + // Call the partition function and get the result + auto result = partition(inputString); + + // Print the result + std::cout << "Partitions:" << std::endl; + for (const auto& partitionSet : result) { + for (const auto& substring : partitionSet) { + std::cout << substring << " "; + } + std::cout << std::endl; + } + + // Print the count of partitions + std::cout << "\nNumber of Partitions: " << result.size() << std::endl; + + return 0; +} + + +``` + +# Code Explanation + +Let's go through the C++ code step by step: + +```cpp +#include +#include +#include +``` + +1. **Header Includes:** + - `#include `: Provides input and output functionality. + - `#include `: Allows the use of dynamic arrays (vectors). + - `#include `: Enables string manipulation. + +```cpp +std::vector> partition(const std::string& str) { +``` + +2. **Function Declaration - `partition`:** + - Declares a function named `partition` that takes a constant reference to a string (`str`) as an argument. + - The function returns a vector of vectors of strings, representing the palindrome partitions. + +```cpp + if (str.empty()) { + return {{}}; + } +``` + +3. **Check for Empty String:** + - Checks if the input string is empty. If so, returns a vector containing an empty vector (representing an empty partition). + +```cpp + std::vector>> dp(str.size() + 1); +``` + +4. **Dynamic Programming Array Initialization:** + - Initializes a 3D vector (`dp`) to store intermediate results during dynamic programming. + - Its size is set to `str.size() + 1` to accommodate substrings of different lengths. + +```cpp + dp[0] = {{}}; +``` + +5. **Initial State Initialization:** + - Sets the initial state of `dp` to contain a single empty vector (representing an empty partition for an empty substring). + +```cpp + for (size_t j = 1; j <= str.size(); ++j) { + for (size_t i = 0; i < j; ++i) { +``` + +6. **Nested Loops - Iterating Over Substrings:** + - Outer loop (`j`): Iterates over each character of the input string. + - Inner loop (`i`): Iterates through each previous character (from 0 to `j`). + +```cpp + if (str.substr(i, j - i) == std::string(str.rbegin() + (str.size() - j), str.rend())) { +``` + +7. **Checking for Palindrome:** + - Uses `substr` to extract the substring from index `i` to `j`. + - Compares the substring with its reverse by creating a temporary reversed string using `rbegin` and `rend`. + +```cpp + for (const auto& each : dp[i]) { + dp[j].push_back(each); + dp[j].back().push_back(str.substr(i, j - i)); + } + } + } + } +``` + +8. **Extending Partitions:** + - If the substring is a palindrome, extends the partitions ending at index `i` with the palindrome substring. + - Iterates over each partition in `dp[i]` and appends a new partition formed by adding the palindrome substring. + +```cpp + return dp.back(); +} +``` + +9. **Returning Final State:** + - Returns the last state of the dynamic programming array (`dp`), which contains all valid partitions for the entire input string. + +```cpp +int main() { + std::cout << "Enter a string: "; + std::string inputString; + std::getline(std::cin, inputString); +``` + +10. **Main Function:** + - Prompts the user to enter a string using `std::cout`. + - Reads the entire line of input using `std::getline` and stores it in `inputString`. + +```cpp + if (inputString.empty()) { + std::cout << "Empty input. Exiting." << std::endl; + return 0; + } +``` + +11. **Checking for Empty String:** + - Checks if the input string is empty. If so, prints a message and exits the program. + +```cpp + auto result = partition(inputString); +``` + +12. **Calling the Partition Function:** + - Calls the `partition` function with the input string and stores the result in the variable `result`. + +```cpp + std::cout << "Partitions:" << std::endl; + for (const auto& partitionSet : result) { + for (const auto& substring : partitionSet) { + std::cout << substring << " "; + } + std::cout << std::endl; + } +``` + +13. **Printing Partitions:** + - Prints each partition set in the result. + +```cpp + std::cout << "\nNumber of Partitions: " << result.size() << std::endl; + + return 0; +} +``` + +14. **Printing the Count of Partitions:** + - Prints the total number of partitions using `result.size()`. The program then returns 0, indicating successful execution. + + + +# java +# code + +```java +/* Copyrights to venkys.io +For more information, visit https://venkys.io */ + +// java program Palindrome Partitioning +// Stable: No +// Inplace: No +// Adaptive: Not applicable (Adaptivity is a characteristic more associated with sorting algorithms and may not directly apply to palindrome partitioning.) + +// Space complexity:O(n^2) +// Time complexity:O(n^2) + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +public class PalindromePartition { + + public static List> partition(String input) { + // Check if the input string is null or empty + if (input == null || input.trim().isEmpty()) { + List> result = new ArrayList<>(); + result.add(new ArrayList<>()); + return result; + } + + // Initialize dynamic programming array + List>[] dp = new ArrayList[input.length() + 1]; + for (int i = 0; i <= input.length(); i++) { + dp[i] = new ArrayList<>(); + } + + // Initialize the first state with an empty partition + dp[0].add(new ArrayList<>()); + + // Iterate over each character of the string + for (int j = 1; j <= input.length(); j++) { + // Iterate through each previous character + for (int i = 0; i < j; i++) { + // Check if the substring is a palindrome + if (input.substring(i, j).equals(new StringBuilder(input.substring(i, j)).reverse().toString())) { + // If so, extend the partitions ending at i with the palindrome substring + for (List each : dp[i]) { + List partition = new ArrayList<>(each); + partition.add(input.substring(i, j)); + dp[j].add(partition); + } + } + } + } + + // Return the final state, which contains all valid partitions + return dp[input.length()]; + } + + public static void main(String[] args) { + // Read input from standard input + Scanner scanner = new Scanner(System.in); + System.out.print("Enter a string: "); + String inputString = scanner.nextLine(); + + // Check for null or empty string + if (inputString == null || inputString.trim().isEmpty()) { + System.out.println("Empty or null input. Exiting."); + return; + } + + // Call the partition function and get the result + List> result = partition(inputString); + + // Print the result + System.out.println("Partitions:"); + for (List partitionSet : result) { + System.out.println(partitionSet); + } + + // Print the count of partitions + System.out.println("\nNumber of Partitions: " + result.size()); + } +} + +``` +# Code Explanation + +Let's go through the provided Java code step by step: + +```java +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +``` + +1. **Imports:** + - Import necessary packages: `ArrayList` and `List` from `java.util` and `Scanner` for user input. + +```java +public class PalindromePartition { +``` + +2. **Class Declaration:** + - Declare a class named `PalindromePartition`. + +```java + /** + * Find all palindrome partitions of a given string. + * + * @param input The input string + * @return List of palindrome partitions + */ + public static List> partition(String input) { +``` + +3. **Method Declaration - partition:** + - Declare a method named `partition` that takes a string (`input`) as a parameter. + - The method returns a list of lists of strings, representing palindrome partitions. + - The `@param` and `@return` annotations provide documentation. + +```java + // Check if the input string is null or empty + if (input == null || input.trim().isEmpty()) { + // Return a list containing an empty list (representing an empty partition) + List> result = new ArrayList<>(); + result.add(new ArrayList<>()); + return result; + } +``` + +4. **Input Validation:** + - Check if the input string is null or empty. If true, return a list containing an empty list (representing an empty partition). + +```java + // Initialize dynamic programming array + List>[] dp = new ArrayList[input.length() + 1]; + for (int i = 0; i <= input.length(); i++) { + dp[i] = new ArrayList<>(); + } +``` + +5. **Dynamic Programming Array Initialization:** + - Initialize a 2D array (`dp`) to store intermediate results during dynamic programming. + - Each element of `dp` is a list of strings, representing partitions. + +```java + // Initialize the first state with an empty partition + dp[0].add(new ArrayList<>()); +``` + +6. **Initial State Initialization:** + - Set the initial state of `dp` to contain a single empty list (representing an empty partition for an empty substring). + +```java + // Iterate over each character of the string + for (int j = 1; j <= input.length(); j++) { + // Iterate through each previous character + for (int i = 0; i < j; i++) { +``` + +7. **Nested Loops - Iterating Over Substrings:** + - Outer loop (`j`): Iterates over each character of the input string. + - Inner loop (`i`): Iterates through each previous character (from 0 to `j`). + +```java + // Check if the substring is a palindrome + if (input.substring(i, j).equals(new StringBuilder(input.substring(i, j)).reverse().toString())) { +``` + +8. **Checking for Palindrome:** + - Use `substring` to extract the substring from index `i` to `j`. + - Compare the substring with its reverse using `StringBuilder`. + - If the substring is a palindrome: + +```java + // If so, extend the partitions ending at i with the palindrome substring + for (List each : dp[i]) { + List partition = new ArrayList<>(each); + partition.add(input.substring(i, j)); + dp[j].add(partition); + } + } + } + } +``` + +9. **Extending Partitions:** + - If the substring is a palindrome, extend the partitions ending at index `i` with the palindrome substring. + - Iterate over each partition in `dp[i]` and append a new partition formed by adding the palindrome substring. + +```java + // Return the final state, which contains all valid partitions + return dp[input.length()]; + } +``` + +10. **Returning Final State:** + - Return the last state of the dynamic programming array (`dp`), which contains all valid partitions for the entire input string. + +```java + /** + * Prompts the user for input and returns the entered string. + * + * @return The user-entered string + */ + private static String getUserInput() { + Scanner scanner = new Scanner(System.in); + System.out.print("Enter a string: "); + return scanner.nextLine(); + } +``` + +11. **User Input Method - getUserInput:** + - A private method to prompt the user for input and return the entered string. + +```java + public static void main(String[] args) { + // Uncomment the line below to read input from stdin + // String inputString = getUserInput(); + + // Example input (remove/comment this line if using stdin) + String inputString = "example"; +``` + +12. **Main Method:** + - The main method where the program starts execution. + - Optionally, read input from stdin by uncommenting the `getUserInput` line or provide an example input. + +```java + // Check for null or empty string + if (inputString == null || inputString.trim().isEmpty()) { + System.out.println("Empty or null input. Exiting."); + return; + } +``` + +13. **Input Validation in Main:** + - Check if the input string is null or empty. If true, print a message and exit the program. + +```java + // Call the partition function and get the result + List> result = partition(inputString); +``` + +14. **Calling partition Function:** + - Call the `partition` function with the input string and store the result in the variable `result`. + +```java + // Print the result + System.out.println("Partitions:"); + for (List partitionSet : result) { + System.out.println(partitionSet); + } +``` + +15. **Printing Partitions:** + - Print each partition set in the result. + +```java + // Print the count of partitions + System.out.println("\nNumber of Partitions: " + result.size()); + } +} +``` + +16. **Printing the Count of Partitions:** + - Print the total number of partitions using `result.size()`. The program then exits. + + +**Time and Space Complexity Analysis**: + +# Time Complexity: +O(n^2), where n is the length of the input string. +The dynamic programming table is typically a 2D array of size n x n, and each entry requires constant time to compute. +# Space Complexity: +O(n^2). +The space complexity is determined by the 2D table used for memoization or tabulation. + +# Real-World Applications of Palindrome Partitioning + +Palindrome partitioning, while primarily a concept used in algorithmic problem-solving, has applications in various real-world scenarios. Here are some areas where the concept of palindrome partitioning or similar techniques can be applied: + +1. **Genomic Sequence Analysis:** + - In bioinformatics, palindrome partitioning techniques can be used for analyzing DNA or RNA sequences. Identifying palindromic sequences can provide insights into the structure and function of genetic material. + +2. **Text Processing and Pattern Matching:** + - Palindrome partitioning concepts can be applied in text processing and pattern matching. For example, identifying palindromic patterns in text can be useful in natural language processing or searching for specific structures in documents. + +3. **Data Compression:** + - Palindromes can be leveraged in data compression algorithms. Identifying and encoding repeated palindromic patterns efficiently can contribute to the compression of data. + +4. **Cryptography:** + - In certain cryptographic algorithms, palindromic structures or related concepts might be employed for creating secure key structures or encoding information. + +5. **Speech Processing:** + - Palindrome partitioning techniques could be applied in speech processing, especially in the analysis of phonetic or acoustic sequences. + +6. **Fault-Tolerant Systems:** + - Palindrome-related algorithms can be used in fault-tolerant systems, where the identification of symmetric or repeating patterns helps in recognizing and correcting errors. + +7. **Robotics and Image Processing:** + - Palindrome partitioning methods may find applications in robotics and image processing. For instance, in image recognition, identifying symmetrical patterns can aid in object recognition. + +8. **Algorithmic Design and Optimization:** + - Understanding and solving problems related to palindrome partitioning contribute to the development of efficient algorithms. These algorithms, in turn, find applications in various computational fields, including data analysis and optimization. + +9. **Network Security:** + - Palindrome partitioning or related concepts might be used in analyzing network traffic patterns or identifying anomalies in network behavior, contributing to network security. + +10. **Game Design:** + - Some game algorithms may involve palindrome partitioning or similar techniques, especially in puzzle-solving or pattern recognition games. diff --git a/Strings/Medium/palindrome partitioning/palindromepartition.py b/Strings/Medium/palindrome partitioning/palindromepartition.py new file mode 100644 index 00000000..55e7f486 --- /dev/null +++ b/Strings/Medium/palindrome partitioning/palindromepartition.py @@ -0,0 +1,44 @@ +def partition(string): + if string is None or not string.strip(): + return [[]] + + # Initialize dynamic programming array + dp = [[] for _ in range(len(string) + 1)] + + # Initialize the first state with an empty partition + dp[0] = [[]] + + # Iterate over each character of the string + for j in range(1, len(string) + 1): + # Iterate through each previous character + for i in range(j): + # Check if the substring is a palindrome + if string[i:j] == string[i:j][::-1]: + # If so, extend the partitions ending at i with the palindrome substring + for each in dp[i]: + dp[j].append(each + [string[i:j]]) + # Return the final state, which contains all valid partitions + return dp[-1] + +def main(): + # Read input from standard input + string = input("Enter a string: ") + + # Check for None or empty string + if string is None or not string.strip(): + print("Empty or None input. Exiting.") + return + + # Call the partition function and get the result + result = partition(string) + + # Print the result + print("Partitions:") + for partition_set in result: + print(partition_set) + + # Print the count of partitions + print(f"\nNumber of Partitions: {len(result)}") + +if __name__ == "__main__": + main() diff --git a/Strings/Medium/palindrome partitioning/palindrompartition.c++ b/Strings/Medium/palindrome partitioning/palindrompartition.c++ new file mode 100644 index 00000000..cf2d1b7e --- /dev/null +++ b/Strings/Medium/palindrome partitioning/palindrompartition.c++ @@ -0,0 +1,68 @@ +/* Copyrights to venkys.io +For more information, visit https://venkys.io */ + +#include +#include +#include + +// Function to find all palindrome partitions of a given string +std::vector> partition(const std::string& str) { + // Check if the input string is empty + if (str.empty()) { + return {{}}; + } + + // Initialize dynamic programming array + std::vector>> dp(str.size() + 1); + + // Initialize the first state with an empty partition + dp[0] = {{}}; + + // Iterate over each character of the string + for (size_t j = 1; j <= str.size(); ++j) { + // Iterate through each previous character + for (size_t i = 0; i < j; ++i) { + // Check if the substring is a palindrome + if (str.substr(i, j - i) == std::string(str.rbegin() + (str.size() - j), str.rend())) { + // If so, extend the partitions ending at i with the palindrome substring + for (const auto& each : dp[i]) { + dp[j].push_back(each); + dp[j].back().push_back(str.substr(i, j - i)); + } + } + } + } + + // Return the final state, which contains all valid partitions + return dp.back(); +} + +int main() { + // Read input from standard input + std::cout << "Enter a string: "; + std::string inputString; + std::getline(std::cin, inputString); + + // Check for empty string + if (inputString.empty()) { + std::cout << "Empty input. Exiting." << std::endl; + return 0; + } + + // Call the partition function and get the result + auto result = partition(inputString); + + // Print the result + std::cout << "Partitions:" << std::endl; + for (const auto& partitionSet : result) { + for (const auto& substring : partitionSet) { + std::cout << substring << " "; + } + std::cout << std::endl; + } + + // Print the count of partitions + std::cout << "\nNumber of Partitions: " << result.size() << std::endl; + + return 0; +} diff --git a/Trees/Easy/Inorder Traversal/BinaryTree.java b/Trees/Easy/Inorder Traversal/BinaryTree.java new file mode 100644 index 00000000..48eb5d4e --- /dev/null +++ b/Trees/Easy/Inorder Traversal/BinaryTree.java @@ -0,0 +1,64 @@ +import java.util.Scanner; + +class TreeNode { + int data; + TreeNode left = null; // Left child reference + TreeNode right = null; // Right child reference + + // Constructor to create a new TreeNode + public TreeNode(int data) { + this.data = data; // Assign the given data to this node + } +} + +public class BinaryTree { + // Method for inorder traversal + static void inorderTraversal(TreeNode root) { + if (root != null) { + inorderTraversal(root.left); + System.out.print(root.data + " "); + inorderTraversal(root.right); + } + } + + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + + // Prompt user to enter the series of numbers + System.out.println("Enter a series of numbers separated by spaces:"); + String input = scanner.nextLine(); + String[] values = input.split("\\s+"); + + // Convert the string values to integers + int[] intValues = new int[values.length]; + for (int i = 0; i < values.length; i++) { + intValues[i] = Integer.parseInt(values[i]); + } + + // Build the binary tree using the series of numbers + TreeNode root = buildBinaryTree(intValues, 0); + + // Printing the message before performing inorder traversal + System.out.print("Inorder Traversal: "); + + // Calling the inorderTraversal method to perform inorder traversal + inorderTraversal(root); + + // Close the scanner + scanner.close(); + } + + // Method to build the binary tree using the series of numbers + static TreeNode buildBinaryTree(int[] values, int index) { + TreeNode node = null; + if (index < values.length) { + // Create a new node with the current value + node = new TreeNode(values[index]); + + // Recursively build the left and right subtrees + node.left = buildBinaryTree(values, 2 * index + 1); + node.right = buildBinaryTree(values, 2 * index + 2); + } + return node; + } +} diff --git a/Trees/Easy/Inorder Traversal/Readme.md b/Trees/Easy/Inorder Traversal/Readme.md new file mode 100644 index 00000000..cbf42a4e --- /dev/null +++ b/Trees/Easy/Inorder Traversal/Readme.md @@ -0,0 +1,544 @@ +# Exploring Binary Trees and Inorder Traversal + +## Introduction to Trees +A tree is a hierarchical data structure which is composed of nodes, where each node has at maximum two children and they are called as left child and right child. This interconnected system is reminiscent of an upside-down tree, with the topmost node called the root. In this structure, nodes hold data, and edges represent connections between nodes. Trees play a crucial role in computer science, serving as versatile tools for tasks such as searching, sorting, and analysis. Each node's ability to have multiple children allows for the creation of complex hierarchical relationships, making trees an indispensable concept for organizing and manipulating data efficiently. Whether utilized for representing hierarchical relationships, optimizing search algorithms, or structuring databases, trees form a foundational element in computer science, contributing to the systematic and effective management of information. + +## Introduction to Inorder Traversal +Inorder traversal is a method of traversing a binary tree in a specific order. It starts from the leftmost node and moves towards the rightmost node, visiting the left subtree first, then the root, and finally the right subtree. This traversal strategy is fundamental for exploring and processing binary trees systematically. + +## Overview +The provided Python code defines a binary tree node using a class named `Node` and demonstrates recursive inorder traversal. The `Node` class has a constructor initializing the node with some data and two pointers, `left` and `right`, representing the left and right children. + +## Step-by-Step Explanation + +# Using Python +# code + +```python +class Node: + def __init__(self,data): + self.data=data + self.left=self.right=None +# Recursive Inorder Traversal. +def inorder(root): + # Check if the root exists + if root: + # Perform an inorder traversal on the left subtree + inorder(root.left) + # Print the value of the current node + print(root.data,end=" ") + # Perform an inorder traversal on the right subtree + inorder(root.right) + # Driver code to test above function +# The values of nodes are given below : +if __name__=="__main__": + root_data = int(input()) + root = Node(root_data) + + left_data = int(input()) + if left_data != -1: + root.left = Node(left_data) + + right_data = int(input()) + if right_data != -1: + root.right = Node(right_data) + + left_left_data = int(input()) + if left_left_data != -1: + root.left.left = Node(left_left_data) + + left_right_data = int(input()) + if left_right_data != -1: + root.left.right = Node(left_right_data) + + right_left_data = int(input()) + if right_left_data != -1: + root.right.left = Node(right_left_data) + + right_right_data = int(input()) + if right_right_data != -1: + root.right.right = Node(right_right_data) + inorder(root) +``` + +**Explanation:** + +1. **Node Class Definition:** + +```python +class Node: + def __init__(self, data): + self.data = data + self.left = self.right = None +``` + +This class defines the structure of a binary tree node. Each node has a data attribute to store the value of the node, and left and right attributes to point to the left and right children, respectively. + +2. **Inorder Traversal Function:** + +```python +def inorder(root): + if root: + inorder(root.left) + print(root.data, end=" ") + inorder(root.right) +``` + +This function, inorder, is a recursive implementation of the inorder traversal algorithm. It takes a root node as an argument and performs the following steps: +=> Check if the current root exists. +=> Recursively traverse the left subtree. +=> Print the value of the current node. +=> Recursively traverse the right subtree. + +3.**Driver Code and Tree Initialization** + +```python + if __name__ == "__main__": + root = Node(80) + root.left = Node(20) + root.right = Node(30) + root.left.left = Node(40) + root.left.right = Node(350) + root.right.left = Node(460) + root.right.right = Node(70) +``` + +In the __main__ block, a binary tree is created with the following structure: + + 80 + / \ + 20 30 + / \ / \ + 40 350 460 70 + + +4. **Inorder Traversal Call** + +```python + inorder(root) +``` + +This line calls the inorder function with the root of the tree, initiating the inorder traversal. The result is the values of the nodes printed in ascending order. + +In summary, the code defines a binary tree, initializes it with specific values, and then performs an inorder traversal, printing the values of the nodes in ascending order. The recursive nature of the inorder function is key to traversing the tree systematically. + +# using C++ +# code +```c++ +#include + +class Node{ + public: + int data; + Node *left=NULL; + Node *right=NULL; + + // Node constructor initializes data + Node(int val){ + data=val; + } +}; +void inorder(Node* root){ + // call inorder for left subtree + if(root!=NULL){ + inorder(root->left); + // Print root node data + std::cout<data<<" "; + // call inorder for right subtree + inorder(root->right); + } +} + +int main(){ + // Creating the binary tree + int value; + // std::cout << "Enter value for root node: "; + std::cin >> value; + Node* root = new Node(value); + + // std::cout << "Enter value for left child of root node (or -1 if none): "; + std::cin >> value; + if (value != -1) + root->left = new Node(value); + + // std::cout << "Enter value for right child of root node (or -1 if none): "; + std::cin >> value; + if (value != -1) + root->right = new Node(value); + + // std::cout << "Enter value for left child of left child of root node (or -1 if none): "; + std::cin >> value; + if (value != -1 && root->left != nullptr) + root->left->left = new Node(value); + + // std::cout << "Enter value for right child of left child of root node (or -1 if none): "; + std::cin >> value; + if (value != -1 && root->left != nullptr) + root->left->right = new Node(value); + + // std::cout << "Enter value for left child of right child of root node (or -1 if none): "; + std::cin >> value; + if (value != -1 && root->right != nullptr) + root->right->left = new Node(value); + + // std::cout << "Enter value for right child of right child of root node (or -1 if none): "; + std::cin >> value; + if (value != -1 && root->right != nullptr) + root->right->right = new Node(value); + + // Displaying the binary tree in inorder traversal + inorder(root); + // Inorder traversal of the tree + return 0; +} +``` +**Explanation** + +1. **Header Inclusion**: + + ```cpp + #include + ``` + + This line includes a standard C++ library header, which is a common practice to include necessary headers like `` for input and output. + +2. **Node Class Definition:** + + ```cpp + class Node { + public: + int data; + Node *left = NULL; + Node *right = NULL; + + // Node constructor initializes data + Node(int val) { + data = val; + } + }; + ``` + + This part defines a `Node` class representing nodes in a binary tree. Each node contains an integer `data`, a pointer to the left child (`left`), and a pointer to the right child (`right`). The constructor initializes the node with the provided data value. + +3. **Inorder Traversal Function:** + + ```cpp + void inorder(Node* root) { + // call inorder for the left subtree + if (root != NULL) { + inorder(root->left); + // Print root node data + std::cout << root->data << " "; + // call inorder for the right subtree + inorder(root->right); + } + } + ``` + + The `inorder` function performs recursive inorder traversal on the binary tree. It prints the data of each node in ascending order by first traversing the left subtree, then printing the root node's data, and finally traversing the right subtree. + +4. **Main Function:** + + ```cpp + int main() { + // Creating the binary tree + Node* root = new Node(10); + root->left = new Node(20); + root->right = new Node(30); + root->left->left = new Node(40); + root->left->right = new Node(50); + root->right->left = new Node(60); + root->right->right = new Node(70); + + // Displaying the binary tree in inorder traversal + inorder(root); + + // Inorder traversal of the tree + return 0; + } + ``` + + In the `main` function, a binary tree is created with specific values. Nodes are allocated dynamically using the `new` keyword. The `inorder` function is then called to display the binary tree in inorder traversal, printing the values of the nodes in ascending order. + +In summary, this C++ program defines a binary tree using a `Node` class, creates a binary tree with specific values, and performs an inorder traversal to display the tree's content. The code demonstrates the concept of recursive traversal in a binary tree. +This code will output `40 20 50 10 60 30 70`, which is a correct + +# using Java +# Code + +```java +class TreeNode { + int data; + // Store integer data + TreeNode left = null, + // Left child reference + right = null; + // Right child reference + + public TreeNode(int data) { + // Constructor to create a new TreeNode + this.data = data; + // Assign the given data to this node + } +} + +public class BinaryTree { +// Method for inorder traversal + + static void inorderTraversal(TreeNode root) { + if (root != null) { + inorderTraversal(root.left); + System.out.print(root.data + " "); + inorderTraversal(root.right); + } + } + + public static void main(String[] args) { + + Scanner scanner = new Scanner(System.in); + + // Input for the value of the root node + // System.out.print("Enter value for root node: "); + int rootValue = scanner.nextInt(); + TreeNode root = new TreeNode(rootValue); + + // Input for the left child of the root node + // System.out.print("Enter value for left child of root node (or -1 if none): "); + int leftChildValue = scanner.nextInt(); + if (leftChildValue != -1) + root.left = new TreeNode(leftChildValue); + + // Input for the right child of the root node + // System.out.print("Enter value for right child of root node (or -1 if none): "); + int rightChildValue = scanner.nextInt(); + if (rightChildValue != -1) + root.right = new TreeNode(rightChildValue); + + // Input for the left child of the left child of the root node + if (root.left != null) { + // System.out.print("Enter value for left child of left child of root node (or -1 if none): "); + int leftLeftChildValue = scanner.nextInt(); + if (leftLeftChildValue != -1) + root.left.left = new TreeNode(leftLeftChildValue); + } + + // Input for the right child of the left child of the root node + if (root.left != null) { + // System.out.print("Enter value for right child of left child of root node (or -1 if none): "); + int leftRightChildValue = scanner.nextInt(); + if (leftRightChildValue != -1) + root.left.right = new TreeNode(leftRightChildValue); + } + + // Input for the left child of the right child of the root node + if (root.right != null) { + // System.out.print("Enter value for left child of right child of root node (or -1 if none): "); + int rightLeftChildValue = scanner.nextInt(); + if (rightLeftChildValue != -1) + root.right.left = new TreeNode(rightLeftChildValue); + } + + // Input for the right child of the right child of the root node + if (root.right != null) { + // System.out.print("Enter value for right child of right child of root node (or -1 if none): "); + int rightRightChildValue = scanner.nextInt(); + if (rightRightChildValue != -1) + root.right.right = new TreeNode(rightRightChildValue); + } + + // Close the scanner + scanner.close(); + + // Printing the message before performing inorder traversal + // System.out.print("Inorder Traversal: "); + + // Calling the inorderTraversal method to perform inorder traversal + inorderTraversal(root); + } +} + +``` +**explanation** + +1. **TreeNode Class Definition:** + ```java + class TreeNode { + int data; + TreeNode left = null, right = null; + + public TreeNode(int data) { + this.data = data; + } + } + ``` + + - `TreeNode` class defines the structure of a binary tree node. + - It has an `int` variable `data` to store the node's value and references (`left` and `right`) to its left and right children. + - The constructor initializes a new `TreeNode` with the given data. + +2. **BinaryTree Class:** + ```java + public class BinaryTree { + // Method for inorder traversal + + static void inorderTraversal(TreeNode root) { + if (root != null) { + inorderTraversal(root.left); + System.out.print(root.data + " "); + inorderTraversal(root.right); + } + } + ``` + - This class contains a method `inorderTraversal` that performs recursive inorder traversal on a binary tree. + - It prints the data of each node in ascending order by first traversing the left subtree, then printing the root node's data, and finally traversing the right subtree. + - InorderTraversal` is a static method used for inorder traversal of the binary tree. + - It is a recursive function that traverses the left subtree, prints the data of the current node, and then traverses the right subtree. + +3. **Main Method:** + ```java + public static void main(String[] args) { + // Creating a binary tree with root node having data 10 + TreeNode root = new TreeNode(10); + + // Adding nodes to the root of the created binary tree + // Adding left and right child nodes to the root + root.left = new TreeNode(20); + root.right = new TreeNode(30); + + // Adding left and right child nodes to the left child of the root + root.left.left = new TreeNode(40); + root.left.right = new TreeNode(50); + + // Adding left and right child nodes to the right child of the root + root.right.left = new TreeNode(60); + root.right.right = new TreeNode(70); + + // Printing the message before performing inorder traversal + System.out.print("Inorder Traversal: "); + + // Calling the inorderTraversal method to perform inorder traversal + inorderTraversal(root); + } + ``` + + - The `main` method is the entry point of the program. + - It creates an instance of the `TreeNode` class, representing the root of the binary tree, with a data value of 10. + - Child nodes are added to the root, forming a binary tree structure. + - The `inorderTraversal` method is called to perform an inorder traversal of the tree, printing the nodes in ascending order. + + +4. **Tree Structure:** + ``` + 10 + / \ + 20 30 + / \ / \ + 40 50 60 70 + ``` + +5. **Output:** + ``` + Inorder Traversal: 40 20 50 10 60 30 70 + ``` + +In summary, this Java program demonstrates the creation of a binary tree, addition of nodes, and the execution of an inorder traversal. The `TreeNode` class encapsulates the node structure, and the `BinaryTree` class utilizes it to construct and traverse the binary tree. + + +## Time and Space Complexity Analysis +- Time Complexity: The time complexity of the inorder traversal is O(n), where n is the number of nodes in the tree. This is because each node is visited once. +- Space Complexity: The space complexity is O(h), where h is the height of the binary tree. In the worst case (skewed tree), the height is n, making the space complexity O(n). In a balanced tree, the height is log(n), resulting in a space complexity of O(log(n)). + +## Real-World Applications +Binary trees and inorder traversal have applications in various domains, including: +- **Database Indexing:** Binary trees are used in database systems to efficiently index and search for records. +- **Expression Trees:** In compilers, expression trees are used to represent mathematical expressions for efficient evaluation. +- **File Systems:** File systems often use tree structures to organize and locate files efficiently. + +## Test Cases: + +- Input: + Values of Nodes: + The user inputs the values of each node in the binary tree. If a node does not have a left or right child, the user enters -1. + + For example, let's consider the following input: + 1 + 2 + 3 + 4 + -1 + 5 + -1 + -1 + 6 + + Output: + The output will be the inorder traversal of the constructed binary tree. + For the provided input, the output would be: + 4 2 1 5 3 6 + + Explanation: + Input Interpretation: + - The user inputs the value of the root node, which is 1. + - Then, the user inputs the values of the left and right children of the root node, which are 2 and 3, respectively. + - Further, the user inputs the values of the left child's left child, which is 4. + - Then, the user inputs the value of the left child's right child, which is -1, indicating that there is no right child for node 2. + - Similarly, the user inputs the value of the right child's left child, which is 5, and then inputs -1 for the right child's right child. + - Finally, the user inputs the value of the right child's right child, which is 6. + + Binary Tree Construction: + - Based on the input provided, the binary tree is constructed as follows: +markdown + 1 + / \ + 2 3 + / \ + 4 6 + \ + 5 + + Inorder Traversal: + - The inorder traversal of the constructed binary tree is performed recursively. + - The values are printed in non-decreasing order as 4 2 1 5 3 6, which represents the inorder traversal path from the root node. + +- Input: + Values of Nodes: + The user inputs the values of each node in the binary tree. If a node does not have a left or right child, the user enters -1. + For example, let's consider the following input: + 10 + 20 + 30 + 40 + -1 + 50 + -1 + -1 + 60 + Output: + The output will be the inorder traversal of the constructed binary tree. + For the provided input, the output would be: + 40 20 50 10 30 60 + + Explanation: + Input Interpretation: + - The user inputs the value of the root node, which is 10. + - Then, the user inputs the values of the left and right children of the root node, which are 20 and 30, respectively. + - Further, the user inputs the values of the left child's left child, which is 40. + - Then, the user inputs the value of the left child's right child, which is -1, indicating that there is no right child for node 20. + - Similarly, the user inputs the value of the right child's left child, which is 50, and then inputs -1 for the right child's right child. + - Finally, the user inputs the value of the right child's right child, which is 60. + + Binary Tree Construction: + - Based on the input provided, the binary tree is constructed as follows: + 10 + / \ + 20 30 + / / + 40 50 + \ + 60 + Inorder Traversal: + - The inorder traversal of the constructed binary tree is performed recursively. + - The values are printed in non-decreasing order as 40 20 50 10 30 60, which represents the inorder traversal path from the root node. + +## Conclusion +Understanding trees and traversal algorithms, such as inorder traversal, is crucial in computer science. These codes provide a practical example of how to implement and apply inorder traversal in different languages like Python ,C++ and Java. The recursive nature of the traversal allows for elegant and concise code, making it a powerful tool for tree-related operations. diff --git a/Trees/Easy/Inorder Traversal/inorder.cpp b/Trees/Easy/Inorder Traversal/inorder.cpp new file mode 100644 index 00000000..3a45d169 --- /dev/null +++ b/Trees/Easy/Inorder Traversal/inorder.cpp @@ -0,0 +1,74 @@ +#include +#include + +// Definition for a binary tree node +class Node { +public: + int data; + Node* left = nullptr; + Node* right = nullptr; + + // Node constructor initializes data + Node(int val) : data(val) {} +}; + +// Recursive Inorder Traversal +void inorder(Node* root) { + if (root != nullptr) { + // Traverse the left subtree + inorder(root->left); + + // Print the value of the current node + std::cout << root->data << " "; + + // Traverse the right subtree + inorder(root->right); + } +} + +// Function to build a binary tree interactively based on user input +Node* buildTree() { + int rootData; + std::cout << "Enter the value for the current node: "; + std::cin >> rootData; + + auto root = new Node(rootData); + + char hasLeftChild, hasRightChild; + + // Ask user if the current node has a left child + std::cout << "Does the current node have a left child? (y/n): "; + std::cin >> hasLeftChild; + + // If yes, recursively build the left subtree + if (hasLeftChild == 'y') { + root->left = buildTree(); + } + + // Ask user if the current node has a right child + std::cout << "Does the current node have a right child? (y/n): "; + std::cin >> hasRightChild; + + // If yes, recursively build the right subtree + if (hasRightChild == 'y') { + root->right = buildTree(); + } + + return root; +} + +int main() { + try { + std::cout << "Interactive Binary Tree Construction\n"; + Node* root = buildTree(); + + std::cout << "\nInorder Traversal: "; + inorder(root); + std::cout << std::endl; + } catch (const std::exception& e) { + // Handle exceptions, if any + std::cerr << "Error: " << e.what() << std::endl; + } + + return 0; +} diff --git a/Trees/Easy/Inorder Traversal/inorder.py b/Trees/Easy/Inorder Traversal/inorder.py new file mode 100644 index 00000000..609fa126 --- /dev/null +++ b/Trees/Easy/Inorder Traversal/inorder.py @@ -0,0 +1,49 @@ +# Definition for a binary tree node +class Node: + def __init__(self, data): + self.data = data + self.left = self.right = None + +# Recursive Inorder Traversal. +def inorder(root): + # Check if the root exists + if root: + # Perform an inorder traversal on the left subtree + inorder(root.left) + # Print the value of the current node + print(root.data, end=" ") + # Perform an inorder traversal on the right subtree + inorder(root.right) + +if __name__ == "__main__": + try: + # Prompt the user to enter values for the binary tree + root_data = int(input("Enter the value for the root node: ")) + root = Node(root_data) + + left_data = int(input("Enter the value for the left child of the root node: ")) + root.left = Node(left_data) + + right_data = int(input("Enter the value for the right child of the root node: ")) + root.right = Node(right_data) + + left_left_data = int(input("Enter the value for the left child of the left child: ")) + root.left.left = Node(left_left_data) + + left_right_data = int(input("Enter the value for the right child of the left child: ")) + root.left.right = Node(left_right_data) + + right_left_data = int(input("Enter the value for the left child of the right child: ")) + root.right.left = Node(right_left_data) + + right_right_data = int(input("Enter the value for the right child of the right child: ")) + root.right.right = Node(right_right_data) + + # Perform Inorder Traversal + print("Inorder Traversal:") + inorder(root) + + except ValueError: + print("Error: Please enter valid integer values for the nodes.") + except Exception as e: + print(f"Error: {e}")