By the end of this module, you will be able to:
The Sorting problem:
Let's start by looking at a classic algorithm: BubbleSort
Sweep for i=0:
Comparison at position j=4: 40 30 10 50 20 // Need to swap 20 and 50
Comparison at position j=3: 40 30 10 20 50
Comparison at position j=2: 40 30 10 20 50 // Need to swap 10 and 30
Comparison at position j=1: 40 10 30 20 50 // Need to swap 10 and 40
Final array for i=0: 10 40 30 20 50 // Smallest element in correct place (i=0)
Sweep for i=1:
Comparison at position j=4: 10 40 30 20 50
Comparison at position j=3: 10 40 30 20 50 // Need to swap 20 and 30
Comparison at position j=2: 10 40 20 30 50 // Need to swap 20 and 40
Final array for i=1: 10 20 40 30 50 // Second smallest in correct place
Sweep for i=2:
Comparison at position j=4: 10 20 40 30 50
Comparison at position j=3: 10 20 40 30 50 // Need to swap 30 and 40
Final array for i=2: 10 20 30 40 50
Sweep for i=3:
Comparison at position j=4: 10 20 30 40 50
Final array for i=3: 10 20 30 40 50
Last element stays in place: 10 20 30 40 50
2 4 1 0 3
Algorithm: bubbleSort (data) Input: data - an array of size n 1. for i=0 to n-2 2. for j=n-1 down to i+1 3. if data[j-1] > data[j] 4. swap (data, j-1, j) 5. endif 6. endfor 7. endfor
The code:
public class BubbleSort {
public void sortInPlace (int[] data)
{
// Key idea: Bubble smallest element, then next smallest etc.
// 1. For positions i=0,...,length-1, find the element that should be at position i
for (int i=0; i < data.length-1; i++){
// 1.1 Scan all elements past i starting from the end:
for (int j=data.length-1; j > i; j--) {
// 1.1.1 If a pair of consecutive elements is out of
// order, swap the elements:
if (data[j-1] > data[j])
swap (data, j-1, j);
}
}
}
public void sortInPlace (Comparable[] data)
{
for (int i=0; i < data.length-1; i++){
for (int j=data.length-1; j > i; j--) {
// Notice the use of the compareTo method:
if (data[j-1].compareTo (data[j]) > 0)
swap (data, j-1, j);
}
}
}
public int[] createSortIndex (int[] data)
{
// 1. Create space for a sort index:
int[] index = new int [data.length];
// 2. Initialize to current data:
for (int i=0; i < data.length; i++)
index[i] = i;
// 3. Swap indices by comparing data:
// ...
return index;
}
void swap (int[] data, int i, int j)
{
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
void swap (Comparable[] data, int i, int j)
{
// ...
}
public static void main (String[] argv)
{
// Testing ...
}
}
Note:
public class BubbleSort2 {
public void sortInPlace (int[] data)
{
// Key idea: Bubble smallest element, then next smallest etc.
// 1. For positions i=0,...,length-1, find the element that belongs at i:
for (int i=0; i < data.length-1; i++){
bubble (data, i);
}
}
void bubble (int[] data, int startingPosition)
{
// Swap down the element that belongs in "startingPosition".
for (int j=data.length-1; j > startingPosition; j--) {
if (data[j-1] > data[j])
swap (data, j-1, j);
}
}
// ...
}
This is more readable, but costs an extra method call in each iteration.
public void sortInPlace (int[] data)
{
for (int i=0; i < data.length-1; i++){
for (int j=data.length-1; j > i; j--) {
if (data[j-1] > data[j]) {
int temp = data[j-1];
data[j-1] = data[j];
data[j] = temp;
}
}
}
}
...
public void sortInPlace (Comparable[] data)
{
for (int i=0; i < data.length-1; i++){
for (int j=data.length-1; j > i; j--) {
// Notice the use of the compareTo method:
if (data[j-1].compareTo (data[j]) > 0)
swap (data, j-1, j);
}
}
}
...
Review recursion
from CS-1112, Module-4
and
CS-1112, Module-9
In-Class Exercise 2.1:
Download QuickSortTest.java
and implement the QuickSort algorithm:
You will also need UniformRandom.java.
Algorithm: quickSort (data)
Input: data - an array of size n
1. quickSortRecursive (data, 0, n-1)
Method: quickSortRecursive (data, L, R)
Input: data - an array of size n with a subrange specified by L and R
1. if L ≥ R
2. return
3. endif
// Partition the array and position partition element correctly.
4. p = partition (data, L, R)
5. quickSortRecursive (data, L, p-1) // Sort left side
6. quickSortRecursive (data, p+1, R) // Sort right side
Let's look at a sample execution on a 10-character array "N G Q S K V S K W I"
N G Q S K V S K W I 0 1 2 3 4 5 6 7 8 9
Here is the trace (P denotes the index where the partition element ends up):
N G Q S K V S K W I Start
K G I K N V S S W Q L=0 R=9 P=4 next intervals to recurse down: [0,3] [5,9]
^ ^
K G I K N V S S W Q L=0 R=3 P=3 next: [0,2] [4,3]
^ ^
I G K K N V S S W Q L=0 R=2 P=2 next: [0,1] [3,2]
^ ^
G I K K N V S S W Q L=0 R=1 P=1 next: [0,0] [2,1]
^ ^
G I K K N V S S W Q L=0 R=0 No partition (because L ≥ R)
^
G I K K N V S S W Q L=2 R=1 No partition
^ ^
G I K K N V S S W Q L=3 R=2 No partition
^ ^
G I K K N V S S W Q L=4 R=3 No partition
^ ^
G I K K N Q S S V W L=5 R=9 P=8 next: [5,7] [9,9]
^ ^
G I K K N Q S S V W L=5 R=7 P=5 next: [5,4] [6,7]
^ ^
G I K K N Q S S V W L=5 R=4 No partition
^ ^
G I K K N Q S S V W L=6 R=7 P=7 next: [6,6] [8,7]
^ ^
G I K K N Q S S V W L=6 R=6 No partition
^
G I K K N Q S S V W L=8 R=7 No partition
^ ^
G I K K N Q S S V W L=9 R=9 No partition
^
In-Class Exercise 2.2:
Next, we will examine some variations of QuickSort:
First, observe what happens with the following test data: "A B C D E F G H I J":
A B C D E F G H I J L=0 R=9 P=0 next: [0,-1] [1,9]
^ ^
A B C D E F G H I J L=0 R=-1 No partition
^
A B C D E F G H I J L=1 R=9 P=1 next: [1,0] [2,9]
^ ^
A B C D E F G H I J L=1 R=0 No partition
^ ^
A B C D E F G H I J L=2 R=9 P=2 next: [2,1] [3,9]
^ ^
A B C D E F G H I J L=2 R=1 No partition
^ ^
A B C D E F G H I J L=3 R=9 P=3 next: [3,2] [4,9]
^ ^
A B C D E F G H I J L=3 R=2 No partition
^ ^
A B C D E F G H I J L=4 R=9 P=4 next: [4,3] [5,9]
^ ^
A B C D E F G H I J L=4 R=3 No partition
^ ^
A B C D E F G H I J L=5 R=9 P=5 next: [5,4] [6,9]
^ ^
A B C D E F G H I J L=5 R=4 No partition
^ ^
A B C D E F G H I J L=6 R=9 P=6 next: [6,5] [7,9]
^ ^
A B C D E F G H I J L=6 R=5 No partition
^ ^
A B C D E F G H I J L=7 R=9 P=7 next: [7,6] [8,9]
^ ^
A B C D E F G H I J L=7 R=6 No partition
^ ^
A B C D E F G H I J L=8 R=9 P=8 next: [8,7] [9,9]
^ ^
A B C D E F G H I J L=8 R=7 No partition
^ ^
A B C D E F G H I J L=9 R=9 No partition
^
Why is this a bad thing?
Choosing a partitioning element:
A random partitioning element:
void quickSortRecursive (int[] data, int left, int right)
{
if (left < right) {
// 1. Swap a random element into the leftmost position:
int k = (int) UniformRandom.uniform ( (int) left+1, (int) right );
swap (data, k, left);
// 2. Partition:
int partitionPosition = leftPartition (data, left, right);
// 3. Sort left side:
quickSortRecursive (data, left, partitionPosition-1);
// 4. Sort right side:
quickSortRecursive (data, partitionPosition+1, right);
}
}
Note:
Using the median approach:
int pickMedian3 (int[] data, int a, int b, int c)
{
// Return the index of the middle value among data[a], data[b] and data[c].
}
void quickSortRecursive (int[] data, int left, int right)
{
if (left < right) {
// 1. Pick the median amongst the left, middle and right elements
// and swap into leftmost position:
int k = pickMedian3 (data, left, (left+right)/2, right);
swap (data, k, left);
// 2. Partition:
int partitionPosition = leftPartition (data, left, right);
// 3. Sort left side:
quickSortRecursive (data, left, partitionPosition-1);
// 4. Sort right side:
quickSortRecursive (data, partitionPosition+1, right);
// Partition element is already in the correct place.
}
}
In-Class Exercise 2.3: Implement the median approach in your QuickSort algorithm.
Next, we will consider different partitioning ideas:
K G I L N V B E B Q LC=1 RC=9 Move RC left
^ ^
K G I L N V B E B Q LC=1 RC=8 Move LC right
^ ^
K G I L N V B E B Q LC=2 RC=8 Move LC right
^ ^
K G I L N V B E B Q LC=3 RC=8 Swap
^ ^
K G I B N V B E L Q LC=4 RC=7 Swap
^ ^
K G I B E V B N L Q LC=5 RC=6 Swap
^ ^
K G I B E B V N L Q LC=6 RC=5 Swap partition element
^ ^
B G I B E K V N L Q LC=6 RC=5 Partition complete
^ ^
Algorithm: leftBidirectionalPartition (data, left, right)
Input: data array, indices left and right.
// The partitioning element is the leftmost element:
1. partitionElement = data[left];
// Start bidirectional scan from next-to-left, and right end.
2. LC = left+1; RC = right;
// Scan until cursors cross.
3. while (true)
// As long as elements to the right are larger, skip over them:
4. while data[RC] > partitionElement
RC = RC - 1;
// As long as elements on the left are smaller, skip over them:
5. while (LC <= right) and (data[LC] < partitionElement)
LC = LC + 1;
// We've found the first pair to swap:
6. if (LC < RC)
7. Swap elements at positions LC and RC;
8. LC = LC + 1; RC = RC - 1;
9. else
// Otherwise, if the cursors cross, we're done.
10. break out of loop;
11. endif
12. endwhile
13. Swap RC-th element with leftmost.
14. return RC;
Output: new position of partition element
In-Class Exercise 2.4: In line 4 above, why don't we need to check whether RC becomes negative?
K G I L N V B E B Q LC=1 RC=1 Swap (in place)
^
K G I L N V B E B Q LC=2 RC=2 Swap (in place)
^
K G I L N V B E B Q LC=2 RC=3
^ ^
K G I L N V B E B Q LC=2 RC=4
^ ^
K G I L N V B E B Q LC=2 RC=5
^ ^
K G I L N V B E B Q LC=2 RC=6 Swap
^ ^
K G I B N V L E B Q LC=3 RC=7 Swap
^ ^
K G I B E V L N B Q LC=4 RC=8 Swap
^ ^
K G I B E B L N V Q LC=5 RC=9
^ ^
K G I B E B L N V Q LC=5 RC=9 Swap partition element
^ ^
B G I B E K L N V Q LC=5 RC=9 Partition complete
^ ^
Algorithm: leftUnidirectionalPartition (data, left, right)
Input: data array, indices left and right.
// The partitioning element is the leftmost element:
1. partitionElement = data[left];
// The trailing cursor for swaps:
2. LC = left;
3. // Loop over the forward cursor for examining data:
4. for RC=left+1 to right
// Check whether an element is out-of-place:
5. if data[RC] < partitionElement
// Switch behind trailing-cursor:
6. LC = LC + 1;
7. Swap elements at LC and RC;
8. endif
9. endfor
10. Swap LC element into leftmost;
11. return LC;
Output: new position of partition element
Analysis of QuickSort:

Let's look at the details of the best-case analysis:
In-Class Exercise 2.5: Why is it true that the number of cuts is \(\log_{10/8} n\) above? And why is \(\log_{10/8} n = O(\log_e n)\)?
Next, here are some performance comparisons with Insertion-Sort (in seconds):
| Data size | Insertion Sort | QuickSort-Randomized | QuickSort-Leftmost |
| 10000 | 0.9802 | 0.0044 | 0.008 |
| 30000 | 9.263 | 0.013 | 0.0276 |
| 50000 | 26.035 | 0.0225 | 0.0276 |
| 70000 | 51.172 | 0.0317 | 0.0495 |
| 90000 | 84.848 | 0.041 | 0.0715 |
| Data size | Insertion Sort | QuickSort-Randomized | QuickSort-Leftmost |
| 1000 | 0 | 0.0020 | 0.009 |
| 1500 | 0.021 | 0.003 | 0.02 |
| 2000 | 0.038 | 0.004 | 0.034 |
| 2500 | 0.059 | 0.003 | 0.074 |
| 3000 | 0.084 | 0.004 | 0.098 |
Notice: the un-randomized quicksort performs poorly on
already-sorted data.
(The smaller sizes are used because Java complains about the
excessive recursion depth).
We will now look at some optimizations to QuickSort:
Three-way partitioning:
B A B C B B B D C A LC=1 RC=9 Move LC right
^ ^
B A B C B B B D C A LC=2 RC=9 Swap
^ ^
B A A C B B B D C B LC=3 RC=8 Move RC left
^ ^
B A A C B B B D C B LC=3 RC=7 Move RC left
^ ^
B A A C B B B D C B LC=3 RC=6 Swap
^ ^
B A A B B B C D C B LC=4 RC=5 Swap
^ ^
B A A B B B C D C B LC=5 RC=4 Swap partition element
^ ^
Note: After the above partition, there will still be "B" to "B" comparisons.
B A B C B B B D C A LC=1 RC=9 Move LC right
^ ^
B A B C B B B D C A LC=2 RC=9 Swap
^ ^
B A A C B B B D C B LC=2 RC=9 Swap to right group
^ ^
B A A C B B B D C B LC=3 RC=8 Move RC left
^ ^
B A A C B B B D C B LC=3 RC=7 Move RC left
^ ^
B A A C B B B D C B LC=3 RC=6 Swap
^ ^
B A A B B B C D C B LC=3 RC=6 Swap to left group
^ ^
B B A A B B C D C B LC=4 RC=5 Swap
^ ^
B B A A B B C D C B LC=4 RC=5 Swap to left group
^ ^
B B B A A B C D C B LC=4 RC=5 Swap to right group
^ ^
B B B A A C C D B B LC=5 RC=4 Swap partition element
^ ^
A B B A B C C D B B LC=5 RC=4 Move left group to center
^ ^
A A B B B C C D B B LC=5 RC=4 Move left group to center
^ ^
A A B B B C C D B B LC=5 RC=4 Move right group to center
^ ^
A A B B B B C D B C LC=5 RC=4 Move right group to center
^ ^
A A B B B B B D C C LC=5 RC=4
^ ^
A A B B B B B D C C Smaller left and right sub-arrays
___ _____
Note:
void quickSortThreeWayRecursive (int[] data, int left, int right)
{
if (left < right) {
// The partition returns both ends of the "partition-element range":
PartitionRange pr = leftThreeWayPartition (data, left, right);
// Sort (smaller) left side:
quickSortThreeWayRecursive (data, left, pr.left-1);
// Sort (smaller) right side:
quickSortThreeWayRecursive (data, pr.right+1, right);
}
}
Removing some swaps:
void quickSortRecursive (int[] data, int left, int right)
{
if (left+1 == right) {
// Handle this case right here.
if (data[left] > data[right]) {
// Swap:
int temp = data[left];
data[left] = data[right];
data[right] = temp;
}
}
else if (left < right) {
// ... usual partition and recursion ...
}
Mixed sorting:
In-Class Exercise 2.6: Assuming you have a complete InsertionSort, write pseudocode to use it in QuickSort, as described above.
Recall from QuickSort:
First, suppose two halves have been sorted. Here's how they're merged:
Sorted halves Additional merge space A A E F J C D G I K _ _ _ _ _ _ _ _ _ _ LC=0 RC=5 Merge from left into additional merge space ^ | ^ | A A E F J C D G I K A _ _ _ _ _ _ _ _ _ LC=1 RC=5 Merge from left | ^ | ^ | A A E F J C D G I K A A _ _ _ _ _ _ _ _ LC=2 RC=5 Merge from right | ^ | ^ | A A E F J C D G I K A A C _ _ _ _ _ _ _ LC=2 RC=6 Merge from right | ^ | ^ | A A E F J C D G I K A A C D _ _ _ _ _ _ LC=2 RC=7 Merge from left | ^ | ^ | A A E F J C D G I K A A C D E _ _ _ _ _ LC=3 RC=7 Merge from left | ^ | ^ | A A E F J C D G I K A A C D E F _ _ _ _ LC=4 RC=7 Merge from right | ^ ^ | A A E F J C D G I K A A C D E F G _ _ _ LC=4 RC=8 Merge from right | ^ ^ | A A E F J C D G I K A A C D E F G I _ _ LC=4 RC=9 Merge from left | ^ ^ A A E F J C D G I K A A C D E F G I J _ LC=5 RC=9 Merge only from right | | ^ ^ A A C D E F G I J K A A C D E F G I J K LC=5 RC=10 After merge | | ^ |
Using "merge" for sorting:
Algorithm: mergeSort (data) Input: data - an array of size n 1. mergeSortRecursive (data, 0, n-1) Algorithm: mergeSortRecursive (data, L, R) Input: data - an array of size n with a subrange specified by L and R 1. if L ≥ R // Bottom-out cases 2. return 3. else if L+1 = R 4. if data[R] < data[L] 5. swap (data, L, R) 6. endif 7. return 8. endif 9. M = (L + R) / 2 // Index of middle element. 10. mergeSortRecursive (data, L, M) // Sort the left half. 11. mergeSortRecursive (data, M+1, R) // Sort the right half. 12. merge (data, L, M, R)
The sorting code:
void mergeSortRecursive (Comparable[] data, int left, int right)
{
// 1. We will allow left cursor to go past right as one way
// to bottom out of recursion.
if (left >= right)
return;
// 2. Handle 2-element case directly:
if (left+1 == right) {
if (data[left].compareTo (data[right]) > 0)
swap (data, left, right);
return;
}
// 3. Find the middle point:
int middle = (left+right) / 2;
// 4. Sort the left side, including the middle:
mergeSortRecursive (data, left, middle);
// 5. Sort the right side:
mergeSortRecursive (data, middle+1, right);
// 6. Merge:
simpleMerge (data, left, middle, right);
}
Merging:
void simpleMerge (Comparable[] data, int left, int middle, int right)
{
// 1. Create the space and initialize the cursors:
Comparable[] mergeSpace = new Comparable [right-left+1];
int leftCursor = left;
int rightCursor = middle+1;
// 2. Fill the merge space by one by one, selecting from the correct partition
for (int i=0; i < mergeSpace.length; i++) {
if (leftCursor > middle) {
// 2.1 If left side is done, merge only from right:
mergeSpace[i] = data[rightCursor];
rightCursor++;
}
else if (rightCursor > right) {
// 2.2 If right side is done, merge only from left:
mergeSpace[i] = data[leftCursor];
leftCursor++;
}
else if (data[leftCursor].compareTo (data[rightCursor]) <= 0) {
// 2.3 Otherwise, if the leftCursor element is less, move it:
mergeSpace[i] = data[leftCursor];
leftCursor++;
}
else {
// 2.4 Move from right:
mergeSpace[i] = data[rightCursor];
rightCursor++;
}
}
// 3. Copy back into original array:
for (int i=0; i < mergeSpace.length; i++)
data[left+i] = mergeSpace[i];
}
Example: consider sorting this array of unit-sized strings: "F A A E J K I C G D" (only merge steps shown below):
Merge:
A F A E J K I C G D _ _ _ LC=0 RC=2 Merge from left
^ | ^
A F A E J K I C G D A _ _ LC=1 RC=2 Merge from right
| ^ ^
A F A E J K I C G D A A _ LC=1 RC=3 Merge only from left
| ^ | ^
A A F E J K I C G D A A F LC=2 RC=3 After merge
| | ^ ^
Merge:
A A F E J K I C G D _ _ _ _ _ LC=0 RC=3 Merge from left
^ | ^ |
A A F E J K I C G D A _ _ _ _ LC=1 RC=3 Merge from left
| ^ | ^ |
A A F E J K I C G D A A _ _ _ LC=2 RC=3 Merge from right
| ^ ^ |
A A F E J K I C G D A A E _ _ LC=2 RC=4 Merge from left
| ^ ^
A A F E J K I C G D A A E F _ LC=3 RC=4 Merge only from right
| | ^ ^
A A E F J K I C G D A A E F J LC=3 RC=5 After merge
| | ^ | ^
Merge:
A A E F J I K C G D _ _ _ LC=5 RC=7 Merge from right
^ | ^
A A E F J I K C G D C _ _ LC=5 RC=8 Merge only from left
^ | | ^
A A E F J I K C G D C I _ LC=6 RC=8 Merge only from left
| ^ | ^
A A E F J C I K G D C I K LC=7 RC=8 After merge
| | ^ ^
Merge:
A A E F J C I K D G _ _ _ _ _ LC=5 RC=8 Merge from left
^ | ^ |
A A E F J C I K D G C _ _ _ _ LC=6 RC=8 Merge from right
| ^ | ^ |
A A E F J C I K D G C D _ _ _ LC=6 RC=9 Merge from right
| ^ | ^
A A E F J C I K D G C D G _ _ LC=6 RC=10 Merge only from left
| ^ | |
A A E F J C I K D G C D G I _ LC=7 RC=10 Merge only from left
| ^ |
A A E F J C D G I K C D G I K LC=8 RC=10 After merge
| | ^ |
Merge:
A A E F J C D G I K _ _ _ _ _ _ _ _ _ _ LC=0 RC=5 Merge from left
^ | ^ |
A A E F J C D G I K A _ _ _ _ _ _ _ _ _ LC=1 RC=5 Merge from left
| ^ | ^ |
A A E F J C D G I K A A _ _ _ _ _ _ _ _ LC=2 RC=5 Merge from right
| ^ | ^ |
A A E F J C D G I K A A C _ _ _ _ _ _ _ LC=2 RC=6 Merge from right
| ^ | ^ |
A A E F J C D G I K A A C D _ _ _ _ _ _ LC=2 RC=7 Merge from left
| ^ | ^ |
A A E F J C D G I K A A C D E _ _ _ _ _ LC=3 RC=7 Merge from left
| ^ | ^ |
A A E F J C D G I K A A C D E F _ _ _ _ LC=4 RC=7 Merge from right
| ^ ^ |
A A E F J C D G I K A A C D E F G _ _ _ LC=4 RC=8 Merge from right
| ^ ^ |
A A E F J C D G I K A A C D E F G I _ _ LC=4 RC=9 Merge from left
| ^ ^
A A E F J C D G I K A A C D E F G I J _ LC=5 RC=9 Merge only from right
| | ^ ^
A A C D E F G I J K A A C D E F G I J K LC=5 RC=10 After merge
| | ^ |
Sometimes, it's easier to understand the execution of a recursive algorithm through a recursion tree:

In-Class Exercise 2.7:
Analysis:
Comparison with QuickSort:
In-Class Exercise 2.8: Why is MergeSort stable? Examine the code in simpleMerge() above and identify the critical line of code that makes it stable.
MergeSort variations:
void merge (int[] A, int leftA, int[] B, int leftB, int rightB, int[] C, int leftC, int rightC)
{
// Merge into A, starting from leftA, the ranges from B and C.
}
void mergeSortSwitchRecursive (int[] A, int[] B, int left, int right)
{
// 1. Bottom-out cases:
// ... (not shown)
// 2. Find the middle point:
int middle = (left+right) / 2;
// 3. Switch A and B in recursion so partitions are merged into B:
mergeSortSwitchRecursive (B, A, left, middle);
mergeSortSwitchRecursive (B, A, middle+1, right);
// 4. Merge the two sorted partitions (in B) into A:
merge (A, left, B, left, middle, B, middle+1, right);
}
public void mergeSortSwitch (int[] data)
{
// 1. Create the additional array:
mergeSpace = new int [data.length];
// 2. Copy over data:
for (int i=0; i < data.length; i++)
mergeSpace[i] = data[i];
// 3. Final merge is into original data:
mergeSortSwitchRecursive (data, mergeSpace, 0, data.length-1);
}
F A A E J K I C G D Start
A F A E J K I C G D MergeSize = 1
A F A E J K I C G D MergeSize = 1
A F A E J K I C G D MergeSize = 1
A F A E J K C I G D MergeSize = 1
A F A E J K C I D G MergeSize = 1
A A E F J K C I D G MergeSize = 2
A A E F C I J K D G MergeSize = 2
A A C E F I J K D G MergeSize = 4
A A C D E F G I J K MergeSize = 8
A A C D E F G I J K End
MergeSort optimizations:
First, some definitions:


Key ideas about binary heaps:
Insertion example:


Extracting min:


Implementing a binary min-heap:

In-Class Exercise 2.9:

Pseudocode:
Key methods: minHeapify, buildMinHeap, extractMin:
Algorithm: minHeapify (data, i)
Input: heap, index into heap
1. left = left (i);
2. right = right (i);
3. smallest = i;
3. if (left < currentHeapSize) and (data[left] < data[i])
4. smallest = left;
5. else if (right < currentHeapSize) and (data[right] < data[smallest])
6. smallest = right;
7. endif
8. if (smallest != i)
9. swap (data, i, smallest);
10. minHeapify (data, smallest);
11. endif
Output: subtree at i in min-heap-order
Algorithm: buildMinHeap (data)
Input: data array
1. currentHeapSize = length of data;
// Find the index from which the lowest level starts:
2. startOfLeaves = findStartOfLeaves (currentHeapSize);
// Start heapifying from the rightmost parent.
3. for i=startOfLeaves-1 downto 0
4. minHeapify (data, i);
5. endfor
Output: a min-order heap constructed from the data.
Algorithm: extractMin (data)
Input: heap
// Min element is in data[0].
1. minValue = data[0];
// Swap last element into root:
2. data[0] = data[currentHeapSize-1];
// Reduce heap size:
3. currentHeapSize--;
// Restore heap-order
4. minHeapify (data, 0);
5. return minValue;
Output: minimum value
Sorting:
Example:
Building heap: heapifying data[6] = I
F A A E J K I C G D P=6 L=13 R=14 Parent is smallest (=> no swap)
^
Building heap: heapifying data[5] = K
F A A E J K I C G D P=5 L=11 R=12 Parent is smallest (=> no swap)
^
Building heap: heapifying data[4] = J
F A A E J K I C G D P=4 L=9 R=10 Left is smallest (=> swap left with root)
^ ^
F A A E D K I C G J P=9 L=19 R=20 Parent is smallest (=> no swap)
^
Building heap: heapifying data[3] = E
F A A E D K I C G J P=3 L=7 R=8 Left is smallest (=> swap left with root)
^ ^ ^
F A A C D K I E G J P=7 L=15 R=16 Parent is smallest (=> no swap)
^
Building heap: heapifying data[2] = A
F A A C D K I E G J P=2 L=5 R=6 Parent is smallest (=> no swap)
^ ^ ^
Building heap: heapifying data[1] = A
F A A C D K I E G J P=1 L=3 R=4 Parent is smallest (=> no swap)
^ ^ ^
Building heap: heapifying data[0] = F
F A A C D K I E G J P=0 L=1 R=2 Left is smallest (=> swap left with root)
^ ^ ^
A F A C D K I E G J P=1 L=3 R=4 Left is smallest (=> swap left with root)
^ ^ ^
A C A F D K I E G J P=3 L=7 R=8 Left is smallest (=> swap left with root)
^ ^ ^
A C A E D K I F G J P=7 L=15 R=16 Parent is smallest (=> no swap)
^
Heap construction complete
Extracting min: heapifying after extract min = A
J C A E D K I F G _ P=0 L=1 R=2 Right is smallest (=> swap right with root)
^ ^ ^
A C J E D K I F G _ P=2 L=5 R=6 Right is smallest (=> swap right with root)
^ ^ ^
A C I E D K J F G _ P=6 L=13 R=14 Parent is smallest (=> no swap)
^
Extracting min: heapifying after extract min = A
G C I E D K J F _ _ P=0 L=1 R=2 Left is smallest (=> swap left with root)
^ ^ ^
C G I E D K J F _ _ P=1 L=3 R=4 Right is smallest (=> swap right with root)
^ ^ ^
C D I E G K J F _ _ P=4 L=9 R=10 Parent is smallest (=> no swap)
^
Extracting min: heapifying after extract min = C
F D I E G K J _ _ _ P=0 L=1 R=2 Left is smallest (=> swap left with root)
^ ^ ^
D F I E G K J _ _ _ P=1 L=3 R=4 Left is smallest (=> swap left with root)
^ ^ ^
D E I F G K J _ _ _ P=3 L=7 R=8 Parent is smallest (=> no swap)
^
Extracting min: heapifying after extract min = D
J E I F G K _ _ _ _ P=0 L=1 R=2 Left is smallest (=> swap left with root)
^ ^ ^
E J I F G K _ _ _ _ P=1 L=3 R=4 Left is smallest (=> swap left with root)
^ ^ ^
E F I J G K _ _ _ _ P=3 L=7 R=8 Parent is smallest (=> no swap)
^
Extracting min: heapifying after extract min = E
K F I J G _ _ _ _ _ P=0 L=1 R=2 Left is smallest (=> swap left with root)
^ ^ ^
F K I J G _ _ _ _ _ P=1 L=3 R=4 Right is smallest (=> swap right with root)
^ ^ ^
F G I J K _ _ _ _ _ P=4 L=9 R=10 Parent is smallest (=> no swap)
^
Extracting min: heapifying after extract min = F
K G I J _ _ _ _ _ _ P=0 L=1 R=2 Left is smallest (=> swap left with root)
^ ^ ^
G K I J _ _ _ _ _ _ P=1 L=3 R=4 Left is smallest (=> swap left with root)
^ ^
G J I K _ _ _ _ _ _ P=3 L=7 R=8 Parent is smallest (=> no swap)
^
Extracting min: heapifying after extract min = G
K J I _ _ _ _ _ _ _ P=0 L=1 R=2 Right is smallest (=> swap right with root)
^ ^ ^
I J K _ _ _ _ _ _ _ P=2 L=5 R=6 Parent is smallest (=> no swap)
^
Extracting min: heapifying after extract min = I
K J _ _ _ _ _ _ _ _ P=0 L=1 R=2 Left is smallest (=> swap left with root)
^ ^
J K _ _ _ _ _ _ _ _ P=1 L=3 R=4 Parent is smallest (=> no swap)
^
Extracting min: heapifying after extract min = J
K _ _ _ _ _ _ _ _ _ P=0 L=1 R=2 Parent is smallest (=> no swap)
^
Extracting min: heapifying after extract min = K
_ _ _ _ _ _ _ _ _ _ P=0 L=1 R=2 Parent is smallest (=> no swap)
Sorted data:
A A C D E F G I J K
In-Class Exercise 2.10: Draw the heap at each step in the above example.
Analysis:
Sorting using a max-heap:

Maxheap example:
Building max heap: heapifying data[6] = I
F A A E J K I C G D P=6 L=13 R=14 Parent is largest (=> no swap)
^
Building max heap: heapifying data[5] = K
F A A E J K I C G D P=5 L=11 R=12 Parent is largest (=> no swap)
^
Building max heap: heapifying data[4] = J
F A A E J K I C G D P=4 L=9 R=10 Parent is largest (=> no swap)
^ ^
Building max heap: heapifying data[3] = E
F A A E J K I C G D P=3 L=7 R=8 Right is largest (=> swap right with root)
^ ^ ^
F A A G J K I C E D P=8 L=17 R=18 Parent is largest (=> no swap)
^
Building max heap: heapifying data[2] = A
F A A G J K I C E D P=2 L=5 R=6 Left is largest (=> swap left with root)
^ ^ ^
F A K G J A I C E D P=5 L=11 R=12 Parent is largest (=> no swap)
^
Building max heap: heapifying data[1] = A
F A K G J A I C E D P=1 L=3 R=4 Right is largest (=> swap right with root)
^ ^ ^
F J K G A A I C E D P=4 L=9 R=10 Left is largest (=> swap left with root)
^ ^
F J K G D A I C E A P=9 L=19 R=20 Parent is largest (=> no swap)
^
Building max heap: heapifying data[0] = F
F J K G D A I C E A P=0 L=1 R=2 Right is largest (=> swap right with root)
^ ^ ^
K J F G D A I C E A P=2 L=5 R=6 Right is largest (=> swap right with root)
^ ^ ^
K J I G D A F C E A P=6 L=13 R=14 Parent is largest (=> no swap)
^
Heap construction complete
Heapifying after swapping max = K
A J I G D A F C E K P=0 L=1 R=2 Left is largest (=> swap left with root)
^ ^ ^ |
J A I G D A F C E K P=1 L=3 R=4 Left is largest (=> swap left with root)
^ ^ ^ |
J G I A D A F C E K P=3 L=7 R=8 Right is largest (=> swap right with root)
^ ^ ^ |
J G I E D A F C A K P=8 L=17 R=18 Parent is largest (=> no swap)
^ |
Heapifying after swapping max = J
A G I E D A F C J K P=0 L=1 R=2 Right is largest (=> swap right with root)
^ ^ ^ |
I G A E D A F C J K P=2 L=5 R=6 Right is largest (=> swap right with root)
^ ^ ^ |
I G F E D A A C J K P=6 L=13 R=14 Parent is largest (=> no swap)
^ |
Heapifying after swapping max = I
C G F E D A A I J K P=0 L=1 R=2 Left is largest (=> swap left with root)
^ ^ ^ |
G C F E D A A I J K P=1 L=3 R=4 Left is largest (=> swap left with root)
^ ^ ^ |
G E F C D A A I J K P=3 L=7 R=8 Parent is largest (=> no swap)
^ ^ ^
Heapifying after swapping max = G
A E F C D A G I J K P=0 L=1 R=2 Right is largest (=> swap right with root)
^ ^ ^ |
F E A C D A G I J K P=2 L=5 R=6 Parent is largest (=> G is deleted)
^ ^ ^
Heapifying after swapping max = F
A E A C D F G I J K P=0 L=1 R=2 Left is largest (=> swap left with root)
^ ^ ^ |
E A A C D F G I J K P=1 L=3 R=4 Right is largest (=> swap right with root)
^ ^ ^ |
E D A C A F G I J K P=4 L=9 R=10 Parent is largest (=> K is deleted)
^ | ^
Heapifying after swapping max = E
A D A C E F G I J K P=0 L=1 R=2 Left is largest (=> swap left with root)
^ ^ ^ |
D A A C E F G I J K P=1 L=3 R=4 Left is largest (=> swap left with root)
^ ^ ^
D C A A E F G I J K P=3 L=7 R=8 Parent is largest (=> no swap)
^ | ^ ^
Heapifying after swapping max = D
A C A D E F G I J K P=0 L=1 R=2 Left is largest (=> swap left with root)
^ ^ ^ |
C A A D E F G I J K P=1 L=3 R=4 Parent is largest (=> no swap)
^ ^ ^
Heapifying after swapping max = C
A A C D E F G I J K P=0 L=1 R=2 Parent is largest (=> C is deleted)
^ ^ ^
Heapifying after swapping max = A
A A C D E F G I J K P=0 L=1 R=2 Parent is largest (=> C is deleted)
^ ^ ^
Array is sorted
In-Class Exercise 2.11:
Apply the max-heap-sort to this data and show the array
contents:

What is a lower bound?
In-Class Exercise 2.12: Why is \(O(\log n!) = O(n\log n)\)?
In some applications, we can exploit knowledge about the data:
BucketSort for small-range integer data:
for i=0 to n
count[data[i]] = count[data[i]] + 1;
endfor
How does this square with the lower bound?
Note: