1 Abstract Data Types Queue + Dequeue Amortized analysis.

50
1 Abstract Data Types Queue + Dequeue Amortized analysis
  • date post

    22-Dec-2015
  • Category

    Documents

  • view

    237
  • download

    3

Transcript of 1 Abstract Data Types Queue + Dequeue Amortized analysis.

1

Abstract Data TypesQueue + DequeueAmortized analysis

26

Can one Implement A Queue with stacks?

S2

•You are given the STACK ABSTRACT data structure (1, 2 .. as many as you want)

•Can you use it to implement a queue

S1

5 4 17 21Q5 4 17 21

27

Implementation of Queue with stacks

size=5

13S1S2

inject(x,Q): push(x,S2); size ← size + 1

5 4 17 21

inject(2,Q)

28

Implementation with stacks

size=5

13S1S2

inject(x,Q): push(x,S2); size ← size + 1

5 4 17 21 2

inject(2,Q)

29

Implementation of a Queue with stacks

size=6

13S1S25 4 17 21 2

inject(2,Q)

inject(x,Q): push(x,S2); size ← size + 1

30

Pop

size=6

S1S2

pop(Q): if empty?(Q) error if empty?(S1) then move(S2, S1) pop( S1); size ← size -1

5 4 17 21 2

pop(Q)

13

31

Pop

size=6

S1S2

pop(Q): if empty?(Q) error if empty?(S1) then move(S2, S1) pop( S1); size ← size -1

5 4 17 21 2

pop(Q)

32

Pop

size=5

S1S2

pop(Q): if empty?(Q) error if empty?(S1) then move(S2, S1) pop( S1); size ← size -1

5 4 17 21 2

pop(Q)

pop(Q)

33

Pop

size=5

S1S2

pop(Q): if empty?(Q) error if empty?(S1) then move(S2, S1) pop( S1); size ← size -1

5 4 17 21

pop(Q)

pop(Q)

2

34

Pop

size=5

S1S2

pop(Q): if empty?(Q) error if empty?(S1) then move(S2, S1) pop( S1); size ← size -1

5 4 17

pop(Q)

pop(Q)

221

35

Pop

size=5

S1S2

pop(Q): if empty?(Q) error if empty?(S1) then move(S2, S1) pop( S1); size ← size -1

5 4

pop(Q)

pop(Q)

22117

36

Pop

size=5

S1S2

pop(Q): if empty?(Q) error if empty?(S1) then move(S2, S1) pop( S1); size ← size -1

5

pop(Q)

pop(Q)

221174

37

Pop

size=5

S1S2

pop(Q): if empty?(Q) error if empty?(S1) then move(S2, S1) pop( S1); size ← size -1

pop(Q)

pop(Q)

2211745

38

Pop

size=4

S1S2

pop(Q): if empty?(Q) error if empty?(S1) then move(S2, S1) pop( S1); size ← size -1

pop(Q)

pop(Q)

221174

39

move(S2, S1)

while not empty?(S2) do x ← pop(S2) push(x,S1)

40

Analysis

• O(n) worst case time per operation

41

Amortized Analysis

• How long it takes to perform m operations on the worst case ?

• O(nm)

• Is this tight ?

42

Key Observation• An expensive operation cannot

occur too often !

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 once2. Moves from S1 to S2 at most once3. Exits S2 at most once

#ops per element ≤ 3 • m operations #elements ≤ m work ≤ 3 m

S2S15 42217

47

Potential based Proof (on your own)

Consider2( ) | |D S

Recall that: Amortized(op) = actual(op) + ΔΦ

This is O(1) if a move does not occur

Say we move S2:Then the actual time is |S2| + O(1)

ΔΦ = -|S2|

So the amortized time is O(1)

Think of Φ as accumulation of easy operations covering for future potential “damage”

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

Implementation with doubly linked lists

5

head

size=2

tail

13

x.next

x.element

x.prev

x

51

Empty list

head

size=0

tail

We use two sentinels here to make the code simpler

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

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

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

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

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

59

Implementation with stacks

size=5

13S1S2

push(x,D): push(x,S1)

5 4 17 21

push(2,D)

60

Implementation with stacks

size=6

2 13

S1S2

push(x,D): push(x,S1)

5 4 17 21

push(2,D)

61

Pop

size=6

S1S2

pop(D): if empty?(D) error if empty?(S1) then split(S2, S1) pop( S1)

2 13

5 4 17 21

pop(D)

62

Pop

size=5

S1S2

pop(D): if empty?(D) error if empty?(S1) then split(S2, S1) pop( S1)

13 5 4 17 21

pop(D)

pop(D)

63

Pop

size=4

S1S2

pop(D): if empty?(D) error if empty?(S1) then split(S2, S1) pop( S1)

5 4 17 21

pop(D)

pop(D)

64

Pop

5 4 17 21

size=4

S1S2

pop(D): if empty?(D) error if empty?(S1) then split(S2, S1) pop( S1)

pop(D)

65

Pop

5 4 17 21

size=4

S1S2

pop(D): if empty?(D) error if empty?(S1) then split(S2, S1) pop( S1)

pop(D)

66

Pop

17 21

size=4

S1S2

pop(D): if empty?(D) error if empty?(S1) then split(S2, S1) pop( S1)

pop(D)

5 4

67

Pop

17 21

size=4

S1S2

pop(D): if empty?(D) error if empty?(S1) then split(S2, S1) pop( S1)

pop(D)

4

68

Pop

17 21

size=3

S1S2

pop(D): if empty?(D) error if empty?(S1) then split(S2, S1) pop( S1)

pop(D)

4

69

Split

5 4 17 21S1S2

S3

70

Split

5 4 17S1S2

S3

21

71

Split

5 4S1S2

S3

2117

72

Split

5 S1S2

S3

2117

4

73

Split

S1S2

S3

2117

5

4

74

Split

S1S2

S3

21

17

5

4

75

Split

S1S2

S3

2117

5

4

76

Split (same thing in reverse)

5 4S1S2

S3

77

split(S2, S1)

S3 ← make-stack() d ← size(S2) while (i ≤ ⌊d/2⌋) do x ← pop(S2) push(x,S3) i ← i+1 while (i ≤ ⌈d/2⌉) do x ← pop(S2) push(x,S1) i ← i+1 while (i ≤ ⌊d/2⌋) do x ← pop(S3) push(x,S2) i ← i+1

78

Analysis

• O(n) worst case time per operation

79

Thm: If we start with an empty deque and perform m operations then it takes O(m) time

82

A better boundConsider

1 2( ) || | | ||D S S

This is O(1) if no splitting occursSay we split S1:Then the actual time is |S1| + O(1)

ΔΦ = -|S1| (S2 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”

Cs
upto here