This homework is intended to give you a chance to better understand sorting and balanced search trees.
Navigation
- A. Implementing Sorting Algorithms
- B. Quicksort and Mergesort Mechanics
- C. Sorting Problems
- D. Submission
A. Implementing Sorting Algorithms
The skeleton, MySortingAlgorithms.java
provides a template for implementing various sorting algorithms.
Open SortingAlgorithm.java
to see the interface you will have to satisfy. Note that every child class's sort method takes in an argument k. The sorting algorithm should sort the array from index 0 to k.
Hint: This argument could be useful for some of your sorts.
Implement the algorithms below (the ones with an asterisk next to it
are required for this assignment). Note that an implementation of Quicksort.java
has been provided. Clicking on the links will take you to
an animation of the sort to refresh your memory.
- *Selection sort: Easier
- *Insertion sort: Easier
- *Mergesort: Moderate
- Counting sort: Moderate
- Heapsort: Harder
- Quicksort: Harder
- *LSD radix sort: Harder
- MSD radix sort: Harder
Conceptual videos:
- General sorting overview: here
- Mergesort: here
- Quicksort: here
- General Radix sort: here
- LSD sort: here
You can use MySortingAlgorithmsTest.java
to test your implementations.
Once you're done, here are some questions to ponder (you don't need to record nor submit your responses):
- How many items are sorted in the video for selection sort?
- Why does insertion sort take longer/more compares than selection sort?
- At what time stamp does the first partition complete for Quicksort?
- Could the size of the input used by mergesort in the video be a power of 2?
- What do the colors mean for heapsort?
- How many characters are in the alphabet used for the LSD sort problem?
- How many digits are in the keys used for the LSD sort problem?
- The complete video ends with a rather odd sort that doesn't complete. Without looking at the captions, can you tell what it's doing?
- How does the standard sort from the GCC library, a form of quicksort, differ from the other version of quicksort shown?
Additionally, the RunBenchmarks
class runs timing tests on our
various sorts. Currently, it is configured to perform two timing
tests:
- Compare (y)our implementation against Arrays.sort (which uses
Quicksort
for ints). - Time insertion sort for almost sorted arrays.
Run the code, and you should see results that are in line with things we've learned in class.
If the second test takes too long to complete, you can "Edit Configurations" in IntelliJ and type a number for program arguments to run the test on a smaller input size.
Other interesting tests you might try:
- How do LSD and MSD compare with Quicksort and Mergesort? How large must N be before they become faster?
- Find a case where LSD is faster than MSD.
- For what N is insertion sort faster than Quicksort?
- How do selection sort and insertion sort compare?
Feel free to post interesting tests and/or observations on Piazza. You do not need to submit anything related to this Benchmark test.
B. Quicksort and Mergesort Mechanics
Interestingly enough, quicksorting an array is equivalent to inserting all of its items into a BST. In this problem, we'll see why.
First, we need a specific implementation for partitioning.
Consider the array [5, 3, 2, 1, 7, 8, 4, 6]
, and suppose that
we pick the leftmost item 5 as our pivot. One approach to partitioning
is to perform a "stable" partitioning where all items that are less
than 5 appear in the same order as they did before partitioning, and
likewise for the greater items. For the given array, we'd get [3,
2, 1, 4, 5, 7, 8, 6]
.
One inefficient but simple way to implement this stable partitioning algorithm is to perform the following steps:
- Create three empty Lists for storing integers smaller, equal to, and larger than the pivot, respectively.
- Go through each item of the list, comparing it to the pivot, and adding items to the respective list based on the comparison.
- Concatenting the three Lists into a single concatenated List.
- Copying the concatenated List back into the array.
So for example if we partition [5, 3, 2, 1, 7, 8, 4, 6]
from index 0
to index 7, we'd get:
Smaller items: [3, 2, 1, 4]
Equal items : [5]
Larger items : [7, 8, 6]
The concatenation of these lists is just [3, 2, 1, 4, 5, 7, 8, 6]
.
Along the way, we compared the following pairs of numbers: 5-3, 5-2,
5-1, 5-4, 5-7, 5-8,
and 5-6
.
If we are using partition to sort, we then repeat this process for the left half and right half sides as discussed in class.
In HW7 Written
on gradescope, fill out the list of comparisons used by
Quicksort. You might find running the provided Quicksort.java
to
be useful. You may also want to draw the BST that results when you insert [5, 3, 2, 1, 7, 8, 4, 6]
(in that order) into an initially empty BST.
You can submit the gradescope assignment more than once.
C. Sorting Problems
There are many problems for which sorting provides a fast solution, even though the problem isn't really about sorting. We encourage you to do all of these, but you only need to do any one of them for full credit.
Intervals (Intervals.java)
Define an interval to be a pair of numbers $[x_i, x_i']$ such that $x_i < x_i'$. An interval specifies a range of values in 1D space, where we can call $x_i$ the start point, and $x_i'$ the end point.
Given a list of such intervals, we want to know the total length of the regions covered by one or more of the intervals. This is not simply the sum of their lengths, $\Sigma x_i' - x_i$, since several may cover the same span.
For example, if we have intervals $[19, 30]$, $[6, 12]$, $[4, 5]$, $[8, 15]$, and $[3, 10]$, then the total length covered is 23: the last four intervals together totally cover the interval $[3, 15]$ of length 12, and the first covers a disjoint interval of length 11.
Fill in Intervals.java
so that the coveredLength
method returns the
correct total length in $\Theta(N log N)$ time.
There is a clever trick to this problem that you'll need to figure out to get $\Theta(N log N)$ time. You do not need to use any nested loops for this problem, and in fact if you find yourself using them, you probably haven't found the right approach.
Distribution Count for Large Numbers (SortInts.java)
[Goodrich & Tamassia] Given a sequence of n distinct integers, each one of which is in the range $[0, n^2 - 1]$, develop an $O(n)$ algorithm for sorting them. See the skeleton file SortInts.java. You can't use ordinary distribution sort for this, because that would require initializing and traversing arrays of size $n^2$, which would take too long
Inversion Counting (Inversions.java)
Find an algorithm that runs in $O(n \lg n)$ time for computing the number of inversions in a list of n items. Array elements that are "out of order" can be corrected by swapping two adjacent elements at a time, each of which counts as a single inversion. See the skeleton file Inversions.java. To test that your code is actually $O(n \lg n)$, provide it a very large list (say hundreds of thousands).
Two Sum (Sum.java)
[Goodrich & Tamassia] Given two sequences of integers, $A$
and $B$, find an algorithm that runs in $O(n \lg n)$
time (where n is the total number of integers in A and B) that
determines, for a given parameter $m$, whether there is an
integer $a$ in $A$ and an integer $b$ in
$B$ such that $m = a + b$. See the skeleton file
Sum.java
. To test that your algorithm runs in $O(n \lg n)$
time, provide sequences of hundreds of thousands of integers. Feel
free to use any of the methods in java.util.Arrays.
D. Submission
You will be required to submit:
MySortingAlgorithms.java
with Selection Sort, Insertion Sort, Mergesort, and LSD Radix Sort implemented.HW7 Written
on gradescope.- At least one of
Intervals.java
,SortInts.java
,Inversions.java
, andSum.java
. (We suggest you attempt all of them, however.)
Don't forget to push both your commits and tags for your final submission. As a reminder, you can push your tags by running:
$ git push --tags