SEN2212 · Week 8 · Using heaps as priority queues. Midterm.

Midterm review: sorting, trees, BST, AVL, heap

A one-page summary of weeks 1-8: the Big-O table, the tracing pattern for each topic and mixed midterm questions.

lower boundradix sortpreorderinorderpostorderlevel-orderbinary search treeinorder successorAVL treerotationbinary heapbuildHeapheapsortstable sort

Midterm week. This page does not teach a new topic; it collects the first eight weeks in one place. First look at the summary table, then read the “how to solve it in the exam” pattern for each topic, and finally solve the questions with pen and paper. Do not open the answer to a question before working out your own trace.

The question types you will meet in the midterm are almost fixed:

  1. Big-O: best, average and worst time for a structure or algorithm.
  2. Tracing: “insert in this order, draw the tree/array”.
  3. Writing a method: recursive tree methods: height, leaf count, sum, search.

The big table

Structure / algorithmOperationBest / averageWorstNote
Merge sortsortingO(nlog⁡n)O(n \log n)O(nlog⁡n)O(n \log n)stable, O(n)O(n) extra memory
QuicksortsortingO(nlog⁡n)O(n \log n)O(n2)O(n^2)in place, unstable
HeapsortsortingO(nlog⁡n)O(n \log n)O(nlog⁡n)O(n \log n)in place, unstable
Counting sortsortingO(n+k)O(n + k)O(n+k)O(n + k)k = value range
Radix sortsortingO(d(n+k))O(d(n + k))O(d(n+k))O(d(n + k))d digits, stable sub-sort
Tree traversalpre/in/post/levelO(n)O(n)O(n)O(n)each node once
BSTfind/insert/removeO(log⁡n)O(\log n)O(n)O(n)a chain with sorted insertion
AVLfind/insert/removeO(log⁡n)O(\log n)O(log⁡n)O(\log n)height ≤ ~1.44 log n
Binary heapinsertO(1)O(1) avg.O(log⁡n)O(\log n)percolate up
Binary heapdeleteMinO(log⁡n)O(\log n)O(log⁡n)O(\log n)percolate down
Binary heapfindMinO(1)O(1)O(1)O(1)root
Binary heapbuildHeapO(n)O(n)O(n)O(n)n inserts would be O(nlog⁡n)O(n \log n)

Sorting: what to remember

The lower bound. An algorithm that only makes comparisons is a decision tree. Each of the n!n! different orderings must end in a separate leaf. Since a binary tree of height h has at most 2h2^h leaves:

h≥⌈log⁡2n!⌉=Ω(nlog⁡n)h \ge \lceil \log_2 n! \rceil = \Omega(n \log n)

n   n!      minimum comparisons
3   6       3
4   24      5
5   120     7
6   720     10
8   40320   16

Why do counting and radix get past this bound? Because they make no comparisons. They use the value itself as an array index. The price: the values must be small integers (or fixed-length keys).

An LSD radix trace. In each pass, distribute stably (stable) by one digit and collect the buckets in order:

start      512  34 208  71 645  93 160
ones       160  71 512  93  34 645 208
tens       208 512  34 645 160  71  93
hundreds    34  71  93 160 208 512 645

PITFALL · stability in radix. In the tens pass 208 and 512 go to different buckets, but numbers that fall in the same bucket must keep the order of the previous pass. If you break stability, the work of the previous pass is lost.

Trees and traversals

You should be able to solve this tree from memory:

        A
       / \
      B   C
     / \   \
    D   E   F
       /
      G
preorder   A B D E G C F    (root, left, right)
inorder    D B G E A C F    (left, root, right)
postorder  D G E B F C A    (left, right, root)
level      A B C D E F G    (with a queue)
height 3, leaf count 3 (D, G, F)

Building a tree from preorder + inorder. The first element of the preorder is the root. Find the root in the inorder: what is to its left is the left subtree, and what is to its right is the right subtree. The next element in the preorder is the root of the left subtree. Continue recursively.

pre: 5 9 1 3 7 2 8     in: 1 9 7 3 5 2 8

