Skip to content

Commit df33eaf

Browse files
authored
Merge pull request #73 from kaitinghh/branch-sortsreadme
Branch Sorts READMEs
2 parents d55074b + bc8573d commit df33eaf

File tree

13 files changed

+168
-316
lines changed

13 files changed

+168
-316
lines changed

src/main/java/algorithms/sorting/mergeSort/iterative/MergeSort.java

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,6 @@
33
/**
44
* Here, we are implementing MergeSort where we sort the array in increasing (or more precisely, non-decreasing)
55
* order iteratively.
6-
* <p>
7-
* Brief Description:
8-
* The iterative implementation of MergeSort takes a bottom-up approach, where the sorting process starts by merging
9-
* intervals of size 1. Intervals of size 1 are trivially in sorted order. The algorithm then proceeds to merge
10-
* adjacent sorted intervals, doubling the interval size with each merge step, until the entire array is fully sorted.
11-
* <p>
12-
* Implementation Invariant:
13-
* At each iteration of the merging process, the main array is divided into sub-arrays of a certain interval
14-
* size (interval). Each of these sub-arrays is sorted within itself. The last sub-array may not be of size
15-
* interval, but it is still sorted since its size is necessarily less than interval.
16-
* <p>
17-
* Complexity Analysis:
18-
* Time:
19-
* - Worst case: O(nlogn)
20-
* - Average case: O(nlogn)
21-
* - Best case: O(nlogn)
22-
* <p>
23-
* Given two sorted arrays of size p and q, we need O(p + q) time to merge the two arrays into one sorted array, since
24-
* we have to iterate through every element in both arrays once.
25-
* <p>
26-
* At the 1st level of the merge process, our merging subroutine involves n sub-arrays of size 1, taking O(n) time.
27-
* At the 2nd level of the merge process, our merging subroutine involves (n/2) sub-arrays of size 2, taking O(n) time.
28-
* At the kth level of the merge process, our merging subroutine involves (n/(2^(k-1))) sub-arrays of size (2^(k-1)),
29-
* taking O(n) time.
30-
* <p>
31-
* Since interval doubles at every iteration of the merge process, there are logn such levels. Every level takes
32-
* O(n) time, hence overall time complexity is n * logn = O(nlogn)
33-
* <p>
34-
* Regardless of how sorted the input array is, MergeSort carries out the partitioning and merging process, so the
35-
* time complexity of MergeSort is O(nlogn) for all cases.
36-
* <p>
37-
* Space:
38-
* - O(n) since we require a temporary array to temporarily store the merged elements in sorted order
396
*/
407

