Lecture 05: Amortized Analysis, Sets, Binary Search
Overview:
- Resizing arrays and amortized analysis.
- Sets: Disordered & order
- Binary Search
Strategies for Programming Success
- segment your time, often 5 * 2hr > 10hr.
- start early
- read assignment thoroughly
- skim readings
- skim provided codes
- work incrementally
- write a method, test and varify, then move on. (write a helper method if needed)
Running time of Add(i, x)
- n = current size of list
- assume size < capacity
What’s the running time of each line in add(i, x)
?
1
2
3
4
5
6
7
8
++size; // O(1)
Object cur = x; // O(1) since it is just assignment
for (int j = i, j < size; ++j) { // O(1) since it is just primary ops
Object next = contents[j]; // O(1) also assignment
contents[j] = cur; // O(1) also assignment
cur = next; // O(1) also assignment
}
a single iteration is constant and doesn’t depend on size. How many iterations: n - i + 1 overall running time is \((n - i + 1) O(1) + O(1) = O(n-i+1)\) This means adding at the end is more efficient than adding in the front.
Stack ADT
- push(x) –> add(size, x)
- peek() –> get(size - 1)
- pop() –> remove(size -1)
Running time of increaseCapapcity()
1
2
3
4
5
6
7
private class increaseCapacity(){
Object[] bigContents = new Object[capacity + 1]; // O(n+1)
for (int i = 0; i < capacity; ++i) {
bigContents[1] = contents[i]; // O(1)
}
}
The for loop: O(n) <– O(1) per iteration + n interations The whole method: \(O(n) = O(n+1) + O(n) + O(1)\)
Running time of buildStack(stk, size)
For loop:
- n interations
stk.push(i)
takes:- O(1) if no resize
- O(n) if resize (invoke
increaseCapacity()
)
Overall running time: \(n * O(n) = O(n^2)\) n = number of interations O(n) = time to resize
Although one operation may be expensive, when averaged across operations, overall cost is less expensive.
Amortized Analysis
Idea: Rather than paying worst-case cost for each op, average cost over all sequences of ops.
- e.g. expensive ops like resize are infrequent, so average add time is small.
Banker’s view: Each op has cost (running time)
- have an account $A$ that we can deposit to / withdraw from
- amortized cost of op is
- $ac(op) = cost(op) + bal(A’) - bal(A)$ bal = get balance of account
Amortized cost is average cost per op over sequence of ops.
Example. push op for stack, with capacity doubling
Suppose last resize was at size $N$, new capacity $2N$.
Total cost of next resize $C_{2N} = O(2N) = O(N)$
If N ≤ size < 2N, on push op:
- pay $O(1)$ for push without resize
- also put $\displaystyle \frac{1}{N} * C_{2N} = \frac{1}{N} * O(N) = O(1)$ in bank –> amortized cost of push op is \(ac(push) = O(1)\)
If size = 2N, on push op:
- pay push out pocket $O(1)$
- pay resize out account - decrease act by $C_{2N}$ –> amortized cost is \(\begin{aligned} ac(push) &= cost(push) + cost(resize) + \Delta A\\ O(1) &= O(1) + C_{2N} - C_{2N} \end{aligned}\)