- Sorting Algorithms: Big Ideas
- Implementing Sorting Algorithms
- Timing Sorting Algorithms
- Quicksort and Mergesort Mechanics (sortingProblems.txt)
- Sorting as a Tool
- Various Problems
- Balanced Search Trees
This homework is intended to give you a chance to better understand sorting and searching.
A. Sorting Algorithms: Big Ideas
Here are some animations of selected sorting algorithms from the YouTube video I showed in lecture
- Insertion sort
- LSD sort
- MSD sort
- Standard sort from GCC library
- Shell's sort
Questions to ponder:
- 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?
B. Implementing Sorting Algorithms
In the skeleton,
provides a template for implementing the various sorting algorithms from 61B.
Try implementing the sorting algorithms without consulting your notes or the net (i.e., can you convert an understanding of the major idea behind an algorithm into code?). You don't have to do all of them, but at least do the starred ones
- *Selection sort: Fairly Easy
- *Insertion sort: Fairly Easy
- Merge sort: Moderate
- Distribution sort: Moderate
- Heapsort: Hard (if you implement the heap from scratch). Using a max priority queue from the library it's very easy.
- *Quicksort: Hard
- *LSD radix sort: Harder
- MSD radix sort: Hardest
You can use MySortingAlgorithmsTest to test your implementations.
C. Timing Sorting Algorithms
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
- 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.
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.
D. Quicksort and Mergesort Mechanics (sortingProblems.txt)
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
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 :  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-1, 5-4, 5-7, 5-8, and
If we are using partition to sort, we then repeat this process for the left half and right half sides as discussed in class.
sortingProblems.txt, fill out the list of comparisons used by
Quicksort. You might find running Quicksort.java from the skeleton to be useful.
BSTs (1b, 1c, 1d in sortingProblems.txt)
Now draw the BST that results when you insert
[5, 3, 2, 1, 7, 8, 4, 6]
(in that order) into an initially empty BST. Record the comparions you
observe in sortingProblems.txt. Answer questions 1c and 1d in
Mergesort (1e in sortingProblems.txt)
Finally, for 1e, give an example of a comparison performed by mergesort that is not performed by Quicksort or BST. This isn't particularly interesting, but we're just asking to make sure you understand how Mergesort works.
E. Sorting as a Tool
There are a large number of problems for which sorting provides a fast solution, even though the problem isn't really about sorting.
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.
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.
F. Various Problems
These problems are optional, but we encourage you to do them to get in good practice.
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. 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
G. Balanced Search Trees
Put answers to the following problems in the file
- Give a formula for the exact height of a binary search tree (as you implemented in homework 6) as a function of N if we insert N values in increasing order.
- Give a formula for the exact height of a 2-3-4 tree in terms of N if we insert N values in increasing order.
You will be required to submit:
MySortingAlgorithms.javawith Selection Sort, Insertion Sort, Quicksort, and LSD Radix Sort implemented