Stacks
Ordered List Very general data type: Set of n elements, where n >= 0 A = (a0, a1, a2, … an-1) Indexing of elements to provide a specific order When n = 0, define as null or empty list
Stacks Ordered list with property: Insertions and deletions always occur at the same end. INSERT DELETE A3 A3 TOP A2 TOP A2 A2 TOP A1 A1 A1 A0 A0 A0
Stacks as Ordered Lists Stack S = (a0, a1, a2, … an-1) a0 = bottom element an-1 = top element ai is on top of ai-1, for all i between 0 and n
Stack Example Real World Stack Example: Function call stack during program execution When a new function begins execution, it is placed on top of the call stack. When a function completes, it is removed from the stack and control returns to its calling function which is below it on the stack. Recursion can cause dramatic increases in height because functions aren’t returning. readFile() getData() main()
Stack Operations Add an item to the stack (PUSH) Place on top Delete an item from the stack (POP) Remove from top Check if empty See if number of entries is == 0 Check if full See if number of entries is == maxSize
Stack API Stack Definition: template <class KeyType> class Stack { public: Stack(int maxSize=DefaultSize); ~Stack(); bool isFull(); bool isEmpty(); void Add(const KeyType& item); KeyType* Delete(KeyType&); }
Stacks Implementation Implementation with arrays: Declare an array of size maxSize Have a reference to the current top spot in the array. Private data members: KeyType *stack; // array of KeyType int maxSize; // max elements int top; // current top index
Stack Constructor Declare array to be maxSize Set initial top to indicate nothing in stack template <class KeyType> Stack<KeyType>::Stack(int maxStackSize):maxSize(maxStackSize) { stack = new KeyType[maxSize]; top = -1; }
Stack Destructor Delete memory for array Set top to indicate nothing in stack template <class KeyType> Stack<KeyType>::~ Stack() { delete [] stack; top = -1; }
Stack Implementation IsFull(), IsEmtpy() just need to look at top reference. template <class KeyType> bool Stack<KeyType>::IsFull() { return (top == (maxSize-1)); } Template <class KeyType> Bool Stack<KeyType>::IsEmpty() return (top == -1);
Stack Implementation Adding should: Ensure not already full Place on top of stack Incrementing top Storing in array template <class KeyType> void Stack<KeyType>::Add(const KeyType& x) { if (IsFull()) return; else { top = top + 1; stack[top] = x; }
Stack Implementation Deleting should: Ensure not already empty Remove from top of stack Decrementing top Returning data template <class KeyType> KeyType* Stack<KeyType>::Delete(KeyType& x) { if (IsEmpty()) return; else { x = stack[top]; top = top – 1; return &x; }
Stack Examples Rewrite Stack::Add() so that when the stack is full, a new array of twice the size of the current array is allocated and used. Ensure that the current indices (top) will still work in the new array: void Stack<KeyType>::Add(const KeyType& x) { if (IsFull()) { maxSize = 2 * maxSize; KeyType* temp = array; array = new KeyType[maxSize]; for (int i = 0; i < maxSize/2; i++) { array[i] = temp[i] }; delete [] temp; } top = top + 1; array[top] = x;
Linked Stacks There are problems with implementing stacks on top of arrays Sizing problems (bounds, clumsy resizing, …) Given concepts of list nodes, can take advantage of to represent stacks. Need to determine appropriate way of: Representing top Facilitating node addition and deletion at the top of the stack
Linked Stacks Add HAT Add CAT Add MAT Add RAT Delete RAT TOP After 4th Add Add HAT Add CAT Add MAT Add RAT Delete MAT TOP After 3rd Add TOP After 1st Delete CAT TOP After 2nd Add HAT TOP (Null initially)
Linked Stacks Need to represent StackNodes Need to represent Stack Data element Pointer to node beneath it in the stack Need to represent Stack Pointer to a StackNode top that indicates the top of the stack
Linked Stack Definition Class StackNode{ friend class Stack; public: StackNode(int d, StackNode * l); private: int data; StackNode *link; };
Linked Stack Definition class Stack { public: Stack(); void Add(const int); int* Delete(int&); bool isEmpty(); private: StackNode* top; void StackEmpty(); }
Linked Stack Implementation Stack::Stack() { top = 0; } bool Stack::isEmpty() return (top == 0);
Linked Stack Implementation void Stack::Add(const int y) { // create a new node that contains data y // and points to the old top // assign new top to pointer to the new node top = new StackNode(y, top); }
Linked Stack Implementation int * Stack::Delete(int & retValue) { // handle empty case if (isEmpty()) { StackEmpty(); return 0;} StackNode* toPop = top; retValue = toPop.data; top = toPop->link; delete toPop; return &retValue; }
Stack Examples Consider a railroad switching network as drawn below: 1 2 3 … N What are possible permutations of cars one could have using this system? Stack
Stack Examples Total possible permutations with n = 3 1,2,3 Add 1, Delete 1 Add 2, Delete 2 Add 3, Delete 3 1 2 3 2,3,1 Add 1, Add 2 Delete 2, Add 3 Add 3, Delete 1 1,3,2 Add 1, Delete 1 Add 2, Add 3 Delete 3, Delete 2 2,1,3 Add 1, Add 2 Delete 2, Delete 1 Add 3, Delete 3 3,2,1 Add 1, Add 2 Add 3, Delete 3 Delete 2, Delete 1 3,1,2 – Not possible Either one moves out first or two has to be on top of one
Stack Examples Testing Palindromes kayak => Should be same on front and back, could pull off front and back characters and compare as in program2 Also, should read same in forwards direction as in reverse direction: kayak: k1a2y3a4k5 k5a4y3a2k1 rogue: r1o2g3u4e5 e5u4g3o2r1
Stack Examples Using a stack to match palindromes: Construct a stack large enough to hold entire string maxSize if array-based stack = string.length() Push whole string onto Stack Now, only way to read word from Stack is in reverse In unison, Step through string from front Delete from top of stack Compare characters Code in palindrome.cpp
Stacks: Expression Evaluation Combination of operators and operands Evaluates to some value X = A/B – C + D * E – A * C Many different interpretations: A/(B-C) + D *E – A * C (A/B) – (C + D) * (E-A) * c A/(B-C+D*E) – (A*C) …
Expression Evaluation Defining order of operations provides the appropriate semantics for evaluating such an expression Equivalent level – go left to right X = A/B – C + D * E – A * C = ((((A/B) – C) + (D*E)) – (A*C)) Priority Operator 1 Unary -, ! 2 *,/,% 3 +,- 4 <,<=,>,>= 5 ==,!= 6 && 7 ||
Expression Evaluation How does compiler ensure correct code? Converts infix notation to postfix notation: Infix: Operators appear between operands 3 + 5 * 4 Postfix: Operators appear after operands 3 5 4 * +
Expression Evaluation Infix: A / B – C + D * E – A * C Postfix: A B / C - D E * + A C * - Postfix Evaluation: T1 = A/B T1C-DE*+AC*- T2 = T1 – C T2DE*+AC*- T3 = D*E T2T3+AC*- T4 = T2+T3 T4AC*- T5 = A * C T4T5- T6 = T4-T5 T6
Expression Evaluation Why use postfix? No need for parentheses Priority is no longer important (explicit in the ordering) Simple to evaluate using a stack, storing temporary values after computation
Expression Evaluation Evaluating postfix expressions: // Assume last token is # void eval(expression e) { Stack<token> stack; // initialize stack for (token x = NextToken(e); x != ‘#’, x = NextToken(e)) if (operand(x)) stack.Add(x); else // x is an operation a = *stack.Delete(a); b = *stack.Delete(b); stack.Add(perform( x, a, b)); }
Evaluating Expressions Postfix: A B / C - D E * + A C * - / - + * - * E C B C D T3 A T5 A A T1 T1 T2 T2 T2 T4 T4 T4 T6
Expression Evaluation Converting infix to postfix (Conceptually): 1) Fully parenthesize the expression 2) Move all operators so they replace corresponding right parentheses 3) Delete all parentheses Operands remain in same order, so once we encounter one, we can print it directly out, need to determine when to print operators
Expression Evaluation Start State: Infix Notation Expression: A+B * C Fully Parenthesize by OrderOfOp: (A+(B*C)) Move Operators To Right Parentheses: (A(BC*+ Remove Parentheses: ABC*+ Evaluate: A B C * + => A T1 + => T2 (Answer)
Expression Evaluation Converting infix to postfix: Store operators in stack until time to use A + B * C => A B C * + Next token Stack Output None Empty None A Empty A + + A B + AB Do we place * on top of stack or pop + off? * is higher priority so it goes on stack * +* AB C +* ABC No more tokens, output all operators off stack ABC*+
Expression Evaluation A*(B+C)*D => ABC+*D* Next token Stack Output None Empty None A Empty A * * A ( *( A B *( AB + *(+ AB C *(+ ABC // unstack to left parentheses ) * ABC+ * * ABC+* D * ABC+*D Done Empty ABC+*D*
Expression Evaluation Rule for what to unstack: Let operator priority be as in table to right Let in-stack priority of ‘(‘ be 8 Let incoming priority of ‘(‘ be 0 Operators are taken out of the stack as long as their in-stack priority is numerically less than or equal to the incoming priority of the new operator. Clears on end of tokens Clears up to left-parentheses on ) Forces onto stack when have ( Priority Operator 1 Unary -, ! 2 *,/,% 3 +,- 4 <,<=,>,>= 5 ==,!= 6 && 7 ||
Expression Evaluation void toPostfix(Expression e) { Stack<token> stack; token y; stack.Add(‘#’); for (token x = NextToken(e); x!= ‘#”; x = NextToken(e)) if (operand(x)) cout << operand; else if (x == ‘)’) { for (y = *stack.Delete(y); y != ‘(‘; y = *stack.Delete(y)) cout << y; } else { // x is an operator for (y = *stack.Delete(y); isp(y) <= icp(x); y = *stackDelete(y)) {cout << y;} stack.Add(y); // restack the last y that was unstacked stack.Add(x); while (!(stack.IsEmpty()) cout << *stack.Delete(y);
Expression Evaluation Big O Analysis: Look at each operand at most once: O(1) Each operator is stacked/unstacked usually one time: [every once in a while more than that if it’s an operator that is checked as whether or not should remain on stack and it should]: O(1) N total operands: O(N)
Maze Search 0 1 0 0 0 1 1 0 0 0 1 1 1 1 1 1 0 0 0 1 1 0 1 1 1 0 0 1 1 1 0 1 1 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 1 1 1 1 0 1 1 0 1 1 0 0 1 1 0 1 0 0 1 0 1 1 1 1 1 1 1 0 0 1 1 0 1 1 1 0 1 0 0 1 0 1 0 1 1 1 1 0 0 1 1 1 1 1 1 1 1 0 0 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 0 0 0 1 1 0 1 1 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 1 0
Maze Search 0 1 0 0 0 1 1 0 0 0 1 1 1 1 1 1 0 0 0 1 1 0 1 1 1 0 0 1 1 1 0 1 1 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 1 1 1 1 0 1 1 0 1 1 0 0 1 1 0 1 0 0 1 0 1 1 1 1 1 1 1 0 0 1 1 0 1 1 1 0 1 0 0 1 0 1 0 1 1 1 1 0 0 1 1 1 1 1 1 1 1 0 0 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 0 0 0 1 1 0 1 1 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 1 0
Maze Search Maze has two dimensions: m rows, p columns Natural representation: 2-dimensional array maze[i][j] Can move in any of eight directions: -1 to +1 rows -1 to +1 cols
Maze Search Basic algorithmic idea: Maintain current successful path Pick one of 8 possible directions [start with East] If not open, select next direction [clockwise] Else, move in new direction When fail, return [backtrack] to last successful state which has directions left to try
Maze Search Implementation: Stack maintains current path: Add and delete new locations Maximum size needed for stack array? Longest possible path through maze = rows * columns 2,3,SW 1,3,S 1,2,E 1,1,E 0,0,SE
Maze Search Mark array: 2-D, same size as maze, Update visited locations with a 1 indicating unsuccessful so don’t repeatedly try a route that is known to fail.
Maze Search Implementation: maze.cpp Examination of stack usage: maze.cpp
Maze Search Big O Analysis: Difficult to make anything but an upper bound estimate. Number of iterations of while loop is dependent on actual path in maze. Since mark visited spots, never search a path more than once. For each spot, look at a maximum of 8 directions. Upper bound: O(rows * columns), constant is ~8