Abstract Data Types Queue Dequeue Amortized analysis 1
Abstract Data Types Queue + Dequeue Amortized analysis 1
Can one Implement A Queue with stacks? • You are given the STACK ABSTRACT data structure (1, 2. . as many as you want) • Can you use it to implement a queue 5 4 17 21 Q 5 4 17 21 S 2 S 1 26
Implementation of Queue with stacks S 1 13 5 4 17 21 S 2 size=5 inject(x, Q): push(x, S 2); size ← size + 1 inject(2, Q) 27
Implementation with stacks S 1 13 5 4 17 21 2 S 2 size=5 inject(x, Q): push(x, S 2); size ← size + 1 inject(2, Q) 28
Implementation of a Queue with stacks S 1 13 5 4 17 21 2 S 2 size=6 inject(x, Q): push(x, S 2); size ← size + 1 inject(2, Q) 29
Pop S 1 13 5 4 17 21 2 S 2 size=6 pop(Q): if empty? (Q) error if empty? (S 1) then move(S 2, S 1) pop( S 1); size ← size -1 pop(Q) 30
Pop S 1 5 4 17 21 2 S 2 size=6 pop(Q): if empty? (Q) error if empty? (S 1) then move(S 2, S 1) pop( S 1); size ← size -1 pop(Q) 31
Pop S 1 5 4 17 21 2 S 2 size=5 pop(Q): if empty? (Q) error if empty? (S 1) then move(S 2, S 1) pop( S 1); size ← size -1 pop(Q) 32
Pop S 1 2 5 4 17 21 S 2 size=5 pop(Q): if empty? (Q) error if empty? (S 1) then move(S 2, S 1) pop( S 1); size ← size -1 pop(Q) 33
Pop S 1 21 2 5 4 17 S 2 size=5 pop(Q): if empty? (Q) error if empty? (S 1) then move(S 2, S 1) pop( S 1); size ← size -1 pop(Q) 34
Pop S 1 17 21 2 5 4 S 2 size=5 pop(Q): if empty? (Q) error if empty? (S 1) then move(S 2, S 1) pop( S 1); size ← size -1 pop(Q) 35
Pop S 1 4 17 21 2 5 S 2 size=5 pop(Q): if empty? (Q) error if empty? (S 1) then move(S 2, S 1) pop( S 1); size ← size -1 pop(Q) 36
Pop S 1 5 4 17 21 2 S 2 size=5 pop(Q): if empty? (Q) error if empty? (S 1) then move(S 2, S 1) pop( S 1); size ← size -1 pop(Q) 37
Pop S 1 4 17 21 2 S 2 size=4 pop(Q): if empty? (Q) error if empty? (S 1) then move(S 2, S 1) pop( S 1); size ← size -1 pop(Q) 38
move(S 2, S 1) while not empty? (S 2) do x ← pop(S 2) push(x, S 1) 39
Analysis • O(n) worst case time per operation 40
Amortized Analysis • How long it takes to perform m operations on the worst case ? • O(nm) • Is this tight ? 41
Key Observation • An expensive operation cannot occur too often ! 42
Amortized complexity THM: If we start with an empty queue and perform m operations then it takes O(m) time Proof: • • No element moves from S 2 to S 1 Entrance at S 1, exit at S 2. • Every element: • #ops per element ≤ 3 m operations #elements ≤ m work ≤ 3 m • S 2 7 21 2 5 4 S 1 1. Enters S 1 exactly once 2. Moves from S 1 to S 2 at most once 3. Exits S 2 at most once 43
Potential based Proof (on your own) Consider Think of Φ as accumulation of easy operations covering for future potential “damage” Recall that: Amortized(op) = actual(op) + ΔΦ This is O(1) if a move does not occur Say we move S 2: Then the actual time is |S 2| + O(1) ΔΦ = -|S 2| So the amortized time is O(1) 47
Double ended queue (deque) • • Push(x, D) : Insert x as the first in D Pop(D) : Delete the first element of D Inject(x, D): Insert x as the last in D Eject(D): Delete the last element of D Size(D) Empty? (D) Make-deque() 49
Implementation with doubly linked lists head tail size=2 13 5 x x. next x. prev x. element 50
Empty list head tail size=0 We use two sentinels here to make the code simpler 51
Push head size=1 tail 5 push(x, D): n = new node n. element ←x n. next ← head. next (head. next). prev ← n head. next ← n n. prev← head size ← size + 1 52
4 head size=1 tail 5 push(x, D): n = new node n. element ←x n. next ← head. next (head. next). prev ← n head. next ← n n. prev← head size ← size + 1 push(4, D) 53
4 head size=1 tail 5 push(x, D): n = new node n. element ←x n. next ← head. next (head. next). prev ← n head. next ← n n. prev← head size ← size + 1 push(4, D) 54
4 head size=1 tail 5 push(x, D): n = new node n. element ←x n. next ← head. next (head. next). prev ← n head. next ← n n. prev← head size ← size + 1 push(4, D) 55
4 head size=2 tail 5 push(x, D): n = new node n. element ←x n. next ← head. next (head. next). prev ← n head. next ← n n. prev← head size ← size + 1 push(4, D) 56
Implementation with stacks S 1 13 5 4 17 21 S 2 size=5 push(x, D): push(x, S 1) push(2, D) 59
Implementation with stacks S 1 2 13 5 4 17 21 S 2 size=6 push(x, D): push(x, S 1) push(2, D) 60
Pop S 1 2 13 5 4 17 21 S 2 size=6 pop(D): if empty? (D) error if empty? (S 1) then split(S 2, S 1) pop(D) 61
Pop S 1 13 5 4 17 21 S 2 size=5 pop(D): if empty? (D) error if empty? (S 1) then split(S 2, S 1) pop(D) 62
Pop S 1 5 4 17 21 S 2 size=4 pop(D): if empty? (D) error if empty? (S 1) then split(S 2, S 1) pop(D) 63
Pop S 1 5 4 17 21 S 2 size=4 pop(D): if empty? (D) error if empty? (S 1) then split(S 2, S 1) pop(D) 64
Pop S 1 5 4 17 21 S 2 size=4 pop(D): if empty? (D) error if empty? (S 1) then split(S 2, S 1) pop(D) 65
Pop 5 4 S 1 17 21 S 2 size=4 pop(D): if empty? (D) error if empty? (S 1) then split(S 2, S 1) pop(D) 66
Pop S 1 4 17 21 S 2 size=4 pop(D): if empty? (D) error if empty? (S 1) then split(S 2, S 1) pop(D) 67
Pop S 1 4 17 21 S 2 size=3 pop(D): if empty? (D) error if empty? (S 1) then split(S 2, S 1) pop(D) 68
Split S 1 S 2 5 4 17 21 S 3 69
Split S 1 S 2 5 4 17 S 3 21 70
Split S 1 S 2 5 4 S 3 17 21 71
Split S 1 4 S 2 5 S 3 17 21 72
Split S 1 S 2 5 4 S 3 17 21 73
Split S 1 5 4 S 2 17 S 3 21 74
Split S 1 5 4 S 2 17 21 S 3 75
Split (same thing in reverse) S 2 S 1 5 4 S 3 76
split(S 2, S 1) S 3 ← make-stack() d ← size(S 2) while (i ≤ �d/2�) do x ← pop(S 2) push(x, S 3) i ← i+1 while (i ≤ �d/2�) do x ← pop(S 2) push(x, S 1) i ← i+1 while (i ≤ �d/2�) do x ← pop(S 3) push(x, S 2) i ← i+1 77
Analysis • O(n) worst case time per operation 78
Thm: If we start with an empty deque and perform m operations then it takes O(m) time 79
A better bound Consider Think of Φ as accumulation of easy operations covering for future potential “damage” Recall that: Amortized(op) = actual(op) + ΔΦ This is O(1) if no splitting occurs Say we split S 1: Then the actual time is |S 1| + O(1) ΔΦ = -|S 1| (S 2 empty) So the amortized time is O(1) 82
- Slides: 50