Presentation is loading. Please wait.

Presentation is loading. Please wait.

Abstract Data Types Stack, Queue Amortized analysis

Similar presentations


Presentation on theme: "Abstract Data Types Stack, Queue Amortized analysis"— Presentation transcript:

1 Abstract Data Types Stack, Queue Amortized analysis

2 ADT is an interface It defines the type of the data stored
operations, what each operation does (not how) parameters of each operation

3 ADT Application ממשק Implementation of the Data structure חוזה בין
מתכנת האפליקציה ומיישם מבנה הנתונים ממשק Implementation of the Data structure

4 Example: Stacks Push(x,S) : Insert element x into S
Pop(S) : Delete the last element inserted into S Empty?(S): Return yes if S is empty Top(S): Return the last element inserted into S Size(S) Make-stack()

5 The Stack Data Abstraction
push push push

6 The Stack Data Abstraction
push push Last in, First out. push pop push

7 A stack application Infix Postfix (2+ 3) * * ( (5 * (7 / 3) ) – (2 * 7) ) / * 2 7 *- Evaluate an expression in postfix or Reverse Polish Notation

8 A stack application * 3 2

9 A stack application * 5 5

10 A stack application * 25

11 Pseudo-code S ← make-stack() while ( not eof )
do B ← read the next data; if B is an operand then push(B,S) else X ← pop(S) Y ← pop(S) Z ← Apply the operation B on X and Y push(Z,S) return(top(S))

12 Implementation We will be interested in algorithms to implement the ADT.. And their efficiency..

13 Using an array t 12 1 3 A A[0] A[1] A[2] A[N-1] The stack is represented by the array A and variable t 3 1 12

14 Using an array t 12 1 3 A A[0] A[1] A[2] A[N-1] The stack is represented by the array A and variable t make-stack(): Allocates the array A, which is of some fixed size N, sets t ← -1

15 Operations size(S): return (t+1) empty?(S): return (t < 0)
12 1 3 A A[0] A[1] A[2] A[N-1] size(S): return (t+1) empty?(S): return (t < 0) top(S): if empty?(S) then error else return A[t]

16 Pop pop(S): if empty?(S) then error else e ←A[t] t ← t – 1 return (e)
12 1 3 A A[0] A[1] A[2] A[N-1] pop(S): if empty?(S) then error else e ←A[t] t ← t – 1 return (e) pop(S)

17 Pop pop(S): if empty?(S) then error else e ←A[t] t ← t – 1 return (e)
12 1 3 A A[0] A[1] A[2] A[N-1] pop(S): if empty?(S) then error else e ←A[t] t ← t – 1 return (e) pop(S)

18 Push push(x,S): if size(S) = N then error else t ←t+1 A[t] ← x
12 1 3 A A[0] A[1] A[2] A[N-1] push(x,S): if size(S) = N then error else t ←t+1 A[t] ← x push(5,S)

19 Push push(x,S): if size(S) = N then error else t ←t+1 A[t] ← x
12 1 5 A A[0] A[1] A[2] A[N-1] push(x,S): if size(S) = N then error else t ←t+1 A[t] ← x push(5,S)

20 Implementation with lists
top size=3 12 1 5 x.element x.next x

21 Implementation with lists
top size=3 12 1 5 make-stack(): top ← null size ← 0

22 Operations top size=3 12 1 5 size(S): return (size)
empty?(S): return (top = null) top(S): if empty?(S) then error else return top.element

23 Pop top size=3 12 1 5 pop(S): if empty?(S) then error
else e ←top.element top ← top.next size ← size-1 return (e) pop(S)

24 Pop top size=2 12 1 5 pop(S): if empty?(S) then error
else e ←top.element top ← top.next size ← size-1 return (e) pop(S)

25 Garbage collection top size=2 1 5 pop(S): if empty?(S) then error
else e ←top.element top ← top.next size ← size-1 return (e) pop(S)

26 Push top size=2 1 5 push(x,S): n = new node n.element ←x n.next ← top
top ← n size ← size + 1 push(5,S)

