| Queue operation | Paid to Stack implementor (worst-case cost) |
Price List (paid to us) |
|
|---|---|---|---|
| Queue.size() | 2 | 2 | |
| Queue.empty() | 2 | 2 | |
| Queue.push() | 1 | 4 | |
| transfer loop triggered |
transfer loop NOT triggered |
||
| Queue.pop() | 3 + 3|S1| | 2 | 3 |
| Queue.front() | 3 + 3|S1| | 2 | 3 |
Our claim is that we can keep a bank account which is guaranteed to hold $3 in reserve for each element currently stored in stack S1 of our structure.
The operations size() and isEmpty() do not alter our structure, and to pay for these operations, we charge the customer a price exactly equal to our worst-case expenditures. An examination of the source code shows that the only way in which items can be added onto stack S1 is from within the push() method. We have set a price of $4 for this method, even though our expenditure is always $1. In this way, we are sure to have an extra $3 per item on S1 which we can keep in the bank.
We analyze pop() and front() by case analysis. When stack S2 is non-empty, our expenditure is exactly $2, and this can be funded from the $3 price we charge our customer (the profit can be thrown away here).
In the case when S2 is empty, we invoke transferItems. This includes one initial call to S1.size() and the for loop of the transferItems code, which moves all current items from S1 onto S2. This loop has an expenditure of exactly $(3|S1|), as the loop body involves 3 stack methods per iteration. The $2 savings-per-item-of-S1 can pay for this loop, with the $3 collected directly from the customer to pay for the overhead of the call to size and the calls to S2.empty and S2.pop (resp. S2.top) from within the call for queue::pop (resp. queue::top). Although we spend all of our savings, we notice that all items will have been removed from S1 during this process, so we can still maintain our guarantee of having savings of $3 per item on S1.
template <typename Object>
void splice(iterator position, list<
void reverse() {
Node* walk = sentinel->next;
while (walk != sentinel) {
Node* advance = walk->next;
walk->next = walk->prev;
walk->prev = advance;
walk = advance;
}
walk = sentinel->next;
sentinel->next = sentinel->prev;
sentinel->prev = walk;
}
| vector operation | Our True cost | Price List (paid to us) |
|
|---|---|---|---|
| when array size unchanged |
when array changed from N to 2N or N to N/2 |
||
| insertion at end | 1 | N+1 | 3 |
| removal from end | 1 | N/4 | 2 |
We claim that we will always have enough surplus to pay for the overhead of copying elements from the old array to the new array, when the array is reallocated.
Assume that the current array has capacity N, that it is full, and that it is about to be expanded to 2N. We consider the last time that the array size was changed (either expanded or shrunk).
So at the time of that last expansion, there had been exactly N/2 elements. If the array is now full, then there must have been at least N/2 "cheap" insertions since that time (perhaps more, if some removals were mixed in). Each of those cheap insertions cost us only $1, yet we charged the customer $3. Therefore we had a profit of at least $2 x N/2 = $N since that time. This profit can be used to pay for relocating the current N items.
When the array previously had size 2N, it would not have been shrunk unless it was exactly one-fourth full at the time. So when it was shrunk, there were exactly N/2 elements at that time. As in Case 1A, there must have been at least N/2 "cheap" insertions since then, and so we have profited at least $N since then. This profit can be used to pay for relocating the current N items.
Assume that the current array has capacity N, yet it has only N/4 items. We consider the last time that the array size was changed (either expanded or shrunk).
So at the time of that last expansion, there had been exactly N/2 elements. If there are now only N/4 elements, then there must have been at least N/4 "cheap" removals since that time (perhaps more, if some insertions were mixed in). Each of those cheap removals cost us only $1, yet we charged the customer $2. Therefore we had a profit of at least $1 x N/4 = $N/4 since that time. This profit can be used to pay for relocating the current N/4 items.
When the array previously had size 2N, it would not have been shrunk unless it was only one-fourth full at the time, and thus when it was shrunk, there were only N/2 elements at that time. As in Case 2A, there must have been at least N/4 "cheap" removals since then, and so we have profited at least $N/4 since then. This profit can be used to pay for relocating the current N/4 items.