Dynamic Arrays and Stacks CS 244 Brent M
Dynamic Arrays and Stacks CS 244 Brent M. Dingle, Ph. D. Game Design and Development Program Department of Mathematics, Statistics, and Computer Science University of Wisconsin – Stout Content derived from: Data Structures Using C++ (D. S. Malik) and Data Structures and Algorithms in C++ (Goodrich, Tamassia, Mount)
Points of Note • Assignment 6 is posted • Due: November 4
Previously • Stacks in general from the book • Dynamic Array (review)
Next Up for Today • Stacks – Chapter 7 • What are they? • How are the used? • How can we implement them • Static Array • Dynamic Array • Amortized Time • Linked Lists
Marker Slide • Questions? • Next up • • Description Application STATIC Array Based DYNAMIC Array Based • Amortization • Description • Applied to Incremental Increase for Dynamic Array Resizing • Applied to Doubling Increase for Dynamic Array Resizing • Stack Implementation Analysis • Static Array versus Dynamic Array • Linked List Refresher • Stack Implemented as a Linked List
Recall: Stack Intro • We are now to chapter 7 of the book • Unit 2 on D 2 L • You are now all assumed to be proficient at C++ and familiar with the processes needed to analyze algorithms using Big-Oh notation • We may still review from time to time • We will be discussing Stacks today • • As an ADT – what are they? Using Static Arrays to implement the Stack ADT Using Dynamic Arrays to implement the Stack ADT Using Linked Lists to implement the Stack ADT
Stacks
Stacks • Stacks store arbitrary objects (Pez in this case)
Stacks • Stacks store arbitrary objects (Pez in this case) • Operations – push(e): inserts an element to the top of the stack
Stacks • Stacks store arbitrary objects (Pez in this case) • Operations – push(e): inserts an element to the top of the stack
Stacks • Stacks store arbitrary objects (Pez in this case) • Operations – push(e): inserts an element to the top of the stack – pop(): removes and returns the top element of the stack
Stacks • Stacks store arbitrary objects (Pez in this case) • Operations – push(e): inserts an element to the top of the stack – pop(): removes and returns the top element of the stack
Stacks • Stacks store arbitrary objects (Pez in this case) • Operations – push(e): inserts an element to the top of the stack – pop(): removes and returns the top element of the stack
Stacks • Stacks store arbitrary objects (Pez in this case) • Operations – push(e): inserts an element to the top of the stack – pop(): removes and returns the top element of the stack – top(): returns a reference to the top element of the stack, but doesn’t remove it
Stacks • Stacks store arbitrary objects (Pez in this case) • Operations – push(e): inserts an element to the top of the stack – pop(): removes and returns the top element of the stack – top(): returns a reference to the top element of the stack, but doesn’t remove it • Optional operations – size(): returns the number of elements in the stack – empty(): returns a bool indicating if the stack contains any objects
Stack Exceptions • Attempting to execute an operation of ADT may cause an error condition called an exception • Exceptions are said to be “thrown” by an operation that cannot be executed • In the Stack ADT, pop and top cannot be performed if the stack is empty • Attempting to execute pop or top on an empty stack throws an Empty. Stack. Exception
Class Exercise: Stacks • Describe the output and final structure of the stack after the following operations: • Push(8) • Push(3) • Pop() • Push(2) • Push(5) • Pop() • Push(9) • Push(1)
Marker Slide • Questions on: • Stacks • Description • Next up • Application • STATIC Array Based • DYNAMIC Array Based • Amortization • Description • Applied to Incremental Increase for Dynamic Array Resizing • Applied to Doubling Increase for Dynamic Array Resizing • Stack Implementation Analysis • Static Array versus Dynamic Array • Linked List Refresher • Stack Implemented as a Linked List
So far Stacks • A stack is an ordered collection of entries that can be accessed only at one end (the top of the stack) • Common place examples: • pancakes, plates, trays
So far Stacks • A stack is an ordered collection of entries that can be accessed only at one end (the top of the stack) • Common place examples: • pancakes, plates, trays • Items in a stack must be removed in the reverse order that they were added to the stack • This is referred to as a ------ data structure PAUSE FOR CLASS PARTICPATION
So far Stacks • A stack is an ordered collection of entries that can be accessed only at one end (the top of the stack) • Common place examples: • pancakes, plates, trays • Items in a stack must be removed in the reverse order that they were added to the stack • This is referred to as a Last-In/First Out (LIFO) structure
Other Applications of Stacks • Direct Applications • Visited Page history of a web-browser • Undo sequence in a text editor • Saving local variables when one function calls another, and that one calls yet another, and… • Indirect Applications • Auxiliary data structure for algorithms • Component of other data structures Let’s take a closer look at this one Good for Computer Engineers
C++ Run-time Stack • The C++ run-time system keeps track of the chain of active functions with a stack • When a function is called, the run-time system pushes on the stack a frame containing • Local variables and return value • Program counter, keeping track of the statement being executed • When a function returns, its frame is popped from the stack and control is passed to the method on top of the stack main() { int i; i = 5; foo(i); } foo(int j) { int k; k = j+1; bar(k); } bar(int m) { … }
C++ Run-time Stack • The C++ run-time system keeps track of the chain of active functions with a stack • When a function is called, the run-time system pushes on the stack a frame containing • Local variables and return value • Program counter, keeping track of the statement being executed • When a function returns, its frame is popped from the stack and control is passed to the method on top of the stack main() { int i; i = 5; foo(i); } foo(int j) { int k; k = j+1; bar(k); } bar(int m) { … } main PC = 2 i=5
C++ Run-time Stack • The C++ run-time system keeps track of the chain of active functions with a stack • When a function is called, the run-time system pushes on the stack a frame containing • Local variables and return value • Program counter, keeping track of the statement being executed • When a function returns, its frame is popped from the stack and control is passed to the method on top of the stack main() { int i; i = 5; foo(i); } foo(int j) { int k; k = j+1; bar(k); } foo PC = 3 j=5 k=6 bar(int m) { … } main PC = 2 i=5
C++ Run-time Stack • The C++ run-time system keeps track of the chain of active functions with a stack • When a function is called, the run-time system pushes on the stack a frame containing • Local variables and return value • Program counter, keeping track of the statement being executed • When a function returns, its frame is popped from the stack and control is passed back to the function that called it main() { int i; i = 5; foo(i); } foo(int j) { int k; k = j+1; bar(k); } foo PC = 3 j=5 k=6 bar(int m) { … } main PC = 2 i=5
C++ Run-time Stack • The C++ run-time system keeps track of the chain of active functions with a stack • When a function is called, the run-time system pushes on the stack a frame containing • Local variables and return value • Program counter, keeping track of the statement being executed • When a function returns, its frame is popped from the stack and control is passed back to the function that called it main() { int i; i = 5; foo(i); } foo(int j) { int k; k = j+1; bar(k); } bar(int m) { … } main PC = 2 i=5
Marker Slide • Questions on: • Stacks • Description • Application • Next up • STATIC Array Based • DYNAMIC Array Based • Amortization • Description • Applied to Incremental Increase for Dynamic Array Resizing • Applied to Doubling Increase for Dynamic Array Resizing • Stack Implementation Analysis • Static Array versus Dynamic Array • Linked List Refresher • Stack Implemented as a Linked List
(static) Array-based Stack • A simple way of implementing the Stack ADT uses an array • We add elements from left to right • A variable keeps track of the index of the top element Algorithm size() return t + 1 Algorithm empty() return size () == 0 Algorithm pop() if empty() then throw Empty. Stack. Exception else t t 1 return S[t + 1] … S 0 1 2 t
(static) Array-based Stack • A simple way of implementing the Stack ADT uses an array • We add elements from left to right • A variable keeps track of the index of the top element Algorithm size() return t + 1 Algorithm empty() return size () == 0 Algorithm pop() if empty() then throw Empty. Stack. Exception else t t 1 return S[t + 1] … S 0 1 2 t
(static) Array-based Stack • A simple way of implementing the Stack ADT uses an array • We add elements from left to right • A variable keeps track of the index of the top element Algorithm size() return t + 1 Algorithm empty() return size () == 0 Algorithm pop() if empty() then throw Empty. Stack. Exception else t t 1 return S[t + 1] … S 0 1 2 t
(static) Array-based Stack • The array storing the stack elements may become full • A push operation will then throw a Full. Stack. Exception • Limitation of the array-based implementation • Not intrinsic to the Stack ADT Algorithm push(e) if t = S. length 1 then throw Full. Stack. Exception else t t + 1 S[t] e … S 0 1 2 t
Performance and Limitations (array-based implementation of stack ADT) • Performance – Let n be the number of elements in the stack – The space used is O(n) – Each operation (push, pop, top, size, empty) runs in time O(1) • Limitations – The maximum size of the stack must be defined a priori , and cannot be changed – Trying to push a new element onto a full stack causes an implementation-specific exception
General Stack Interface in C++ • Requires the definition of a class • Empty. Stack. Exception • Most similar in STL to std: : vector template <typename Type> class Stack { public: int size(); bool is. Empty(); Type& top() throw(Empty. Stack. Exception); void push(Type e); Type pop() throw(Empty. Stack. Exception); };
Array-based Stack in C++ template <class Type> class Array. Stack { private: int capacity; // stack capacity Type *S; // stack array int top; // top of stack public: Array. Stack(int c) : capacity(c) { S = new Type [capacity]; top = -1; } bool is. Empty() { return top < 0; } Type pop() throw(Empty. Stack. Exception) { if ( is. Empty() ) throw Empty. Stack. Exception("Popping from empty stack"); return S[ top-- ]; } //… (other functions omitted)
Stacks – Fun Application Sidetrack • Word Reversal • LOVE becomes EVOL • Useful for finding palindromes • "Radar" becomes "rada. R" • "Step on no Pets" becomes "ste. P on no pet. S"
Sidetrack Math Check – Application • Stacks are often used for evaluating math formulas • For example checking for matching parentheses • ( ( x + y * ( z + 7 ) ) * (a + b) ) • Processing the line left to right • Each open paren, (, equates to a push • Each closed paren, ), is a pop • If matched the stack is empty at the end
Performance and Limitations (Static Array Implementation of Stack ADT) Back on Track • Performance – Let n be the number of elements in the stack – The space used is O(n) – Each operation (push, pop, top, size, empty) runs in time O(1) • Limitations – The maximum size of the stack must be defined a priori , and cannot be changed – Trying to push a new element onto a full stack causes an implementation-specific exception
End Static – Begin Dynamic • Static arrays can be used to implement stacks • But have limitations (previous slide) • Perhaps Dynamic Arrays will be better
Marker Slide • Questions on: • Stacks • Description • Application • STATIC Array Based • Next up • DYNAMIC Array Based • Amortization • Description • Applied to Incremental Increase for Dynamic Array Resizing • Applied to Doubling Increase for Dynamic Array Resizing • Stack Implementation Analysis • Static Array versus Dynamic Array • Linked List Refresher • Stack Implemented as a Linked List
Dynamic (growable) Array-based Stack • In a push operation, when the array is full, instead of throwing an exception, we can replace the array with a larger one • How large should the new array be? • incremental strategy: increase the size by a constant c • doubling strategy: double the size Algorithm push(e) if t = S. length 1 then A new array of size … for i 0 to t do A[i] S[i] S A t t + 1 S[t] o Did we see these options before? With c = 2
So which will be better? • Incremental Strategy • Increasing the array size by a constant c • Doubling Strategy • Doubling the array size Let c=2 if that is easier to think about See also the Pitcher example • The answer is found using amortized time
Marker Slide • Questions on: • Stacks • • Description Application STATIC Array Based DYNAMIC Array Based • Next up • Amortization • Description • Applied to Incremental Increase for Dynamic Array Resizing • Applied to Doubling Increase for Dynamic Array Resizing • Stack Implementation Analysis • Static Array versus Dynamic Array • Linked List Refresher • Stack Implemented as a Linked List
Amortization (common use) • Amortization (definition) • Any guesses at this?
Amortization (common use) • Amortization (definition) • The process of decreasing an amount over time • This shows up in several places in “real life” • Such as • Home Loans • Business Payments
Amortization (common use) • Amortization (definition) • The process of decreasing an amount over time • Home Loans • Amortization is the process by which loan principle decreases over the life of a loan • A portion of the payment is applied towards principle and a portion is applied toward interest • The “cost” is stretched out over time • Each payment is paying a small amount of what would be a large payment if paid all at once
Amortization (common use) • Amortization (definition) • The process of decreasing an amount over time • Home Loans • Amortization is the process by which loan principle decreases over the life of a loan • A portion of the payment is applied towards principle and a portion is applied toward interest • The “cost” is stretched out over time • Each payment is paying a small amount of what would be a large payment if paid all at once • Business • Amortization allocates a lump sum (payment) amount to different time periods
Amortization (CS concept) • Back in Computer Science world • Certain operations may be extremely costly • BUT • They cannot occur frequently enough to slow down the entire program • The less costly operations far outnumber the costly one • Thus over the long-term they are “paying back” the program over a number of iterations
Amortized Analysis • Requires knowledge about the entire series of operations • Usually where a state persists between operations • Like the capacity of memory allocated • The idea is the worst case operation can alter the state in such a way that the worst case cannot occur again for a “long” time • thus amortizing its cost
Applying Amortization Analysis (aka Aggregate Analysis) • Aggregate analysis determines the upper bound T(n) of the total cost of a sequence of n operations • T(n) is what we have been calculating previously for our Big-Oh stuff • Then the amortized cost is • T(n) / n • because we make “small payments” for the worst operation across each operation
Marker Slide • Questions on: • Stacks • • Description Application STATIC Array Based DYNAMIC Array Based • Amortization • Description • Next up • Amortization • Applied to Incremental Increase for Dynamic Array Resizing • Applied to Doubling Increase for Dynamic Array Resizing • Stack Implementation Analysis • Static Array versus Dynamic Array • Linked List Refresher • Stack Implemented as a Linked List
Dynamic Array-based Stack Recall: • In a push operation, when the array is full, instead of throwing an exception, we can replace the array with a larger one • How large should the new array be? • incremental strategy: increase the size by a constant c (say c = 2) • doubling strategy: double the size Algorithm push(e) if t = S. length 1 then A new array of size … for i 0 to t do A[i] S[i] S A t t + 1 S[t] o Recall we used c=2 for the Pitcher class
Apply to +2(incremental) vs. double • We compare the incremental strategy and the doubling strategy by analyzing the total time T(n) needed to perform a series of n push operations. • Assume we start with an empty stack represented by an array of size 1 • We call the amortized time of a push operation: • the average time taken by a push over the series of operations • i. e. T(n) / n
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times We start with an array of capacity 2 and size 0 (empty) How do we know it will replace the array k = n/c times? Assume a call to push() takes time 1 unit Think: hownmany sizesoc”need are inn atime set of n things? n/c we will push things“groups one at aoftime • The total time T(n) of a series of n push operations is proportional to Each time we go past our capacity (k = n/2 times) we will increase capacity by c = 2 • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 6 items the third, 8 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need for 2 + 4 + 6 + 8 + … + 2*k units of time total time = n + 2*2 + 2*3 + 2*4 +…+2*k
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times We start with an array of capacity 2 and size 0 (empty) How do we know it will replace the array k = n/c times? Assume a call to push() takes time 1 unit Think: hownmany sizesoc”need are inn atime set of n things? n/c we will push things“groups one at aoftime • The total time T(n) of a series of n push operations is proportional to Each time we go past our capacity (k = n/2 times) we will increase capacity by c = 2 • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 6 items the third, 8 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need for 2 + 4 + 6 + 8 + … + 2*k units of time total time = n + 2*2 + 2*3 + 2*4 +…+2*k
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times We start with an array of capacity 2 and size 0 (empty) How do we know it will replace the array k = n/c times? Assume a call to push() takes time 1 unit Think: hownmany sizesoc”need are inn atime set of n things? n/c we will push things“groups one at aoftime • The total time T(n) of a series of n push operations is proportional to Each time we go past our capacity (k = n/2 times) we will increase capacity by c = 2 • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 6 items the third, 8 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need for 2 + 4 + 6 + 8 + … + 2*k units of time total time = n + 2*2 + 2*3 + 2*4 +…+2*k
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times We start with an array of capacity 2 and size 0 (empty) • The total time T(n) of a series of n push operations is proportional to Assume a call to push() takes time 1 unit we will push n things one at a time so need n time Each time we go past our capacity (k = n/2 times) we will increase capacity by c = 2 • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 6 items the third, 8 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need for 2 + 4 + 6 + 8 + … + 2*k units of time total time = n + 2*2 + 2*3 + 2*4 +…+2*k
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times We start with an array of capacity 2 and size 0 (empty) • The total time T(n) of a series of n push operations is proportional to Assume a call to push() takes time 1 unit we will push n things one at a time so need n time Each time we go past our capacity (k = n/2 times) we will increase capacity by c = 2 • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 6 items the third, 8 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need for 2 + 4 + 6 + 8 + … + 2*k units of time total time = n + 2*2 + 2*3 + 2*4 +…+2*k
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times We start with an array of capacity 2 and size 0 (empty) • The total time T(n) of a series of n push operations is proportional to Assume a call to push() takes time 1 unit we will push n things one at a time so need n time Each time we go past our capacity (k = n/2 times) we will increase capacity by c = 2 • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 6 items the third, 8 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need 2 + 4 + 6 + 8 + … + 2*k units of time total time = n + 2*2 + 2*3 + 2*4 +…+2*k
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times We start with an array of capacity 2 and size 0 (empty) • The total time T(n) of a series of n push operations is proportional to Assume a call to push() takes time 1 unit we will push n things one at a time so need n time Each time we go past our capacity (k = n/2 times) we will increase capacity by c = 2 • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 6 items the third, 8 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need 2 + 4 + 6 + 8 + … + 2*k units of time total time = n + 2*1 + 2*2 + 2*3 + 2*4 +…+2*k
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times We start with an array of capacity 2 and size 0 (empty) • The total time T(n) of a series of n push operations is proportional to Assume a call to push() takes time 1 unit we will push n things one at a time so need n time Each time we go past our capacity (k = n/2 times) we will increase capacity by c = 2 • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 6 items the third, 8 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need 2 + 4 + 6 + 8 + … + 2*k units of time total time = n + 2*1 + 2*2 + 2*3 + 2*4 +…+2*k
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times total time = n + 2*2 + 2*3 + 2*4 +…+2*k • The total time T(n) of a series of n push but we were using c = 2 for that… now put the c back in operations is proportional to total time = n + c*2 + c*3 + c*4 +…+ c*k • n + c + 2 c + 3 c + 4 c + … + kc Next we simplify • Since c is a constant T(n) is O(n + k 2) = O(n 2) • Divide by T(n) by n • The amortized time is O(n)
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times • The total time T(n) of a series of n push operations is proportional to • n + c + 2 c + 3 c + 4 c + … + kc • .
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times • The total time T(n) of a series of n push operations is proportional to • n + c + 2 c + 3 c + 4 c + … + kc • . n stays n, c*(k 2 + k)/2 = (c/2)*k 2 + k/2 => k 2
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times • The total time T(n) of a series of n push operations is proportional to • n + c + 2 c + 3 c + 4 c + … + kc • . Substitute in n/c for k and simplify
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times • The total time T(n) of a series of n push operations is proportional to • n + c + 2 c + 3 c + 4 c + … + kc • . = O( n 2 )
Incremental Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = n/c times • The total time T(n) of a series of n push operations is proportional to 2) So is O(n ) = +O(n • n…+T(n) c + 2 c + 3 c+ +k 24 c … ++nkc = O( n 2 ) • . And the Amortized Time is T(n)/n = O( n ) • .
Summary So Far • Amortized Analysis tells us • Incremental Increase Method is • O(n) • Next we do similar for the Doubling Method
Marker Slide • Questions on: • Stacks • STATIC Array Based • DYNAMIC Array Based • Amortization • Description • Applied to Incremental Increase for Dynamic Array Resizing • Next up • Amortization • Applied to Doubling Increase for Dynamic Array Resizing • Stack Implementation Analysis • Static Array versus Dynamic Array • Linked List Refresher • Stack Implemented as a Linked List
Doubling Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = log 2 n times We start with an array of capacity 2 and size 0 (empty) • The total time T(n) of a series of n push operations is proportional to Think on: a call How we know replaces Assume todo push() takesittime 1 unitthe array k = lg n times ? we will push n things one at a time so need n time Each time we go past our capacity (k = log 2 n times) we will double capacity • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 8 items the third, 16 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need for 2 + 4 + 8 + 16 + … + 2 k units of time total time = n + 2 + 4 + 8 + 16 + … + 2 k
Doubling Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = log 2 n times We start with an array of capacity 2 and size 0 (empty) • The total time T(n) of a series of n push operations is proportional to Think on: a call How we know replaces Assume todo push() takesittime 1 unitthe array k = lg n times ? n isn the number ofatimes bendivided we will lg push things one at time nsocan need time by 2… Each time we go past our capacity (k = log 2 n times) we will double capacity • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 8 items the third, 16 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need for 2 + 4 + 8 + 16 + … + 2 k units of time total time = n + 2 + 4 + 8 + 16 + … + 2 k
Doubling Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = log 2 n times We start with an array of capacity 2 and size 0 (empty) • The total time T(n) of a series of n push operations is proportional to Assume a call to push() takes time 1 unit we will push n things one at a time so need n time Each time we go past our capacity (k = log 2 n times) we will double capacity • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 8 items the third, 16 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need for 2 + 4 + 8 + 16 + … + 2 k units of time total time = n + 2 + 4 + 8 + 16 + … + 2 k
Doubling Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = log 2 n times We start with an array of capacity 2 and size 0 (empty) • The total time T(n) of a series of n push operations is proportional to Assume a call to push() takes time 1 unit we will push n things one at a time so need n time Each time we go past our capacity (k = log 2 n times) we will double capacity • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 8 items the third, 16 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need for 2 + 4 + 8 + 16 + … + 2 k units of time total time = n + 2 + 4 + 8 + 16 + … + 2 k
Doubling Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = log 2 n times We start with an array of capacity 2 and size 0 (empty) • The total time T(n) of a series of n push operations is proportional to Assume a call to push() takes time 1 unit we will push n things one at a time so need n time Each time we go past our capacity (k = log 2 n times) we will double capacity • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 8 items the third, 16 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need for 2 + 4 + 8 + 16 + … + 2 k units of time total time = n + 2 + 4 + 8 + 16 + … + 2 k
Doubling Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = log 2 n times We start with an array of capacity 2 and size 0 (empty) • The total time T(n) of a series of n push operations is proportional to Assume a call to push() takes time 1 unit we will push n things one at a time so need n time Each time we go past our capacity (k = log 2 n times) we will double capacity • n + c + 2 c + 3 c + 4 c + … + kc And we will have to copy the stuff already in the array into the new array So 2 items the first time, 4 items the second, 8 items the third, 16 items the fourth … • Since c is a constant T(n) is O(n + k ) = O(n ) 2 2 Assuming each item we copy requires time 1 unit So 2 units of time for 2 items, 4 units of times for 4 items, 6 units for 6 items, … • Divide by T(n) by n • The amortized time is O(n) We then have the need for 2 + 4 + 8 + 16 + … + 2 k units of time total time = n + 2 + 4 + 8 + 16 + … + 2 k
Doubling Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = log 2 n times total time = n + 2 + 4 + 8 + 16 + … + 2 k • The total time T(n) of a series of n push operations is proportional to • n + c + 2 c + 3 c + 4 c + … + kc • Since c is a constant T(n) is O(n + k 2) = O(n 2) • Divide by T(n) by n • The amortized time is O(n)
Doubling Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = log 2 n times • The total time T(n) of a series of n push Put into Summation Notation operations is proportional to • n + c + 2 c + 3 c + 4 c + … + kc • Since c is a constant T(n) is O(n + k 2) = O(n 2) • Divide by T(n) by n • The amortized time is O(n)
Doubling Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = log 2 n times • The total time T(n) of a series of n push operations is proportional to the Summation Simplify • n + c + 2 c + 3 c + 4 c + … + kc • Since c is a constant T(n) is O(n + k 2) = O(n 2) • Divide by T(n) by n • The amortized time is O(n)
Doubling Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = log 2 n times • The total time T(n) of a series of n push operations is proportional to • n + c + 2 c + 3 c + 4 c + … + kc Take a 2 out • Since c is a constant T(n) is O(n + k 2) = O(n 2) • Divide by T(n) by n • The amortized time is O(n)
Doubling Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = log 2 n times • The total time T(n) of a series of n push operations is proportional to • n + c + 2 c + 3 c + 4 c + … + kc Substitute lg n in for k • Since c is a constant T(n) is O(n + k 2) = O(n 2) • Divide by T(n) by n • The amortized time is O(n)
Doubling Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = log 2 n times • The total time T(n) of a series of n push operations is proportional to • n + c + 2 c + 3 c + 4 c + … + kc • Since c is a constant T(n) is. Simplify O(n + k 2) = O(n 2) • Divide by T(n) by n • The amortized time is O(n)
Doubling Analysis • Say our array grows to a final size of n • Then this strategy replaces the array k = log 2 n times • The total time T(n) of a series of n push operations is proportional to • . total time = n + 2 + 4 + 8 + 16 + … + 2 k = 3 n – 1 So T(n) is O(n) • Since c is a constant T(n) is O(n + k 2) = O(n 2) and the amortized • Divide bytime T(n) by /nn = O(n) / n • The amortized time is O(n) = O( 1 )
Marker Slide • Questions on: • Stacks • STATIC Array Based • DYNAMIC Array Based • Amortization • Description • Applied to Incremental Increase for Dynamic Array Resizing • Applied to Doubling Increase for Dynamic Array Resizing • Next up • Stack Implementation Analysis • Static Array versus Dynamic Array • Linked List Refresher • Stack Implemented as a Linked List
Conclusions of Analysis • So what did we learn? • If we use a dynamic array the amortized time for a push operation is O(1) • Why do we care? … Recall next slide
Recall: Performance and Limitations (static array-based implementation of stack ADT) • Performance – Let n be the number of elements in the stack – The space used is O(n) – Each operation (push, pop, top, size, empty) runs in time O(1) • Limitations – The maximum size of the stack must be defined a priori , and cannot be changed – Trying to push a new element onto a full stack causes an implementation-specific exception
Recall: Performance and Limitations (static array-based implementation of stack ADT) • Performance – Let n be the number of elements in the stack – The space used is O(n) Dynamic Arrays – Each operation (push, pop, top, size, empty) clearly fix this runs in time O(1) • Limitations … BUT… – The maximum size of the stack must be defined a priori , and cannot be changed – Trying to push a new element onto a full stack causes an implementation-specific exception
Recall: Performance and Limitations (static array-based implementation of stack ADT) • Performance – Let n be the number of elements in the stack – The space used is O(n) – Each operation (push, pop, top, size, empty) runs in time O(1) • Limitations Seemed to fail on this point – The maximum size of the stack must be defined a priori , and cannot be changed – Trying to push a new element onto a full stack causes an implementation-specific exception
Recall: Performance and Limitations (static array-based implementation of stack ADT) • Performance – Let n be the number of elements in the stack – The space used is O(n) – Each operation (push, pop, top, size, empty) runs in time O(1) But dynamic arrays are good here too • Limitations … – The maximum size of the stack must be defined a priori , and cannot be changedper the amortized analysis of doubling the capacity – Trying to push a new element onto a full stack causes an implementation-specific exception
Conclusion: Implementing Stack Using Dynamic Array • Using a Dynamic array to implement a stack meets the ADT specification requirements for a Stack • Doing so does NOT limit the stack size • like a static array • Amortization Analysis is required to see how it is also an efficient way to implement a Stack • Intuitively it is not necessarily obvious
Marker Slide • Questions on: • Stacks • STATIC Array Based • DYNAMIC Array Based • Amortization • • Description Applied to Incremental Increase for Dynamic Array Resizing Applied to Doubling Increase for Dynamic Array Resizing Static Array versus Dynamic Array • Next up • Linked List Refresher head towards Stacks again • Stack Implemented as a Linked List
Re vie w Singly Linked List • A singly linked list is a structure consisting of a sequence of nodes • A singly linked list stores a pointer to the first node (head) and last (tail) • Each node stores head – element – link to the next node tail Leonard Sheldon Howard Raj
Re vie w Singly Linked List • A singly linked list is a structure consisting of a sequence of nodes • A singly linked list stores a pointer to the first node (head) and last (tail) • Each node stores head – element – link to the next node tail Leonard Sheldon Howard Raj
Re vie w Singly Linked List • A singly linked list is a structure consisting of a sequence of nodes next • A singly linked list stores a pointer to the first node (head) and last (tail) head node elem • Each node stores – element – link to the next node tail Leonard Sheldon Howard Raj
Re vie w Singly Linked List Node • A singly linked list is a structure consisting of a sequence of nodes template <typename Type> next class SLinked. List. Node { • A singly linked list stores a public: pointer to the first node (head) Type elem; and last (tail) SLinked. List. Node<Type> *next; node elem }; • Each node stores – element – link to the next node Leonard Sheldon Howard Raj
w vie Re Singly Linked List • A singly linked list is a structure consisting of a sequence of nodes • Operations – insert. Front(e): inserts an element on the front of the list – remove. Front(): returns and removes the element at the front of the list – insert. Back(e): inserts an element on the back of the list – remove. Back(): returns and removes the element at the end of the list Details of each of these operations was given in previously
Marker Slide • Questions on: • Stacks • STATIC Array Based • DYNAMIC Array Based • Amortization • • Description Applied to Incremental Increase for Dynamic Array Resizing Applied to Doubling Increase for Dynamic Array Resizing Static Array versus Dynamic Array • Linked List Refresher head towards Stacks again • Next up • Stack Implemented as a Linked List
So far • Stacks implemented using • Static Arrays • Dynamic Arrays • Next • Linked Lists (also in the Mini. Stack homework)
Stack with a Singly Linked List • CLAIM: • We can implement a stack with a singly linked list • The top element of the stack is the first node of the list • The space used is O(n) and each operation of the Stack ADT takes O(1) time • Demonstration of how follows nodes top t elements
ca ll Re Stack and Singly Linked List • Stack Operations • Singly linked list Operations • push(e): inserts an element to the top of the stack • pop(): removes and returns the top element of the stack • top(): returns a reference to the top element of the stack, but doesn’t remove it • size(): returns the number of elements in the stack • empty(): returns a bool indicating if the stack contains any objects – insert. Front(e): inserts an element on the front of the list – remove. Front(): returns and removes the element at the front of the list – insert. Back(e): inserts an element on the back of the list – remove. Back(): returns and removes the element at the end of the list
Stack and Singly Linked List Singly linked list Operations • Stack Operations Top is the First • Node • push(e): inserts an element to the top of the stack • pop(): removes and returns the top element of the stack • top(): returns a reference to the top element of the stack, but doesn’t remove it • size(): returns the number of elements in the stack • empty(): returns a bool indicating if the stack contains any objects – insert. Front(e): inserts an element on the front of the list – remove. Front(): returns and removes the element at the front of the list – insert. Back(e): inserts an element on the back of the list – remove. Back(): returns and removes the element at the end of the list
Stack and Singly Linked List • Stack Operations • Singly linked list Operations • push(e): inserts an element to the top of the stack • pop(): removes and returns the top element of the stack • top(): returns a reference to the top element of the stack, but doesn’t remove it • size(): returns the number of elements in the stack • empty(): returns a bool indicating if the stack contains any objects – insert. Front(e): inserts an element on the front of the list – remove. Front(): returns and removes the element at the front of the list – insert. Back(e): inserts an element on the back of the list – remove. Back(): returns and removes the element at the end of the list
Stack and Singly Linked List • Stack Operations • Singly linked list Operations • push(e): inserts an element to the top of the stack • pop(): removes and returns the top element of the stack • top(): returns a reference to the top element of the stack, but doesn’t remove it • size(): returns the number of elements in the stack • empty(): returns a bool indicating if the stack contains any objects – insert. Front(e): inserts an element on the front of the list – remove. Front(): returns and removes the element at the front of the list – insert. Back(e): inserts an element on the back of the list – remove. Back(): returns and removes the element at the end of the list
Stack and Singly Linked List • Stack Operations • Singly linked list Operations • push(e): inserts an element to the top of the stack • pop(): removes and returns the top element of the stack • top(): returns a reference to the top element of the stack, but doesn’t remove it • size(): returns the number of elements in the stack • empty(): returns a bool indicating if the stack contains any objects – insert. Front(e): inserts an element on the front of the list – remove. Front(): returns and removes the element at the front of the list – insert. Back(e): inserts an element on the back of the list – remove. Back(): returns and removes the element at the end of the list
Stack and Singly Linked List • Stack Operations • Singly linked list Operations • push(e): inserts an element to the top of the stack • pop(): removes and returns the top element of the stack • top(): returns a reference to the top element of the stack, but doesn’t remove it • size(): returns the number of elements in the stack • empty(): returns a bool indicating if the stack contains any objects – insert. Front(e): inserts an element on the front of the list – remove. Front(): returns and removes the element at the front of the list top() would require a minor alteration or addition to Linked. List very similar to remove. Front()
Stack and Singly Linked List • Stack Operations • Singly linked list Operations • push(e): inserts an element to the top of the stack • pop(): removes and returns the top element of the stack • top(): returns a reference to the top element of the stack, but doesn’t remove it • size(): returns the number of elements in the stack • empty(): returns a bool indicating if the stack contains any objects – insert. Front(e): inserts an element on the front of the list – remove. Front(): returns and removes the element at the front of the list size() and is. Empty() would require the addition of a counter that increments each time push() is called and decrements when pop() is called
Stack with a Singly Linked List • CONCLUSION: • • We can implement a stack with a singly linked list The top element of the stack is the first node of the list The space used is O(n) and each operation of the Stack ADT takes O(1) time • push, pop, top, size, empty each are O(1) time nodes top t elements
Stack Summary • Stack Operation Complexity for Different Implementations Array Fixed-Size Dynamic (doubling strategy) Singly Linked List Pop() O(1) Push(o) O(1) O(n) Worst Case O(1) Best Case O(1) Average Case O(1) Top() O(1) Size(), is. Empty() O(1)
The End • For next time • Read Chapters 7 and 8 (if not already done) • Stacks and Queues
- Slides: 108