root 5: in -> [1 9 7 3] 5 [2 8]
left root 9: in -> [1] 9 [7 3]
  right of 9, root 3: in -> [7] 3
right root 2: in -> 2 [8]

          5
        /   \
       9     2
      / \     \
     1   3     8
        /
       7
post: 1 7 3 9 8 2 5

With only preorder + postorder the tree is not unique (it is unknown whether the child of a single-child node is on the left or the right). The inorder is required.

BST: insertion and deletion

Insert 45, 20, 60, 10, 30, 50, 70, 25, 35:

           45
         /    \
       20      60
      /  \    /  \
    10    30 50   70
         /  \
        25   35

The three cases of deletion: if it is a leaf, delete it directly. If there is one child, link the child to the parent. If there are two children, replace its value with the inorder successor (the leftmost of the right subtree), then delete the successor from the right subtree (it has at most one child).

Delete 20: the successor is 25.

           45
         /    \
       25      60
      /  \    /  \
    10    30 50   70
            \
             35

Preorder: 45 25 10 30 35 60 50 70. If you then delete 45, the successor is 50: 50 25 10 30 35 60 70.

AVL: choosing the rotation

Going up the insertion path from the bottom, the first node whose BF is +2 or -2 is the rotation point. Choose the case by looking from there, according to which grandchild the insertion was made in:

Where the insertion wentBFRotation
left of the left child (LL)+2single right
right of the right child (RR)-2single left
right of the left child (LR)+2first left at the child, then right
left of the right child (RL)-2first right at the child, then left

Insert 40, 20, 60, 10, 30, then insert 25:

before (25 inserted)       after (LR at 40)
       40                        30
      /  \                     /    \
    20    60                 20      40
   /  \                     /  \       \
  10   30                  10   25      60
      /
    25

The BF of 40 is 2 - 0 = 2, and the insertion went to the right of the left child (20): LR. The median value (30) moves up.

Heap: buildHeap and heapsort

buildHeap sinks every node from the last internal node of the array (n/2) toward the root. Most nodes are on the lower levels and sink only a little; the total work is O(n)O(n).

[9, 4, 7, 1, 8, 2, 6]   n=7, i=3..1
i=3  7 <-> 2           [9, 4, 2, 1, 8, 7, 6]
i=2  4 <-> 1           [9, 1, 2, 4, 8, 7, 6]
i=1  9 -> 1 -> 4       [1, 4, 2, 9, 8, 7, 6]

Heapsort builds a max-heap for ascending order (0-indexed: children 2i+1, 2i+2). Then at each step it places the root (the largest) at the end of the active part of the array and shrinks the heap by one.

[3, 8, 5, 1, 9, 6]
buildHeap   [9, 8, 6, 1, 3, 5]
step 1      [8, 5, 6, 1, 3 | 9]
step 2      [6, 5, 3, 1 | 8, 9]
step 3      [5, 1, 3 | 6, 8, 9]
step 4      [3, 1 | 5, 6, 8, 9]
step 5      [1 | 3, 5, 6, 8, 9]

For priority queues and java.util.PriorityQueue, see the other page of this week.

Common mistakes

Wrong ideaThe truth
In radix, use any sort you like in each passThe sub-sort must be stable
Pre + post determines the tree uniquelyThe inorder is needed
In deletion, the successor = the right childThe leftmost node of the right subtree
In AVL, rotate at the topmost node with a broken BFAt the lowest broken node on the path
A single right rotation is enough for LRA double rotation: first left at the child
buildHeap is O(nlog⁡n)O(n \log n)O(n)O(n); n inserts are O(nlog⁡n)O(n \log n)
A min-heap for ascending heapsortA max-heap for in-place ascending order
The height of a single node is 1In this course 0; an empty tree is -1

Test yourself

Exam-style questions. Solve them on paper first, then reveal the answer.

Question 1medium

What is the minimum number of comparisons that any comparison-based algorithm needs, in the worst case, to sort 6 elements?

Show answer
10

The decision tree must have at least 6!=7206! = 720 leaves. A binary tree of height h has at most 2h2^h leaves: 2h≥7202^h \ge 720. 29=512<720≤1024=2102^9 = 512 < 720 \le 1024 = 2^{10}, so h=⌈log⁡2720⌉=10h = \lceil \log_2 720 \rceil = 10.