418
public class MergeSort {
Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
1+
# Merge Sort
2+
3+
### Background
4+
The iterative implementation of MergeSort takes a bottom-up approach, where the sorting process starts by merging
5+
intervals of size 1. Intervals of size 1 are trivially in sorted order. The algorithm then proceeds to merge
6+
adjacent sorted intervals, doubling the interval size with each merge step, until the entire array is fully sorted.
7+
18
![MergeSort Iterative](../../../../../../../docs/assets/images/MergeSortIterative.jpg)
29

3-
Image Source: https://www.chelponline.com/iterative-merge-sort-12243
10+
Image Source: https://www.chelponline.com/iterative-merge-sort-12243
11+
12+
### Implementation Invariant
13+
At each iteration of the merging process, the main array is divided into sub-arrays of a certain interval
14+
size (interval). Each of these sub-arrays is sorted within itself. The last sub-array may not be of size
15+
interval, but it is still sorted since its size is necessarily less than interval.
16+
17+
### Complexity Analysis
18+
Time:
19+
- Worst case: O(nlogn)
20+
- Average case: O(nlogn)
21+
- Best case: O(nlogn)
22+
23+
Given two sorted arrays of size p and q, we need O(p + q) time to merge the two arrays into one sorted array, since
24+
we have to iterate through every element in both arrays once.
25+
26+
- At the 1st level of the merge process, our merging subroutine involves n sub-arrays of size 1, taking O(n) time.
27+
- At the 2nd level of the merge process, our merging subroutine involves (n/2) sub-arrays of size 2, taking O(n) time.
28+
- At the kth level of the merge process, our merging subroutine involves (n/(2^(k-1))) sub-arrays of size (2^(k-1)),
29+
taking O(n) time.
30+
31+
Since interval doubles at every iteration of the merge process, there are logn such levels. Every level takes
32+
O(n) time, hence overall time complexity is n * logn = O(nlogn)
33+
34+
Regardless of how sorted the input array is, MergeSort carries out the partitioning and merging process, so the
35+
time complexity of MergeSort is O(nlogn) for all cases.
36+
37+
Space:
38+
- O(n) since we require a temporary array to temporarily store the merged elements in sorted order

src/main/java/algorithms/sorting/mergeSort/legacy/iterative/MergeSort.java

Lines changed: 0 additions & 94 deletions
This file was deleted.

src/main/java/algorithms/sorting/mergeSort/legacy/recursive/MergeSort.java

Lines changed: 0 additions & 77 deletions
This file was deleted.

src/main/java/algorithms/sorting/mergeSort/recursive/MergeSort.java

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,6 @@
33
/**
44
* Here, we are implementing MergeSort where we sort the array in increasing (or more precisely, non-decreasing)
55
* order recursively.
6-
* <p>
7-
* Brief Description:
8-
* MergeSort is a divide-and-conquer sorting algorithm. The recursive implementation takes a top-down approach by
9-
* recursively dividing the array into two halves, sorting each half separately, and then merging the sorted halves
10-
* to produce the final sorted output.
11-
* <p>
12-
* Implementation Invariant (for the merging subroutine):
13-
* The sub-array temp[start, (k-1)] consists of the (𝑘−start) smallest elements of arr[start, mid] and
14-
* arr[mid + 1, end], in sorted order.
15-
* <p>
16-
* Complexity Analysis:
17-
* Time:
18-
* - Worst case: O(nlogn)
19-
* - Average case: O(nlogn)
20-
* - Best case: O(nlogn)
21-
* Merging two sorted sub-arrays of size (n/2) requires O(n) time as we need to iterate through every element in both
22-
* sub-arrays in order to merge the two sorted sub-arrays into one sorted array.
23-
* <p>
24-
* Recursion expression: T(n) = 2T(n/2) + O(n) => O(nlogn)
25-
* <p>
26-
* Regardless of how sorted the input array is, MergeSort carries out the same divide-and-conquer strategy, so the
27-
* time complexity of MergeSort is O(nlogn) for all cases.
28-
* <p>
29-
* Space:
30-
* - O(n) since we require a temporary array to temporarily store the merged elements in sorted order
316
*/
327

338
public class MergeSort {
Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,30 @@
1+
# Merge Sort
2+
3+
### Background
4+
MergeSort is a divide-and-conquer sorting algorithm. The recursive implementation takes a top-down approach by
5+
recursively dividing the array into two halves, sorting each half separately, and then merging the sorted halves
6+
to produce the final sorted output.
7+
18
![MergeSort Recursive](../../../../../../../docs/assets/images/MergeSortRecursive.png)
29

3-
Image Source: https://www.101computing.net/merge-sort-algorithm/
10+
Image Source: https://www.101computing.net/merge-sort-algorithm/
11+
12+
### Implementation Invariant (for the merging subroutine)
13+
The sub-array temp[start, (k-1)] consists of the (𝑘−start) smallest elements of arr[start, mid] and
14+
arr[mid + 1, end], in sorted order.
15+
16+
### Complexity Analysis
17+
Time:
18+
- Worst case: O(nlogn)
19+
- Average case: O(nlogn)
20+
- Best case: O(nlogn)
21+
Merging two sorted sub-arrays of size (n/2) requires O(n) time as we need to iterate through every element in both
22+
sub-arrays in order to merge the two sorted sub-arrays into one sorted array.
23+
24+
Recursion expression: T(n) = 2T(n/2) + O(n) => O(nlogn)
25+
26+
Regardless of how sorted the input array is, MergeSort carries out the same divide-and-conquer strategy, so the
27+
time complexity of MergeSort is O(nlogn) for all cases.
28+
29+
Space:
30+
- O(n) since we require a temporary array to temporarily store the merged elements in sorted order

src/main/java/algorithms/sorting/quickSort/hoares/README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,23 @@ Note that the usual Hoare's QuickSort differs slightly from lecture implementati
55

66
This version of QuickSort assumes the **absence of duplicates** in our array.
77

8+
## Background
9+
810
QuickSort is a sorting algorithm based on the divide-and-conquer strategy. It works by selecting a pivot element from
911
the array and rearranging the elements such that all elements less than the pivot are on its left, and
1012
all elements greater than the pivot are on its right. This effectively partitions the array into two parts. The same
1113
process is then applied recursively to the two partitions until the entire array is sorted.
1214

13-
Implementation Invariant:
15+
## Implementation Invariant:
1416

1517
The pivot is in the correct position, with elements to its left being < it, and elements to its right being > it.
1618

1719
![Lecture Hoare's QuickSort](../../../../../../../docs/assets/images/LectureHoares.jpeg)
1820
Example Credits: Prof Seth/Lecture Slides
1921

2022
## Complexity Analysis
21-
22-
Complexity Analysis: (this analysis is based on fixed index pivot selection)
23+
* This analysis is based on fixed index pivot selection.
24+
* The complexity analysis for Hoare's quicksort is the same as that of Lomuto's quicksort.
2325

2426
Time:
2527

@@ -78,7 +80,7 @@ Implementation Invariant:
7880

7981
All elements in A[start, returnIdx] are <= pivot and all elements in A[returnIdx + 1, end] are >= pivot.
8082

81-
## Hoare's vs Lomuto's QuickSort
83+
### Hoare's vs Lomuto's QuickSort
8284

8385
Hoare's partition scheme is in contrast to Lomuto's partition scheme. Hoare's uses two pointers, while Lomuto's uses
8486
one. Hoare's partition scheme is generally more efficient as it requires less swaps. See more at

src/main/java/algorithms/sorting/quickSort/lomuto/QuickSort.java

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,6 @@
33
/**
44
* Here, we are implementing Lomuto's QuickSort where we sort the array in increasing (or more precisely,
55
* non-decreasing) order.
6-
* <p>
7-
* Basic Description:
8-
* QuickSort is a divide-and-conquer sorting algorithm. The basic idea behind Quicksort is to choose a pivot element,
9-
* places it in its correct position in the sorted array, and then recursively sorts the sub-arrays on either side of
10-
* the pivot. When we introduce randomization in pivot selection, every element has equal probability of being
11-
* selected as the pivot. This means the chance of an extreme element getting chosen as the pivot is decreased, so we
12-
* reduce the probability of encountering the worst-case scenario of imbalanced partitioning.
13-
* <p>
14-
* Implementation Invariant:
15-
* The pivot is in the correct position, with elements to its left being <= it, and elements to its right being > it.
16-
* <p>
17-
* We are implementing Lomuto's partition scheme here. This is opposed to Hoare's partition scheme, see more at
18-
* https://www.geeksforgeeks.org/hoares-vs-lomuto-partition-scheme-quicksort/.
19-
* <p>
20-
* Complexity Analysis:
21-
* Time:
22-
* - Expected worst case (poor choice of pivot): O(n^2)
23-
* - Expected average case: O(nlogn)
24-
* - Expected best case (balanced pivot): O(nlogn)
25-
* <p>
26-
* In the best case of a balanced pivot, the partitioning process divides the array in half, which leads to log n
27-
* levels of recursion. Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we
28-
* need to iterate through every element in the sub-array once.
29-
* Therefore, the recurrence relation is: T(n) = 2T(n/2) + O(n) => O(nlogn).
30-
* <p>
31-
* Even in the average case where the chosen pivot partitions the array by a fraction, there will still be log n levels
32-
* of recursion. (e.g. T(n) = T(n/10) + T(9n/10) + O(n) => O(nlogn))
33-
* <p>
34-
* However, if there are many duplicates in the array, e.g. {1, 1, 1, 1}, the 1st pivot will be placed in the 3rd idx,
35-
* and 2nd pivot in 2nd idx, 3rd pivot in the 1st idx and 4th pivot in the 0th idx. As we observe, the presence of many
36-
* duplicates in the array leads to extremely unbalanced partitioning, leading to a O(n^2) time complexity.
37-
* <p>
38-
* Space:
39-
* - O(1) excluding memory allocated to the call stack, since partitioning is done in-place
406
*/
417

428
public class QuickSort {

0 commit comments

Comments
 (0)