From 0dbb276a2901348bef720657e6debdb8f74533f3 Mon Sep 17 00:00:00 2001 From: Mayank-tech69 Date: Wed, 22 Jan 2025 20:28:52 +0530 Subject: [PATCH 1/2] added sorting algorithms --- pydatastructs/sorting/MERGE SORT/algorithm.md | 88 +++++++++++++++++ pydatastructs/sorting/MERGE SORT/code.py | 55 +++++++++++ .../sorting/MERGE SORT/documentation.md | 97 +++++++++++++++++++ .../sorting/QUICK SORT/algorithm.txt | 53 ++++++++++ pydatastructs/sorting/QUICK SORT/code.py | 58 +++++++++++ .../sorting/QUICK SORT/documentation.md | 80 +++++++++++++++ 6 files changed, 431 insertions(+) create mode 100644 pydatastructs/sorting/MERGE SORT/algorithm.md create mode 100644 pydatastructs/sorting/MERGE SORT/code.py create mode 100644 pydatastructs/sorting/MERGE SORT/documentation.md create mode 100644 pydatastructs/sorting/QUICK SORT/algorithm.txt create mode 100644 pydatastructs/sorting/QUICK SORT/code.py create mode 100644 pydatastructs/sorting/QUICK SORT/documentation.md diff --git a/pydatastructs/sorting/MERGE SORT/algorithm.md b/pydatastructs/sorting/MERGE SORT/algorithm.md new file mode 100644 index 00000000..c76216de --- /dev/null +++ b/pydatastructs/sorting/MERGE SORT/algorithm.md @@ -0,0 +1,88 @@ +# Merge Sort Algorithm + +## **Merge Sort** + +### **Input** +An unsorted array `A` of size `n`. + +### **Output** +A sorted array `A` in ascending order. + +--- + +## **Algorithm Description** + +1. **Base Case** + - If the size of the array `A` is 1 or less, it is already sorted. Return the array. + +2. **Divide Step** + - Find the middle index `mid` of the array: + $ \text{mid} = \text{start} + \frac{\text{end} - \text{start}}{2} $. + - Split the array `A` into two subarrays: + - Left subarray: `A[start...mid]` + - Right subarray: `A[mid+1...end]`. + +3. **Conquer Step** + - Recursively apply Merge Sort on the left subarray `A[start...mid]`. + - Recursively apply Merge Sort on the right subarray `A[mid+1...end]`. + +4. **Merge Step** + - Merge the two sorted subarrays `A[start...mid]` and `A[mid+1...end]` into a single sorted array. + - Compare elements from both subarrays. + - Place the smaller element into the result array and move the pointer of the corresponding subarray. + - Repeat this process until all elements are merged. + - If one subarray is exhausted, append the remaining elements from the other subarray to the result. + +5. **Return Step** + - Once all recursive calls are complete and merging is done, the entire array `A` will be sorted. + +--- + +## **Time Complexity** + +- **Best Case**: \(O(n \log n)\) + This occurs when the array is already sorted but still requires dividing and merging steps. + +- **Average Case**: \(O(n \log n)\) + Most inputs result in balanced divisions and regular merging. + +- **Worst Case**: \(O(n \log n)\) + Even in the worst-case scenario, all divisions and merging steps require consistent work. + +--- + +## **Space Complexity** + +- **Auxiliary Space**: \(O(n)\) + Merge Sort requires extra memory to store temporary arrays during the merging step. + +--- + +## **Example Walkthrough** + +### Input Array: +`[38, 27, 43, 3, 9, 82, 10]` + +### Step-by-Step Process: + +1. **Initial Split**: + - Divide `[38, 27, 43, 3, 9, 82, 10]` into `[38, 27, 43]` and `[3, 9, 82, 10]`. + +2. **Recursive Splitting**: + - `[38, 27, 43]` becomes `[38]`, `[27]`, and `[43]`. + - `[3, 9, 82, 10]` becomes `[3]`, `[9]`, `[82]`, `[10]`. + +3. **Merging Subarrays**: + - Merge `[38]` and `[27]` into `[27, 38]`. + - Merge `[27, 38]` with `[43]` into `[27, 38, 43]`. + - Similarly, merge `[3]`, `[9]`, `[82]`, `[10]` into `[3, 9, 10, 82]`. + +4. **Final Merge**: + - Merge `[27, 38, 43]` with `[3, 9, 10, 82]` into `[3, 9, 10, 27, 38, 43, 82]`. + +### Output: +`[3, 9, 10, 27, 38, 43, 82]` + +--- + + diff --git a/pydatastructs/sorting/MERGE SORT/code.py b/pydatastructs/sorting/MERGE SORT/code.py new file mode 100644 index 00000000..d5d67bc6 --- /dev/null +++ b/pydatastructs/sorting/MERGE SORT/code.py @@ -0,0 +1,55 @@ +def merge_sort(arr): + """ + Function to perform Merge Sort on a given list. + + Args: + arr (list): Unsorted list to be sorted. + + Returns: + list: Sorted list in ascending order. + """ + if len(arr) > 1: + # Finding the middle of the array to divide it + mid = len(arr) // 2 + + # Dividing the array into two halves + left_half = arr[:mid] + right_half = arr[mid:] + + # Recursively sorting both halves + merge_sort(left_half) + merge_sort(right_half) + + # Initializing pointers for left, right, and the merged array + i = j = k = 0 + + # Merging the two sorted halves into a single sorted array + while i < len(left_half) and j < len(right_half): + if left_half[i] < right_half[j]: + arr[k] = left_half[i] + i += 1 + else: + arr[k] = right_half[j] + j += 1 + k += 1 + + # Checking if any element remains in left_half + while i < len(left_half): + arr[k] = left_half[i] + i += 1 + k += 1 + + # Checking if any element remains in right_half + while j < len(right_half): + arr[k] = right_half[j] + j += 1 + k += 1 + + return arr + +# Example usage: +if __name__ == "__main__": + example_arr = [38, 27, 43, 3, 9, 82, 10] + print("Original Array:", example_arr) + sorted_arr = merge_sort(example_arr) + print("Sorted Array:", sorted_arr) diff --git a/pydatastructs/sorting/MERGE SORT/documentation.md b/pydatastructs/sorting/MERGE SORT/documentation.md new file mode 100644 index 00000000..36f7e13c --- /dev/null +++ b/pydatastructs/sorting/MERGE SORT/documentation.md @@ -0,0 +1,97 @@ +# Merge Sort Algorithm Documentation + +## Introduction + +Merge Sort is a classic, comparison-based sorting algorithm that uses the **divide-and-conquer** paradigm. It divides the input array into smaller subarrays, recursively sorts those subarrays, and then merges them back together to form the final sorted array. Known for its stability and predictable performance, Merge Sort is an essential tool in many computational applications. + +--- + +## Core Concept + +The core principle of Merge Sort involves breaking the problem into smaller, manageable parts: +1. **Divide**: The input array is divided into two halves until subarrays of size 1 are achieved. +2. **Conquer**: Each subarray is sorted recursively. +3. **Merge**: The sorted subarrays are merged into a single sorted array. + +This approach ensures that sorting is both systematic and efficient. + +--- + +## Advantages + +1. **Stability**: Merge Sort preserves the relative order of elements with equal keys. +2. **Predictable Performance**: Its time complexity is consistent, making it suitable for large datasets. +3. **Well-Suited for Linked Lists**: Unlike Quick Sort, Merge Sort is ideal for sorting linked lists as it doesn’t rely on random access. + +--- + +## Limitations + +1. **Space Complexity**: Requires additional memory for temporary arrays during the merging process. +2. **Slower for Small Arrays**: Merge Sort’s overhead can make it less efficient than simpler algorithms like Insertion Sort for small datasets. + +--- + +## Performance + +### **Time Complexity** +- **Best Case**: \(O(n \log n)\) +- **Average Case**: \(O(n \log n)\) +- **Worst Case**: \(O(n \log n)\) + +### **Space Complexity** +- Merge Sort requires \(O(n)\) additional memory for temporary arrays, making it less memory-efficient compared to in-place sorting algorithms like Quick Sort. + +--- + +## How It Works + +### Example Walkthrough + +#### Input Array: `[38, 27, 43, 3, 9, 82, 10]` + +1. **Step 1: Divide** + - Split the array into two halves: `[38, 27, 43]` and `[3, 9, 82, 10]`. + +2. **Step 2: Recursively Divide** + - `[38, 27, 43]` becomes `[38]`, `[27]`, `[43]`. + - `[3, 9, 82, 10]` becomes `[3]`, `[9]`, `[82]`, `[10]`. + +3. **Step 3: Merge** + - Merge `[38]` and `[27]` into `[27, 38]`. + - Merge `[27, 38]` with `[43]` into `[27, 38, 43]`. + - Similarly, merge the second half `[3]`, `[9]`, `[82]`, `[10]` into `[3, 9, 10, 82]`. + - Finally, merge `[27, 38, 43]` and `[3, 9, 10, 82]` into `[3, 9, 10, 27, 38, 43, 82]`. + +#### Output: `[3, 9, 10, 27, 38, 43, 82]` + +--- + +## Comparison with Other Sorting Algorithms + +1. **Quick Sort**: While Quick Sort is faster for in-place sorting, Merge Sort guarantees \(O(n \log n)\) performance, even in the worst case. +2. **Heap Sort**: Merge Sort is more stable and predictable but uses more memory compared to the in-place nature of Heap Sort. +3. **Insertion Sort**: For smaller datasets, Insertion Sort can outperform Merge Sort due to its lower overhead. + +--- + +## Applications + +1. **Sorting Linked Lists**: Merge Sort is well-suited for linked lists, as it doesn’t require random access. +2. **External Sorting**: Ideal for large datasets stored in external memory, where limited RAM necessitates efficient merging techniques. + +--- + +## Optimization Techniques + +1. **Hybrid Sorting**: For smaller subarrays (e.g., size < 10), switch to a simpler algorithm like Insertion Sort to reduce overhead. +2. **In-Place Merge Sort**: While challenging to implement, in-place techniques reduce additional memory requirements. + +--- + +## Real-World Example + +Consider an e-commerce platform that processes millions of transactions daily. To analyze trends, these transactions must be sorted by date or value. Merge Sort is ideal here due to its stability and consistent time complexity, even for large datasets. + +--- + diff --git a/pydatastructs/sorting/QUICK SORT/algorithm.txt b/pydatastructs/sorting/QUICK SORT/algorithm.txt new file mode 100644 index 00000000..82dc84e8 --- /dev/null +++ b/pydatastructs/sorting/QUICK SORT/algorithm.txt @@ -0,0 +1,53 @@ + +Purpose + +Quick Sort is a highly efficient divide-and-conquer sorting algorithm. +It is commonly used because of its performance, simplicity, and ability to work in-place without requiring additional memory. +The algorithm works by partitioning the array around a pivot element and recursively sorting the subarrays. + + + +algorithm:- + +-> Input + An unsorted array arr[] of size n. + Two indices start and end representing the segment of the array to be sorted. + +-> Output + A sorted version of the array arr[] in ascending order. + +QuickSort(arr, start, end): + Base Condition: + If start is greater than or equal to end, return (the array segment is already sorted). + +Partitioning: + Call the Partition() function to partition the array into two segments: + Elements smaller than or equal to the pivot. + Elements greater than the pivot. + The function returns the index of the pivot (pivotIndex), where the pivot is in its correct position. + +Recursive Sorting: + + Recursively sort the left subarray (start to pivotIndex - 1). + Recursively sort the right subarray (pivotIndex + 1 to end). + +Termination: + The recursion terminates when all subarrays are reduced to size 1 or 0, and the entire array is sorted. + +Partition(arr, start, end): + Select the pivot element, typically the last element of the segment: + pivot ← arr[end]. + +Initialize a pointer partitionIndex to start. This will keep track of the position where the next smaller element should be placed. + +Iterate through the array segment from start to end - 1: + + For each element: + If the element is smaller than or equal to the pivot: + Swap the element with the element at partitionIndex. + Increment partitionIndex. + +After the loop, place the pivot in its correct position by swapping it with the element at partitionIndex. + +Return the partitionIndex, which is the final position of the pivot. + diff --git a/pydatastructs/sorting/QUICK SORT/code.py b/pydatastructs/sorting/QUICK SORT/code.py new file mode 100644 index 00000000..8eacd1f4 --- /dev/null +++ b/pydatastructs/sorting/QUICK SORT/code.py @@ -0,0 +1,58 @@ +def quick_sort(arr, start, end): + """ + Quick Sort algorithm: Sorts the array in ascending order. + + Parameters: + arr (list): The list of elements to be sorted. + start (int): The starting index of the array segment. + end (int): The ending index of the array segment. + """ + if start < end: + # Partition the array and get the pivot index + pivot_index = partition(arr, start, end) + + # Recursively sort the left subarray + quick_sort(arr, start, pivot_index - 1) + + # Recursively sort the right subarray + quick_sort(arr, pivot_index + 1, end) + + +def partition(arr, start, end): + """ + Partitions the array around a pivot such that all elements smaller than + or equal to the pivot are on the left, and all elements greater are on the right. + + Parameters: + arr (list): The list of elements to be partitioned. + start (int): The starting index of the array segment. + end (int): The ending index of the array segment (pivot). + + Returns: + int: The index of the pivot element after partitioning. + """ + pivot = arr[end] # Choose the last element as the pivot + partition_index = start # Initialize the partition index + + for i in range(start, end): + if arr[i] <= pivot: + # Swap if the current element is smaller than or equal to the pivot + arr[i], arr[partition_index] = arr[partition_index], arr[i] + partition_index += 1 + + # Place the pivot element at the correct position + arr[partition_index], arr[end] = arr[end], arr[partition_index] + return partition_index + + +# Example Usage +if __name__ == "__main__": + # Input array + array = [8, 3, 1, 7, 0, 10, 2] + print("Original Array:", array) + + # Perform Quick Sort + quick_sort(array, 0, len(array) - 1) + + # Output sorted array + print("Sorted Array:", array) diff --git a/pydatastructs/sorting/QUICK SORT/documentation.md b/pydatastructs/sorting/QUICK SORT/documentation.md new file mode 100644 index 00000000..a511299f --- /dev/null +++ b/pydatastructs/sorting/QUICK SORT/documentation.md @@ -0,0 +1,80 @@ +# Quick Sort Algorithm Documentation + +## Introduction + +Quick Sort is an efficient and widely-used sorting algorithm that follows a divide-and-conquer approach. It sorts data by recursively dividing the array into smaller parts based on comparisons with a chosen element, referred to as the **pivot**. This algorithm is valued for its speed and space efficiency, particularly in scenarios involving large datasets. + +--- + +## Core Concept + +Quick Sort works by partitioning an array into two segments relative to a chosen pivot: +1. Elements smaller than or equal to the pivot are placed on one side. +2. Elements greater than the pivot are placed on the other side. + +This process ensures that the pivot is positioned at its correct sorted location within the array. Recursively applying this logic to the unsorted partitions eventually results in a fully sorted array. + +--- + +## Pivot Selection + +The choice of pivot can significantly influence the performance of Quick Sort. Common strategies for selecting a pivot include: +- **First element**: Easy to implement but may lead to imbalanced partitions in sorted or nearly sorted arrays. +- **Last element**: Simplistic and often used in basic implementations. +- **Median-of-three**: Selects the median of the first, middle, and last elements for better partitioning balance. +- **Random selection**: Helps avoid worst-case scenarios for specific input patterns. + +--- + +## Advantages + +1. **Time Efficiency**: Quick Sort is faster than many other sorting algorithms, such as Bubble Sort or Insertion Sort, for larger datasets. +2. **In-Place Sorting**: Requires minimal additional memory as it operates directly within the original array. +3. **Adaptability**: Performs well on various data structures and can be tailored for different use cases, such as sorting based on custom comparison rules. + +--- + +## Limitations + +1. **Unstable**: Quick Sort does not preserve the relative order of elements with equal keys. +2. **Worst-Case Performance**: Sorting an already sorted array (or similar patterns) without precautions can degrade performance. +3. **Recursion Depth**: Excessive recursion on large datasets may lead to stack overflow or increased overhead. + +--- + +## Performance + +### **Time Complexity** +- **Best Case**: \(O(n \log n)\), achieved when the pivot divides the array into two equal-sized partitions repeatedly. +- **Average Case**: \(O(n \log n)\), observed in randomly distributed datasets. +- **Worst Case**: \(O(n^2)\), occurs in unbalanced partitions (e.g., sorted or reverse-sorted input with a naive pivot selection). + +### **Space Complexity** +- **In-Place Algorithm**: Requires \(O(\log n)\) additional space for the recursion stack, making it memory-efficient compared to merge sort. + +--- + +## Example Use Case + +Imagine sorting a list of numbers representing scores in a competition. Quick Sort is well-suited to organize these scores efficiently, particularly when the dataset is large. For instance, sorting the scores `[45, 12, 33, 98, 77]` will result in `[12, 33, 45, 77, 98]`. + +Quick Sort is not limited to numbers; it can be extended to sort strings, objects, or even user-defined structures based on custom comparison rules, making it highly versatile. + +--- + +## Comparison with Other Sorting Algorithms + +- **Merge Sort**: While both algorithms have \(O(n \log n)\) average complexity, Merge Sort requires additional memory for temporary arrays, whereas Quick Sort operates in-place. +- **Heap Sort**: Quick Sort is generally faster due to better cache performance but may require more recursion overhead in practice. +- **Insertion Sort**: Quick Sort outperforms Insertion Sort on large datasets but is less efficient on small arrays, where Insertion Sort’s simplicity and low overhead shine. + +--- + +## Optimization Techniques + +1. **Improved Pivot Selection**: Using strategies like median-of-three or random pivots prevents worst-case behavior. +2. **Hybrid Sorting**: Switching to a simpler algorithm, such as Insertion Sort, for small subarrays can improve performance. +3. **Tail Recursion Elimination**: Converting recursion into iteration reduces stack depth, making the algorithm more robust. + +--- + From 5e3943d43d6b8801bdc220f8ae2166634d372510 Mon Sep 17 00:00:00 2001 From: Mayank-tech69 Date: Wed, 22 Jan 2025 20:46:28 +0530 Subject: [PATCH 2/2] updated .py filename --- .../sorting/MERGE SORT/merge_sort.py | 55 ++++++++++++++++++ .../sorting/QUICK SORT/quick_sort.py | 58 +++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 pydatastructs/sorting/MERGE SORT/merge_sort.py create mode 100644 pydatastructs/sorting/QUICK SORT/quick_sort.py diff --git a/pydatastructs/sorting/MERGE SORT/merge_sort.py b/pydatastructs/sorting/MERGE SORT/merge_sort.py new file mode 100644 index 00000000..d5d67bc6 --- /dev/null +++ b/pydatastructs/sorting/MERGE SORT/merge_sort.py @@ -0,0 +1,55 @@ +def merge_sort(arr): + """ + Function to perform Merge Sort on a given list. + + Args: + arr (list): Unsorted list to be sorted. + + Returns: + list: Sorted list in ascending order. + """ + if len(arr) > 1: + # Finding the middle of the array to divide it + mid = len(arr) // 2 + + # Dividing the array into two halves + left_half = arr[:mid] + right_half = arr[mid:] + + # Recursively sorting both halves + merge_sort(left_half) + merge_sort(right_half) + + # Initializing pointers for left, right, and the merged array + i = j = k = 0 + + # Merging the two sorted halves into a single sorted array + while i < len(left_half) and j < len(right_half): + if left_half[i] < right_half[j]: + arr[k] = left_half[i] + i += 1 + else: + arr[k] = right_half[j] + j += 1 + k += 1 + + # Checking if any element remains in left_half + while i < len(left_half): + arr[k] = left_half[i] + i += 1 + k += 1 + + # Checking if any element remains in right_half + while j < len(right_half): + arr[k] = right_half[j] + j += 1 + k += 1 + + return arr + +# Example usage: +if __name__ == "__main__": + example_arr = [38, 27, 43, 3, 9, 82, 10] + print("Original Array:", example_arr) + sorted_arr = merge_sort(example_arr) + print("Sorted Array:", sorted_arr) diff --git a/pydatastructs/sorting/QUICK SORT/quick_sort.py b/pydatastructs/sorting/QUICK SORT/quick_sort.py new file mode 100644 index 00000000..8eacd1f4 --- /dev/null +++ b/pydatastructs/sorting/QUICK SORT/quick_sort.py @@ -0,0 +1,58 @@ +def quick_sort(arr, start, end): + """ + Quick Sort algorithm: Sorts the array in ascending order. + + Parameters: + arr (list): The list of elements to be sorted. + start (int): The starting index of the array segment. + end (int): The ending index of the array segment. + """ + if start < end: + # Partition the array and get the pivot index + pivot_index = partition(arr, start, end) + + # Recursively sort the left subarray + quick_sort(arr, start, pivot_index - 1) + + # Recursively sort the right subarray + quick_sort(arr, pivot_index + 1, end) + + +def partition(arr, start, end): + """ + Partitions the array around a pivot such that all elements smaller than + or equal to the pivot are on the left, and all elements greater are on the right. + + Parameters: + arr (list): The list of elements to be partitioned. + start (int): The starting index of the array segment. + end (int): The ending index of the array segment (pivot). + + Returns: + int: The index of the pivot element after partitioning. + """ + pivot = arr[end] # Choose the last element as the pivot + partition_index = start # Initialize the partition index + + for i in range(start, end): + if arr[i] <= pivot: + # Swap if the current element is smaller than or equal to the pivot + arr[i], arr[partition_index] = arr[partition_index], arr[i] + partition_index += 1 + + # Place the pivot element at the correct position + arr[partition_index], arr[end] = arr[end], arr[partition_index] + return partition_index + + +# Example Usage +if __name__ == "__main__": + # Input array + array = [8, 3, 1, 7, 0, 10, 2] + print("Original Array:", array) + + # Perform Quick Sort + quick_sort(array, 0, len(array) - 1) + + # Output sorted array + print("Sorted Array:", array)