Question 2medium

Apply LSD radix sort (base 10) to [512, 34, 208, 71, 645, 93, 160]. Give the array after the first pass and after the second pass.

Show answer
[160, 71, 512, 93, 34, 645, 208] then [208, 512, 34, 645, 160, 71, 93]

Pass 1 by the ones digit (stable): 160(0), 71(1), 512(2), 93(3), 34(4), 645(5), 208(8).
Pass 2 by the tens digit, equal digits keep the previous order: 208(0), 512(1), 34(3), 645(4), 160(6), 71(7), 93(9).
Pass 3 by the hundreds digit gives the sorted array: [34, 71, 93, 160, 208, 512, 645].

Question 3easy

Which of merge sort, quicksort and heapsort is both stable and O(nlog⁡n)O(n \log n) in the worst case?

  1. merge sort
  2. quicksort
  3. heapsort
  4. none of them
Show answer
merge sort

Merge sort is stable if it takes the left element first on ties, and it is O(nlog⁡n)O(n \log n) in every case. The worst case of quicksort is O(n2)O(n^2) and it is not stable. Heapsort is O(nlog⁡n)O(n \log n) but not stable (it makes long-distance swaps).

Question 4easy

For the tree below, give the postorder and level-order traversals.

        A
       / \
      B   C
     / \   \
    D   E   F
       /
      G
Show answer
post: D G E B F C A; level: A B C D E F G

Postorder = left, right, root. Under B: D, then under E (G, E), then B. Then under C: F, C. Finally A.
Level-order: level by level from left to right. A | B C | D E F | G.
(Preorder A B D E G C F, inorder D B G E A C F.)

Question 5hard

A binary tree has preorder 5 9 1 3 7 2 8 and inorder 1 9 7 3 5 2 8. Give its postorder traversal.

Show answer
1 7 3 9 8 2 5

The first element of the preorder is the root: 5. In the inorder, the left of 5 is {1 9 7 3} and the right is {2 8}.
Left subtree: the next in the preorder, 9, is the root. Inorder {1 | 9 | 7 3}: its left is 1, its right is {7 3}. The next one, 3, is the root, and in the inorder {7 3}, 7 is its left child.
Right subtree: the root is 2, inorder {2 8} → 8 is the right child.
Postorder: 1, 7, 3, 9, 8, 2, 5.

Question 6medium

Insert 45, 20, 60, 10, 30, 50, 70, 25, 35 into an empty BST, then delete 20 (use the inorder successor). Give the preorder traversal of the result.

Show answer
45 25 10 30 35 60 50 70

Preorder before the deletion: 45 20 10 30 25 35 60 50 70.
20 has two children. The inorder successor = the smallest of the right subtree = 25. 25 is written in place of 20, then 25 is deleted from the right subtree (a leaf). The left of 30 becomes empty.
The resulting preorder: 45 25 10 30 35 60 50 70.

Question 7medium

Keys 1, 2, 3, 4, 5, 6, 7 are inserted in this order into (a) a plain BST and (b) an AVL tree. What is the height of each tree? (A single node has height 0.)

Show answer
(a) 6, (b) 2

In a plain BST, sorted insertion produces a chain extending to the right: 7 nodes, 6 edges, height 6. The AVL tree performs four RR (single left) rotations and builds the perfect tree: preorder 4 2 1 3 6 5 7, height 2.

Question 8hard

Insert 40, 20, 60, 10, 30, 25 into an empty AVL tree. Which rotation happens, at which node, and what is the preorder traversal afterwards?

Show answer
LR (double) rotation at 40; preorder 30 20 10 25 40 60

When 25 is inserted, the path is 40 → 20 → 30 → 25 (left). Heights: h(20)=2, h(60)=0, the BF of 40 = 2 - 0 = 2.
The insertion was made in the RIGHT subtree of the LEFT child of 40: the LR case.
First a left rotation at 20 (30 moves up), then a right rotation at 40. The new root is 30; its left is 20 (with children 10 and 25), and its right is 40 (with right child 60).

