16. Amortized Analysis
Exercises
16.1-1
Available in the latest revision of the IM.
16.1-2
Available in the latest revision of the IM.
16.1-3
Available in the latest revision of the IM.
16.2-1
Available in the latest revision of the IM.
16.2-2
Available in the latest revision of the IM.
16.2-3
Available in the latest revision of the IM.
The solution in the IM ignores the cost of reading bits. Namely, the while loop in the INCREMENT operation checks whether A[i] == 1. If we follow the prompt's exact wording and charge $1 for every read and $1 for every write, our accounting must be more precise:
Every bit flipped to 1 needs $2 in credit attached to it, not $1 (to pay for the future act of reading it as a 1, and the subsequent act of modifying it to 0).
The operation itself needs to pay $2 just to handle the final 0 bit (reading the 0 to exit the
whileloop, then modifying it to 1).
Therefore, each INCREMENT should be charged by $6 rather than $4. Of course, this doesn't impact the final conclusion about amortized cost, but emphasizes the need for due diligence when applying the accounting method.
16.3-1
Available in the latest revision of the IM.
16.3-2 🌟
Demonstrates the core rule of designing a potential function: it must be capable of collapsing. In the potential method, a massive drop in potential Φ is the mathematical equivalent of spending credit in the accounting method. This is why the IM defines Φ(Di)=2i−2⌊lgi⌋+1 for i>0 and Φ(D0)=0. Try using Φ(Di)=2i for i>0 and things will fall apart despite Φ(Di)≥0 for all i>0.
Available in the latest revision of the IM.
Reverse-engineering the potential function
Let’s derive the formula Φ(Di)=2i−2⌊lgi⌋+1 for i>0 from the IM.
Calculate the savings rate
The previous expensive operation happened at i=2k−1. The current one is at i=2k. The number of cheap steps between them is 2k−1. During this period, we must accumulate enough potential to pay for the next expensive operation. Therefore, we must increase it by 2 at every single cheap step.
16.3-3 🌟
Illustrates that amortized analysis is rarely used to discover unknown bounds; it is a tool used to justify bounds we already suspect or actively want to design.
The real question is: "What benefits do we get by proving an amortized cost of EXTRACT-MIN to be O(1)?" Because we can't extract an item that hasn't been inserted, the total time spent extracting can never exceed the total time spent inserting. By successfully shifting the cost so that INSERT pays for EXTRACT-MIN, we mathematically formalize the idea that the insertion phase is the absolute bottleneck of the data structure. Therefore, amortized analysis simplifies complex algorithm reasoning by focusing on what matters most.
Available in the latest revision of the IM.
16.3-4
Available in the latest revision of the IM.
16.3-5
Available in the latest revision of the IM.
16.3-6
Available in the latest revision of the IM.
16.4-1
Available in the latest revision of the IM.
16.4-2 🌟
Exemplifies the probabilistic amortized analysis by computing the expected value of the amortized cost:
E[c^i]=E[ci]+Φ(Di)−Φ(Di−1).
Available in the latest revision of the IM.
16.4-3
Available in the latest revision of the IM.
16.4-4
Available in the latest revision of the IM.
Problems
16-1 Binary reflected Gray code
Available in the latest revision of the IM.
16-2 Making binary search dynamic
Available in the latest revision of the IM.
Part (b) in the IM contains a flaw in applying the aggregate method. By strict definition, it computes the total cost of a sequence of n operations and divides by n. The defining characteristic of this method is that it assigns the exact same amortized cost to every single operation in the sequence, regardless of the operation type. If we follow the prompt's constraint ("assuming that the only operations are INSERT and SEARCH") and create a sequence of n/2 inserts followed by n/2 searches, the total time is heavily dominated by the searches: O(nlg2n). Therefore, the aggregate method must conclude that the amortized cost per operation (for the whole sequence) is:
nO(nlg2n)=O(lg2n).
16-3 Amortized weight-balanced trees
a.
The initial call is Rebuild-Subtree(T, x).
b.
Search takes O(h) worst-case time, where h is the height of a BST. After h steps down the tree, the number of nodes remaining in a subtree is at most nαh. Since the smallest possible subtree has 1 node (a leaf), we have:
Therefore, performing a search in an n-node α-balanced binary search tree takes O(lgn) worst-case time.
c.
By definition, Δ(x)≥0 for all x, thus any BST has nonnegative potential (assuming that the constant multiplier c is positive). A 1/2-balanced tree is, in a sense, as balanced as it can be. Therefore, it has Δ(x)≤1 for all x. This entails that a 1/2-balanced tree has potential 0.
d.
To determine how large the constant c must be, we need to ensure that the drop in the potential function Φ during a rebuild is large enough to completely pay for the m units of actual work required to rebuild the m-node subtree.
A rebuild is triggered exactly when a node x (where x.size=m) ceases to be α-balanced. This happens when an insertion (or symmetrically deletion) pushes the size of its heavier child strictly above the threshold αm. Let the heavier child's size be h. We know h>αm. Let the lighter child's size be l. Because the total size is m and the root x itself counts as 1 node, l=m−1−h. Now, let's calculate the size difference Δ(x) for this node at the exact moment the threshold is crossed:
Before the rebuild, the potential at node x alone is c⋅Δ(x). After the rebuild, the entire subtree is 1/2-balanced. As we proved previously, a 1/2-balanced tree has a potential of exactly 0. Furthermore, rebuilding the subtree rooted at x does not change the sizes of any ancestors of x, so their potentials remain untouched. Therefore, the total drop in potential in the system is at least the potential that was stored at x. We need to ensure
Because α>1/2, the denominator (2α−1) is always a strictly positive fraction. If α is close to 1/2 (meaning the tree is kept very strictly balanced), then it forces c to be very large—we have to aggressively save a lot of potential during regular operations to pay for the frequent, inevitable rebuilds!
e.
Inserting a node into or deleting a node from an n-node α-balanced tree costs O(lgn) time in the worst-case. If such an operation triggers a rebalancing action, then by part (d), we know that rebuilding a subtree to make it 1/2-balanced takes O(1) amortized time.
Because the tree is α-balanced before the operation, its height is strictly O(lgn). Therefore, there are at most O(lgn) ancestors on the search path for a new slot or toward the node to be deleted. For any ancestor on this path, the size of exactly one of its subtrees (either left or right) changes by exactly 1. The other subtree remains unchanged. Since the potential function is Φ(T)=c∑Δ(x), each node on the path contributes at most c⋅1 to the new potential. With O(lgn) nodes involved, the total maximum increase in the tree's potential is c⋅O(lgn)=O(lgn).
Therefore, inserting a node into or deleting a node from an n-node α-balanced tree costs O(lgn) amortized time.
16-4 The cost of restructuring red-black trees
Available in the latest revision of the IM.
Last updated