Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added sorting algorithms like quick sort and merge sort #577

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions pydatastructs/sorting/MERGE SORT/algorithm.md
Original file line number Diff line number Diff line change
@@ -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]`

---


55 changes: 55 additions & 0 deletions pydatastructs/sorting/MERGE SORT/code.py
Original file line number Diff line number Diff line change
@@ -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)
97 changes: 97 additions & 0 deletions pydatastructs/sorting/MERGE SORT/documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Merge Sort Algorithm Documentation

## <u>Introduction</u>

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.

---

## <u>Core Concept</u>

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.

---

## <u>Advantages</u>

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.

---

## <u>Limitations</u>

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.

---

## <u>Performance</u>

### **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.

---

## <u>How It Works</u>

### 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]`

---

## <u>Comparison with Other Sorting Algorithms</u>

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.

---

## <u>Applications</u>

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.

---

## <u>Optimization Techniques</u>

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.

---

## <u>Real-World Example</u>

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.

---

55 changes: 55 additions & 0 deletions pydatastructs/sorting/MERGE SORT/merge_sort.py
Original file line number Diff line number Diff line change
@@ -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)
53 changes: 53 additions & 0 deletions pydatastructs/sorting/QUICK SORT/algorithm.txt
Original file line number Diff line number Diff line change
@@ -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.

Loading
Loading