27 Push top size=2 5 1 5 push(x,S): n = new node n.element ←x
n.next ← top top ← n size ← size + 1 push(5,S)

28 Push top size=2 5 1 5 push(x,S): n = new node n.element ←x
n.next ← top top ← n size ← size + 1 push(5,S)

29 Push top size=3 5 1 5 push(x,S): n = new node n.element ←x
n.next ← top top ← n size ← size + 1 push(5,S)

30 Analysis Bound the running time of an operation on the worst-case
As a function of the “size”, n, of the data structure T(n) < 4n+7 Too detailed, we are just interested in the order of growth

31 Big-O - קיים c ו כך ש: דוגמא:

32 Big-O cg(n) f(n) n0

33 The running time of our stack and queue operations
Each operation takes O(1) time

34 Stacks via extendable arrays
We do not want our implementation using arrays to be limited to only N elements When the array is full we will double its size

35 Push push(x,S): if size(S) = N then {allocate a new array of size 2N
12 1 A[0] A[1] A[N-1] push(x,S): if size(S) = N then {allocate a new array of size 2N copy the old array to the new one; N ← 2N } t ←t+1 A[t] ← x

36 Push push(x,S): if size(S) = N then {allocate a new array of size 2N
12 1 3 3 4 5 7 3 2 8 1 A[0] A[1] A[N-1] push(x,S): if size(S) = N then {allocate a new array of size 2N copy the old array to the new one; N ← 2N } t ←t+1 A[t] ← x push(5,S)

37 Push push(x,S): if size(S) = N then {allocate a new array of size 2N
12 1 3 3 4 5 7 3 2 8 1 A[0] A[1] push(x,S): if size(S) = N then {allocate a new array of size 2N copy the old array to the new one; N ← 2N } t ←t+1 A[t] ← x push(5,S)

38 Push push(x,S): if size(S) = N then {allocate a new array of size 2N
12 1 3 3 4 5 7 3 2 8 1 t A 12 1 3 3 4 5 7 3 2 8 1 A[0] A[1] push(x,S): if size(S) = N then {allocate a new array of size 2N copy the old array to the new one; N ← 2N } t ←t+1 A[t] ← x push(5,S)

39 Push push(x,S): if size(S) = N then {allocate a new array of size 2N
12 1 3 3 4 5 7 3 2 8 1 A[0] A[1] A[2N-1] push(x,S): if size(S) = N then {allocate a new array of size 2N copy the old array to the new one; N ← 2N } t ←t+1 A[t] ← x push(5,S)

40 Push push(x,S): if size(S) = N then {allocate a new array of size 2N
12 1 3 3 4 5 7 3 2 8 1 5 A[0] A[1] A[2N-1] push(x,S): if size(S) = N then {allocate a new array of size 2N copy the old array to the new one; N ← 2N } t ←t+1 A[t] ← x push(5,S)

41 Analysis An operation may take O(n) worst case time !
But that cannot happen often..

42 Amortized Analysis How long it takes to do m operations in the worst case ? Well, O(nm) Yes, but can it really take that long ?

43

44 x

45 x x

46 x x x x x

47 x x x x x x

48 x x x x x x x x x x x

49 x x x x x x x x x x x x

50 x x x x x x x x x x x x x

51 x x x x x x x x x x x x x x

52 x x x x x x x x x x x x x x x x x x x x x x x

53 x x x x x x x x x x x x x x x x x x x x x x x x

54 x x x x x x x x x x x x x x x x x x x x x x x x x

55 After N-1 pushes that cost 1 we will have a push that costs 2N+1
x x x x N x x x x x x x x x x x x x x x x x Let N be the size of the array we just copied (half the size of the new array) After N-1 pushes that cost 1 we will have a push that costs 2N+1 Total time 3N for N pushes, 3 per push

56 Theorem: A sequence of m operations on a stack takes O(m) time
We proved: Theorem: A sequence of m operations on a stack takes O(m) time

57 Amortized Analysis (The bank’s view)
You come to do an operation with a bunch of tokens (amortized cost of the operation)

58 Operation

59 You have to pay for each unit of work by a token
Operation

60 If you have more tokens than work you put the remaining tokens in the bank
Operation

61 Operation

62 If we have more work than tokens we pay with token from the bank
Operation

63 If we have more work than tokens we pay with tokens from the bank
Operation

64 Amortized Analysis (The bank’s view)
Suppose we prove that the bank is never in a deficit  The total work is no larger than the total number of tokens 8*m in our example if m is the number of operations

65 “Easy” push Each push comes with 3 tokens
If it’s an easy push then we pay with one token for the single unit of work, and put two in the bank. t A 12 1 3 3 4 5 7 3 2 8 1 A[0] A[1] A[N-1]

66 “Easy” push Imagine that the tokens in the bank are placed on the items of the stack We put one token on the item just inserted and another token on an arbitrary item without a token t A 12 1 3 3 4 5 7 3 2 8 1 5 A[0] A[1] A[N-1]

67 t A 12 1 3 3 4 5 7 3 2 8 1 5 6 A[0] A[1] A[N-1]

68 t A 12 1 3 3 4 5 7 3 2 8 1 5 6 6 7 1 10 4 67 2 5 7 A[0] A[1] A[N-1]

69 “hard” push Tokens from the bank “pay” for copying the array into the larger array t A 12 1 3 3 4 5 7 3 2 8 1 5 6 6 7 1 10 4 67 2 5 7 A[0] A[1] A[N-1]

70 “hard” push Then you pay a token and put two in the bank as an “easy” push t A 12 1 3 3 4 5 7 3 2 8 1 5 6 6 7 1 10 4 67 2 5 7 5 6 7 1 10 4 67 2 12 3 8 t

71 Need to prove The balance is never negative:
When we get to an expensive push there are enough tokens to pay for copying By induction: prove that after the i-th push following a copying there are 2i tokens in the bank

72 How many tokens we spent ?
Each operation spent 3 tokens

73 Summary So the total # of tokens is 3m
THM: A sequence of m operations takes O(m) time !! (we finished the proof) Each operation takes O(1) amortized time

74 Theorem: A sequence of m operations on a stack takes O(m) time
proof (3) . We formalize the bank as a potential function

75 Let  be a potential function
Define Amortized(op) = actual(op) + 

76 + Amortized(op1) = actual(op1) + 1- 0
Amortized(opm) = actual(opm) + m- (m-1) iAmortized(opi) = iactual(opi) + m- 0 iAmortized(opi)  iactual(opi) if m- 0  0 We need to find  that gives a nontrivial statement

77 Example: Our extendable arrays
Define: the potential of the stack

78 Without copying: Amortized(push) = actual(push) +  = 1 + 2(t’+1) – N - (2(t+1) – N) = 1+2(t’-t) = 3 With copying: Amortized(push) = N  = N (2 - N) = 3 Amortized(pop) = 1 +  = 1 + 2(t-1) – N - (2t – N) = -1

79 + 3m ≥ Amortized(op1) = actual(op1) + 1- 0
Amortized(opn) = actual(opn) + n- (n-1) iAmortized(opi) = iactual(opi) + n- 0 3m ≥ iAmortized(opi)  iactual(opi) if n- 0  0

80 Summary So the total # of tokens is 3m
 THM: A sequence of m operations starting from an empty stack takes O(m) time !! Each operations take O(1) amortized time

81 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()

82 The Queue Data Abstraction
inject inject

83 The Queue Data Abstraction
inject inject First in, First out (FIFO). pop inject inject

84 Using an array t 12 1 4 2 5 A A[0] A[1] A[2] A[N-1] pop(Q)

85 Using an array t 1 4 2 5 A A[0] A[1] A[2] A[N-1] pop(Q)

86 Using an array t 1 4 2 5 A A[0] A[1] A[2] A[N-1] This would be inefficient if we insist that elements span a prefix of the array

87 Using an array f r 12 1 4 2 5 A f r A Empty queue f=r A[0] A[1] A[2]

88 Using an array f r 12 1 4 2 5 A A[0] A[1] A[2] A[N-1] pop(Q)

89 Using an array f r 1 4 2 5 A A[0] A[1] A[2] A[N-1] pop(Q) inject(5,Q)

90 Using an array f r 1 4 2 5 5 A pop(Q) inject(5,Q) inject(5,Q) A[0]

91 Using an array f r 1 4 2 5 5 5 A pop(Q) inject(5,Q) inject(5,Q) pop(Q)

92 Using an array f r 2 5 5 5 A pop(Q) inject(5,Q) inject(5,Q) pop(Q)
pop(Q), inject(5,Q), pop(Q), inject(5,Q),……….

93 Using an array f r 5 5 5 5 A pop(Q) inject(5,Q) inject(5,Q) pop(Q)
pop(Q), inject(5,Q), pop(Q), inject(5,Q),……….

94 Make the array “circular”
f 5 5 5 5 A A[0] A[1] A[2] A[N-1] Pop(Q), inject(5,Q), pop(Q), inject(5,Q),……….

95 Operations empty?(Q): return (f = r) top(Q): if empty?(Q) then error
1 4 2 5 A A[0] A[1] A[2] A[N-1] empty?(Q): return (f = r) top(Q): if empty?(Q) then error else return A[f]

96 Operations size(Q): if (r >= f) then return (r-f)
1 4 2 5 A A[0] A[1] A[2] A[N-1] size(Q): if (r >= f) then return (r-f) else return N-(f-r)

97 Operations size(Q): if (r >= f) then return (r-f)
5 5 5 5 A A[0] A[1] A[2] A[N-1] size(Q): if (r >= f) then return (r-f) else return N-(f-r)

98 Pop pop(Q): if empty?(Q) then error else e ←A[f] f ← (f + 1) mod N
4 2 5 A A[0] A[1] A[2] pop(Q): if empty?(Q) then error else e ←A[f] f ← (f + 1) mod N return (e) pop(Q)

99 Pop pop(Q): if empty?(Q) then error else e ←A[f] f ← (f + 1) mod N
4 2 5 A A[0] A[1] A[2] pop(Q): if empty?(Q) then error else e ←A[f] f ← (f + 1) mod N return (e) pop(Q)

100 Inject inject(x,Q): if size(Q) = N-1 then error else A[r] ← x
4 2 5 A A[0] A[1] A[2] inject(x,Q): if size(Q) = N-1 then error else A[r] ← x r ← (r+1) mod N inject(5,Q)

101 Inject inject(x,Q): if size(Q) = N-1 then error else A[r] ← x
4 2 5 5 A A[0] A[1] A[2] inject(x,Q): if size(Q) = N-1 then error else A[r] ← x r ← (r+1) mod N inject(5,Q)

102 Implementation with lists
head size=3 12 1 5 tail inject(4,Q)

103 Implementation with lists
head size=3 12 1 5 4 tail inject(4,Q)

104 Implementation with lists
head size=3 12 1 5 4 tail inject(4,Q) Complete the details by yourself

105 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()

106 Implementation with doubly linked lists
head tail size=2 13 5 x.next x.element x.prev x

107 Empty list size=0 We use two sentinels here to make the code simpler
head tail size=0 We use two sentinels here to make the code simpler

108 Push size=1 5 push(x,D): n = new node n.element ←x n.next ← head.next
tail size=1 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

109 4 size=1 5 push(x,D): n = new node n.element ←x n.next ← head.next
tail size=1 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)

110 4 size=1 5 push(x,D): n = new node n.element ←x n.next ← head.next
tail size=1 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)

111 4 size=1 5 push(x,D): n = new node n.element ←x n.next ← head.next
tail size=1 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)

112 4 size=2 5 push(x,D): n = new node n.element ←x n.next ← head.next
tail size=2 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)

113 Implementations of the other operations are similar
Try by yourself


Download ppt "Abstract Data Types Stack, Queue Amortized analysis"

Similar presentations


Ads by Google