CSE 214 Computer Science II Stack Spring 2018 Stony Brook University Instructor: Shebuti Rayana shebuti.rayana@stonybrook.edu http://www3.cs.stonybrook.edu/~cse214/sec02/
Random and Sequential Access Random Access Data Structures Example: array each element can be accessed directly and in constant time typical illustration of random access is a book - each page of the book can be open independently of others random access is critical to many algorithms, for example binary search. Sequential Access Data Structures Example: linked list each element can be accessed only in particular order typical illustration of sequential access is a roll of paper or tape - all prior material must be unrolled in order to get to data you want. Shebuti Rayana (CS, Stony Brook University) 2
Random and Sequential Access Shebuti Rayana (CS, Stony Brook University) 3
Limited Access Data Structures A subcase of sequential data structures called limited access data structures: Stacks and Queues stack - elements can be added and removed from the stack only at the top queue - elements can be added at the back and can be removed only from the front They are limited access because access is restricted only to certain terminal positions. Shebuti Rayana (CS, Stony Brook University) 4
Stack In stacks only two operations are allowed: push adds an item to the top of the stack, pop removes the item from the top. A helpful analogy is to think of a stack of books; you have to remove the top book first, also if you want to add new book, you have to add it on the top. Shebuti Rayana (CS, Stony Brook University) 5
Stack: Applications The simplest application of a stack is to reverse a word. You push a given word to stack - letter by letter - and then pop letters from the stack. Another application is an "undo" mechanism in text editors; this operation is accomplished by keeping all text changes in a stack. Programming language processing: space for parameters and local variables is created internally using a stack. compiler's syntax check for matching braces is implemented by using stack. support for recursion Shebuti Rayana (CS, Stony Brook University) 6
Stack: Applications Backtracking. This is a process when you need to access the most recent data element in a series of elements. Think of a labyrinth or maze - how do you find a way from an entrance to an exit? Once you reach a dead end, you must backtrack. But backtrack to where? to the previous choice point. Therefore, at each choice point you store on a stack all possible choices. Then backtracking simply means popping a next choice from the stack. Shebuti Rayana (CS, Stony Brook University) 7
Stack Interface The underlying structure for a stack could be, (i) an array, or (ii) a linked list, or (iii) any other collection. Regardless of the type of the underlying data structure, a Stack must implement the same functionality: Allow to add (push) only at the top with running time constant O(1) Allow to remove (pop) only from the top with running time constant O(1) Shebuti Rayana (CS, Stony Brook University) 8
Stack API (Cont.) Regardless of whether stack is implemented based on Array or LinkedList, it should have following operations: Shebuti Rayana (CS, Stony Brook University) 9
Stack: Linked List Implementation Maintain pointer first/top_of_stack (head) to the first node in a singlylinked list. Push new item before first/top_of_stack. (addtofirst() for Linked List) Pop item from first/top_of_stack. (RemoveFirst() for LinkedList) Shebuti Rayana (CS, Stony Brook University) 10
Stack: Linked List Implementation (Cont.) Output: d c b a Shebuti Rayana (CS, Stony Brook University) 11
Stack: Push save a link to the list Node oldfirst = first; first oldfirst c b a create a new node for the beginning first = new Node(); null first c b a null oldfirst set the instance variable in the new node first.item = item; First.next = oldfirst; first d c b a null Shebuti Rayana (CS, Stony Brook University) 12
Stack: Pop save item to return String item = first delete first node first = first.next; first first c b a c b a null null Return saved item return item Shebuti Rayana (CS, Stony Brook University) 13
Stack: Array Implementation (Fixed Capacity) Use array s[] to store N items on stack push(): add new item at s[n] pop(): remove item from s[n-1] Defect: Stack overflows when N exceeds capacity. Shebuti Rayana (CS, Stony Brook University) 14
Stack: Array Implementation (Fixed Capacity) Shebuti Rayana (CS, Stony Brook University) 15
Stack: Array Implementation (Fixed Capacity) Output: d c b a Shebuti Rayana (CS, Stony Brook University) 16
Stack: Array Implementation Considerations Overflow and Underflow Underflow: Occurs when stack is already empty and an attempt to pop() is made Throw exception if pop is called on an empty stack. Overflow: Occurs for fixed capacity implementation by the index of top exceeding the capacity. Use resizing array for array implementation. Shebuti Rayana (CS, Stony Brook University) 17
Stack: Array Implementation Considerations Loitering: Holding a reference to an object when it is no longer needed public String pop() { return s[- - top]; } loitering public String pop() { String item = s[- - top]; s[top] = null; return item; } this version avoids "loitering": garbage collector can reclaim memory for an object only if no outstanding references Shebuti Rayana (CS, Stony Brook University) 18
Stack: Array Implementation (Resizing Array) Resizing array on push and pop operation push(): increase size of array s[] by 1 pop(): decrease size of array s[] by 1 Shebuti Rayana (CS, Stony Brook University) 19
Stack: Array Implementation (Resizing Array) Too expensive. Need to copy all items to a new array, for each operation Array accesses to insert first N items = 1 array access per push N + (2 + 4 + + 2(N 1)) ~ N 2 To expensive for large N 2(n-1) array accesses to expand to size k We must ensure that array resizing happens infrequently Shebuti Rayana (CS, Stony Brook University) 20
Stack: Stack: Array Implementation (Resizing Array) Resizing array using repeated doubling strategy: push(): If array is full, create a new array of twice the size, and copy items Shebuti Rayana (CS, Stony Brook University) 21
Stack: Array Implementation (Resizing Array) Array accesses to insert first N = 2 i items = 1 array access per push N + (2 + 4 + 8 + + 2^logN) ~ 3N. Much better k array accesses to expand to size k (ignoring cost to create new array) Shebuti Rayana (CS, Stony Brook University) 22
Stack: Array Implementation (Resizing Array) Shrinking the array: push(): double size of array s[] when array is full pop(): halve size of array s[] when array is one-half full. Too expensive in worst case. Consider push-pop-push-pop- sequence when array is full. Each operation takes time proportional to N (top). Shebuti Rayana (CS, Stony Brook University) 23
Stack: Array Implementation (Resizing Array) Efficient solution push(): double size of array s[] when array is full. pop(): halve size of array s[] when array is one-quarter full. Array is between 25% and 100% full Shebuti Rayana (CS, Stony Brook University) 24
Stack: Array Implementation Performance Amortized analysis: Starting from an empty data structure, average running time per operation over a worst-case sequence of operations. Proposition: Starting from an empty stack, any sequence of M push and pop operations takes time proportional to M. Shebuti Rayana (CS, Stony Brook University) 25
Stack: Array Implementation Performance Therefore, n insertions take O(n) time. Shebuti Rayana (CS, Stony Brook University) 26
Stack Implementation: Resizing Array vs Linked List Linked-list implementation Every operation takes constant time in the worst case. Uses extra time and space to deal with the links. Resizing-array implementation. Every operation takes constant amortized time. Less wasted space. Shebuti Rayana (CS, Stony Brook University) 27
Stack: Complete Implementation Shebuti Rayana (CS, Stony Brook University) 28
Stack: Complete Implementation (Linked List) Shebuti Rayana (CS, Stony Brook University) 29
Stack: Complete Implementation (Linked List) Shebuti Rayana (CS, Stony Brook University) 30
Stack: Complete Implementation (Linked List) Shebuti Rayana (CS, Stony Brook University) 31
Stack: Complete Implementation (Array) Shebuti Rayana (CS, Stony Brook University) 32
Stack: Complete Implementation (Array) Shebuti Rayana (CS, Stony Brook University) 33
Stack: Application 1 Balanced Parenthesis: An arithmetic expression has balanced parenthesis if and only if: the number of left parentheses of each type is equal to the number of right parentheses of each type each right parenthesis of a given type matches to a left parenthesis of the same type to its left and all parentheses in between are balanced correctly. Example: ({A + B} C) Balanced ({A + B) C} Not balanced ({A + B} [C / D]) Balanced (({A + B} C) / D)) Not balanced Shebuti Rayana (CS, Stony Brook University) 34
Algorithm: check for balanced parenthesis Scan the expression from left to right. For each left parenthesis that is found, push on the stack For each right parenthesis that is found, If the stack is empty, return false(too many right parentheses) Otherwise, pop the top parenthesis from the stack: If the left and right parentheses are of the same type, discard. Otherwise, return false. If the stack is empty when the scan is complete, return true. Otherwise, return false. (too many left parentheses) Shebuti Rayana (CS, Stony Brook University) 35
Trace Shebuti Rayana (CS, Stony Brook University) 36
Stack: Application 2 Evaluating Expressions: An expression is fully parenthesized if every operator has a pair of balanced parentheses marking its left and right operands. Not fully-parenthesized: 3 * (5 + 7) 9 (2-4) * (5-7) + 8 Fully-parenthesized: ((3 * (5 + 7)) 9)(((2-4)*(5-7))+ 8) Shebuti Rayana (CS, Stony Brook University) 37
General Idea The first operation to perform is surrounded by the innermost set of balanced parentheses. Example:((3 * (5 + 7)) 9) First op: + By reading expression from left to right, first operator comes immediately before first right parenthesis. Replace that subexpression with its result and search for next right parenthesis, etc. Example:((3 * 12) 9) = (36 9) = 27 Shebuti Rayana (CS, Stony Brook University) 38
General Idea (cont.) How do we keep track of operands and operators as we read past them in the expression from left to right? Use two stacks: one for operands and one for operators. When we encounter a right parenthesis, pop off one operator and two operands, perform the operation, and push the result back on the operand stack. Shebuti Rayana (CS, Stony Brook University) 39
Trace Shebuti Rayana (CS, Stony Brook University) 40
Algorithm Let each operand or operator or parenthesis symbol be a token. Let NumStack store the operands. Let OpStack store the operations. For each token in the input expression do If token = operand, NumStack.push(token) If token = operator, OpStack.push(token) If token = ), operand2ß NumStack.pop() operand1ß NumStack.pop() operator ß OpStack.pop() result ß operand1 operator operand2 NumStack.push(result) If token = (, ignore token After expression is parsed, answer ß NumStack.pop() 41
Stack: Application 3 Arithmetic Expression: Infix notation: operator is between its two operands 3 + 5 (5 + 7) * 9 5 + (7 * 9) Prefix notation: operator precedes its two operands + 3 5 * + 5 7 9 + 5 * 7 9 Postfix notation: operator follows its two operands 3 5 + 5 7 + 9 * 5 7 9 * + Shebuti Rayana (CS, Stony Brook University) 42
Precedence of Operators Multiplication and division (higher precedence) are performed before addition and subtraction (lower precedence) Operators in balanced parentheses are performed before operators outside of the balanced parentheses. If two operators are of the same precedence, they are evaluated left to right. Shebuti Rayana (CS, Stony Brook University) 43
Example Infix expression: A + B * ( C * D E / F ) / G H 6 4 1 3 2 5 7 What is prefix equivalent? + A / * B * C D / E F G H 7 6 5 4 3 1 2 What is postfix equivalent? A B C D * E F / * G / + H 1 2 3 4 5 6 7 Shebuti Rayana (CS, Stony Brook University) 44
Evaluating a Postfix Expression Let each operand or operator be a token. Let NumStack store the operands. For each token in the input expression do If token = operand, NumStack.push(token) If token = operator, operand2ß pop() operand1ß pop() result ß operand1 operator operand2 NumStack.push(result) answer ß pop() Shebuti Rayana (CS, Stony Brook University) 45
Trace Shebuti Rayana (CS, Stony Brook University) 46
Translating Infix to Postfixt Let each operand, operator, or parenthesis be a token. Let OpStack store the operators. Let postfix string P = (empty string) For each token in the input expression do If token = operand, append operand to P If token = operator, push(token) If token = ), append pop() to P If token = (, ignore Shebuti Rayana (CS, Stony Brook University) 47
Trace Shebuti Rayana (CS, Stony Brook University) 48