Download presentation
Presentation is loading. Please wait.
1
1 Abstract Data Types Queue + Dequeue Amortized analysis
2
2 Queue Inject(x,Q) : Insert last element x into Q Pop(Q) : Delete the first element in Q Empty?(Q): Return yes if Q is empty Front(Q): Return the first element in Q Size(Q) Make-queue()
3
3 The Queue Data Abstraction inject
4
4 The Queue Data Abstraction inject pop First in, First out (FIFO).
5
5 Using an array 12142 A 5 A[0] A[1] t pop(Q) A[2] A[N-1]
6
6 Using an array 1425 A A[0] A[1] t pop(Q) A[2] A[N-1]
7
7 Using an array 1425 A A[0] A[1] t A[2] A[N-1] This would be inefficient if we insist that elements span a prefix of the array
8
8 Using an array 12142 A 5 A[0] A[1] r A[2] A[N-1] f A A[0] A[1] r A[2] f Empty queue f=r
9
9 Using an array 12142 A 5 A[0] A[1] r pop(Q) A[2] A[N-1] f
10
10 Using an array 142 A 5 A[0] A[1] A[2] A[N-1] f r pop(Q) inject(5,Q)
11
11 Using an array 142 A 55 A[0] A[1] A[2] A[N-1] f r pop(Q) inject(5,Q)
12
12 Using an array 142 A 555 A[0] A[1] A[2] A[N-1] f r pop(Q) inject(5,Q) pop(Q)
13
13 Using an array 2 A 555 A[0] A[1] A[2] A[N-1] f r pop(Q) inject(5,Q) pop(Q) pop(Q), inject(5,Q), pop(Q), inject(5,Q),……….
14
14 Using an array A 5555 A[0] A[1] r A[2] A[N-1] f pop(Q) inject(5,Q) pop(Q) pop(Q), inject(5,Q), pop(Q), inject(5,Q),……….
15
15 Make the array “circular” 5 5 A 55 A[0] A[1] r A[2] A[N-1] f Pop(Q), inject(5,Q), pop(Q), inject(5,Q),……….
16
16 Operations empty?(Q): return (f = r) top(Q): if empty?(Q) then error else return A[f] 142 A 5 A[0] A[1] A[2] A[N-1] f r
17
17 Operations size(Q): if (r >= f) then return (r-f) else return N-(f-r) 142 A 5 A[0] A[1] A[2] A[N-1] f r
18
18 Operations size(Q): if (r >= f) then return (r-f) else return N-(f-r) 5 5 A 55 A[0] A[1] r A[2] A[N-1] f
19
19 Pop pop(Q) 142 A 5 A[0] A[1] A[2] f r pop(Q): if empty?(Q) then error else e ← A[f] f ← (f + 1) mod N return (e)
20
20 Pop pop(Q): if empty?(Q) then error else e ← A[f] f ← (f + 1) mod N return (e) pop(Q) 142 A 5 A[0] A[1] A[2] f r
21
21 Push inject(x,Q): if size(Q) = N-1 then error else A[r] ← x r ← (r+1) mod N inject(5,Q) 42 A 5 A[0] A[1] A[2] f r
22
22 Push inject(x,Q): if size(Q) = N-1 then error else A[r] ← x r ← (r+1) mod N inject(5,Q) 42 A 55 A[0] A[1] A[2] f r
23
23 Implementation with lists 12 1 5 head size=3 tail inject(4,Q)
24
24 Implementation with lists 12 1 5 head size=3 tail inject(4,Q) 4
25
25 Implementation with lists 12 1 5 head size=3 tail inject(4,Q) 4 Complete the details by yourself
26
26 Can one Implement A Queue with stacks? S2S2 You are given the STACK ABSTRACT data structure (1, 2.. as many as you want) Can you use it to implement a queue S1S1 5 4 17 21 Q
27
27 Implementation of Queue with stacks size=5 13 S1S1 S2S2 inject(x,Q): push(x,S 2 ); size ← size + 1 5 4 17 21 inject(2,Q)
28
28 Implementation with stacks size=5 13 S1S1 S2S2 inject(x,Q): push(x,S 2 ); size ← size + 1 5 4 17 21 2 inject(2,Q)
29
29 Implementation of a Queue with stacks size=6 13 S1S1 S2S2 5 4 17 21 2 inject(2,Q) inject(x,Q): push(x,S 2 ); size ← size + 1
30
30 Pop size=6 S1S1 S2S2 pop(Q): if empty?(Q) error if empty?(S 1 ) then move(S 2, S 1 ) pop( S 1 ); size ← size -1 5 4 17 21 2 pop(Q) 13
31
31 Pop size=6 S1S1 S2S2 pop(Q): if empty?(Q) error if empty?(S 1 ) then move(S 2, S 1 ) pop( S 1 ); size ← size -1 5 4 17 21 2 pop(Q)
32
32 Pop size=5 S1S1 S2S2 pop(Q): if empty?(Q) error if empty?(S 1 ) then move(S 2, S 1 ) pop( S 1 ); size ← size -1 5 4 17 21 2 pop(Q) pop(Q)
33
33 Pop size=5 S1S1 S2S2 pop(Q): if empty?(Q) error if empty?(S 1 ) then move(S 2, S 1 ) pop( S 1 ); size ← size -1 5 4 17 21 pop(Q) pop(Q) 2
34
34 Pop size=5 S1S1 S2S2 pop(Q): if empty?(Q) error if empty?(S 1 ) then move(S 2, S 1 ) pop( S 1 ); size ← size -1 5 4 17 pop(Q) pop(Q) 2 21
35
35 Pop size=5 S1S1 S2S2 pop(Q): if empty?(Q) error if empty?(S 1 ) then move(S 2, S 1 ) pop( S 1 ); size ← size -1 5 4 pop(Q) pop(Q) 2 21 17
36
36 Pop size=5 S1S1 S2S2 pop(Q): if empty?(Q) error if empty?(S 1 ) then move(S 2, S 1 ) pop( S 1 ); size ← size -1 5 pop(Q) pop(Q) 2 21 17 4
37
37 Pop size=5 S1S1 S2S2 pop(Q): if empty?(Q) error if empty?(S 1 ) then move(S 2, S 1 ) pop( S 1 ); size ← size -1 pop(Q) pop(Q) 2 21 17 4 5
38
38 Pop size=4 S1S1 S2S2 pop(Q): if empty?(Q) error if empty?(S 1 ) then move(S 2, S 1 ) pop( S 1 ); size ← size -1 pop(Q) pop(Q) 2 21 17 4
39
39 move(S 2, S 1 ) while not empty?(S 2 ) do x ← pop(S 2 ) push(x,S 1 )
40
40 Analysis O(n) worst case time per operation
41
41 Amortized Analysis How long it takes to perform m operations on the worst case ? O(nm) Is this tight ?
42
42 Key Observation An expensive operation cannot occur too often !
43
43 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 S2 to S1 Entrance at S1, exit at S2. Every element: 1.Enters S1 exactly once 2.Moves from S1 to S2 at most once 3.Exits S2 at most once #ops per element ≤ 3 m operations #elements ≤ m work ≤ 3 m S2S2 S1S1 5 4 2 21 7
44
44 The potential formalism The potential is in fact the bank
45
45 Amortized(op) = actual(op) + Define Define: a potential function
46
46 Amortized(op 1 ) = actual(op 1 ) + 1 - 0 Amortized(op 2 ) = actual(op 2 ) + 2 - 1 Amortized(op n ) = actual(op n ) + n - (n-1) ………… + i Amortized(op i ) = i actual(op i ) + n - 0 i Amortized(op i ) i actual(op i ) if n - 0 0
47
47 Potential based Proof (on your own) Consider 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) Think of Φ as accumulation of easy operations covering for future potential “damage”
48
48 Conclusion THM: If we start with an empty queue and perform m operations then it takes O(m) time
49
49 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()
50
50 Implementation with doubly linked lists 5 head size=2 tail 13 x.next x.element x.prev x
51
51 Empty list head size=0 tail We use two sentinels here to make the code simpler
52
52 Push 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 5 head size=1 tail
53
53 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 5 head size=1 tail push(4,D) 4
54
54 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 5 head size=1 tail push(4,D) 4
55
55 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 5 head size=1 tail push(4,D) 4
56
56 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 5 head size=2 tail push(4,D) 4
57
57 Implementations of the other operations are similar Try by yourself
58
58 Back to deques Alternative implementation using stacks
59
59 Implementation with stacks size=5 13 S1S1 S2S2 push(x,D): push(x,S 1 ) 5 4 17 21 push(2,D)
60
60 Implementation with stacks size=6 2 13 S1S1 S2S2 push(x,D): push(x,S 1 ) 5 4 17 21 push(2,D)
61
61 Pop size=6 S1S1 S2S2 pop(D): if empty?(D) error if empty?(S 1 ) then split(S 2, S 1 ) pop( S 1 ) 2 13 5 4 17 21 pop(D)
62
62 Pop size=5 S1S1 S2S2 pop(D): if empty?(D) error if empty?(S 1 ) then split(S 2, S 1 ) pop( S 1 ) 13 5 4 17 21 pop(D) pop(D)
63
63 Pop size=4 S1S1 S2S2 pop(D): if empty?(D) error if empty?(S 1 ) then split(S 2, S 1 ) pop( S 1 ) 5 4 17 21 pop(D) pop(D)
64
64 Pop 5 4 17 21 size=4 S1S1 S2S2 pop(D): if empty?(D) error if empty?(S 1 ) then split(S 2, S 1 ) pop( S 1 ) pop(D)
65
65 Pop 5 4 17 21 size=4 S1S1 S2S2 pop(D): if empty?(D) error if empty?(S 1 ) then split(S 2, S 1 ) pop( S 1 ) pop(D)
66
66 Pop 17 21 size=4 S1S1 S2S2 pop(D): if empty?(D) error if empty?(S 1 ) then split(S 2, S 1 ) pop( S 1 ) pop(D) 5 4
67
67 Pop 17 21 size=4 S1S1 S2S2 pop(D): if empty?(D) error if empty?(S 1 ) then split(S 2, S 1 ) pop( S 1 ) pop(D) 4
68
68 Pop 17 21 size=3 S1S1 S2S2 pop(D): if empty?(D) error if empty?(S 1 ) then split(S 2, S 1 ) pop( S 1 ) pop(D) 4
69
69 Split 5 4 17 21 S1S1 S2S2 S3S3
70
70 Split 5 4 17 S1S1 S2S2 S3S3 21
71
71 Split 5 4 S1S1 S2S2 S3S3 21 17
72
72 Split 5 S1S1 S2S2 S3S3 21 17 4
73
73 Split S1S1 S2S2 S3S3 21 17 54
74
74 Split S1S1 S2S2 S3S3 21 17 54
75
75 Split S1S1 S2S2 S3S3 2117 54
76
76 Split (same thing in reverse) 5 4S1S1 S2S2 S3S3
77
77 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
78
78 Analysis O(n) worst case time per operation
79
79 Thm: If we start with an empty deque and perform m operations then it takes O(m) time
80
80 Amortized Analysis How long it takes to perform m operations on the worst case ? O(nm) Really ?
81
81 Key Observation An expensive operation cannot occur too often !
82
82 A better bound Consider 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) Recall that: Amortized(op) = actual(op) + ΔΦ Think of Φ as accumulation of easy operations covering for future potential “damage”
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.