Question 9medium

Run buildHeap (min-heap, array starting at index 1) on [9, 4, 7, 1, 8, 2, 6]. Give the resulting array.

Show answer
[1, 4, 2, 9, 8, 7, 6]

n = 7, sink from i = n/2 = 3 down to 1.
i=3: 7, the children are 2 and 6, the smaller is 2 → [9, 4, 2, 1, 8, 7, 6]
i=2: 4, the children are 1 and 8 → 1 moves up → [9, 1, 2, 4, 8, 7, 6]
i=1: 9, the children are 1 and 2 → 1 moves up; 9 is at index 2, the children are 4 and 8 → 4 moves up → [1, 4, 2, 9, 8, 7, 6]

Question 10hard

Heapsort (max-heap, 0-indexed) is applied to [3, 8, 5, 1, 9, 6]. Give the array after buildHeap and after the first deleteMax step (swap root with last, percolate down).

Show answer
[9, 8, 6, 1, 3, 5] then [8, 5, 6, 1, 3, 9]

buildHeap: i = 2, 1, 0. i=2: 5 < 6 → [3, 8, 6, 1, 9, 5]. i=1: 8 < 9 → [3, 9, 6, 1, 8, 5]. i=0: 3, the larger child 9 → moves up; 3 is at index 1, the larger child 8 → moves up → [9, 8, 6, 1, 3, 5].
Step 1: 9 is swapped with the last element 5 → [5, 8, 6, 1, 3 | 9]. Sink 5 within the first 5 elements: 8 moves up, then the children are 1 and 3, both smaller than 5 → stop: [8, 5, 6, 1, 3, 9].

Question 11medium

Write a recursive method int height(Node t) for a binary tree, where an empty tree has height -1 and a single node has height 0.

Show answer
null → -1, else 1 + max(height(left), height(right))

The empty tree is the base case. For every other node, add 1 to the larger of the heights of the two subtrees.

static int height(Node t) {
    if (t == null) return -1;
    return 1 + Math.max(height(t.left), height(t.right));
}

For the lettered tree above (A...G) the result is 3 (A → B → E → G). Each node is visited once: O(n)O(n).

Question 12easy

Give the worst-case time of search in (a) a BST with n nodes, (b) an AVL tree with n nodes, and (c) findMin in a binary min-heap.

Show answer
(a) O(n), (b) O(log n), (c) O(1)

A plain BST turns into a chain with sorted insertion, so search is O(n)O(n). AVL always keeps the height O(log⁡n)O(\log n), so search is O(log⁡n)O(\log n). In a min-heap the smallest is always at the root: O(1)O(1). But searching for an arbitrary element in a heap is O(n)O(n).

Verification: All traces (radix passes, traversal orders, building a tree from preorder+inorder, BST deletion, AVL rotations, buildHeap, heapsort steps) were coded in Java and run (verify/SEN2212/W08R.java, java W08R.java).

Review card

Comparison sorting lower bound: ceil(log2 n!) comparisons, Ω(n log n). n=5 → 7, n=6 → 10. Merge O(n log n) in every case, stable, O(n) extra memory. Quick average n log n, worst n^2. Heap n log n, not stable. Counting O(n+k), radix O(d(n+k)); radix needs a STABLE sort in each pass, LSD starts from the ones digit. Pre = root left right, In = left root right, Post = left right root, Level = level by level with a queue. Pre + In → the tree is unique: the first of pre is the root, split at it in the in. In a BST inorder = sorted. Delete: leaf remove, one child link it, two children → inorder successor (leftmost of the right). BST height: best log n, n-1 with sorted insertion. AVL: BF = h(left) - h(right) ∈ {-1,0,1}, h(null) = -1. LL → single right, RR → single left, LR/RL → double. Rotation place: going up the insertion path, the FIRST node whose BF is ±2. Heap: parent i/2, children 2i, 2i+1 (1-indexed). insert = sink up, deleteMin = put the last element at the root, sink down. buildHeap: sink from n/2 to 1, total O(n). Heapsort: max-heap, move the root to the end, n log n.