Data Structures via C++ Lecture 7: Stacks Dr. Shaker El-Sappagh Benha University Faculty of Computers and Informatics Department of Information Systems 2017 What is a stack? - A container is an ADT that can hold multiple objects and structure them in a different way. - Stack is a container that stores items in LIFO order. - Items are added and removed from the same end (head). - Stack reverses the order of items. - Stack has these operations: 1. The push operation adds elements to the stack, 2. the pop operation retrieve elements, 3. the isEmpty operation to check if the stack is empty, 4. the top return the top element without modifying the stack. Implementing the stack by array - Stack stores elements of the same type (homogeneous), so array data structure is suitable. - The two ways of pushing: 1. Fixing one position (index 0) as the top of the stack. What is the problem? What is the cost for push and pop? O(n) 0 1 2 3 4 5 ? ? ? ? ? ? push (‘a’) 0 1 2 3 4 5 a ? ? ? ? ? push (‘b’) 0 1 2 3 4 5 b a ? ? ? ? Implementing the stack by array 2. Let the top of the stack flow up and down by pushing at the end of the stack. Set the top as an integer. Set top= -1 for empty stack. For each push, top++. For each pop, top--. 0 1 2 3 4 5 top = -1 ? ? push (‘a’) ? ? ? ? top = 0 0 a 1 ? 2 ? 3 ? 4 ? 5 ? push (‘b’) 0 1 2 3 4 5 top = 1 a b ? ? ? ? Implementing the stack by array C++ implementation of the stack: - The stackArray is an array to store maxStackSize elements. - The topIndex stores the top of stack. Initially, topIndex = -1. - In push operation: topIndex ++, stackArray [topIndex]= new item. - As a start, items are of type char. - In pop operation: return stackArray [topIndex], topIndex --. - The isEmpty operation check if topIndex = -1. - The isFull operation check if topIndex = maxStackSize - 1 Array based stack const int maxStackSize = 1000; typedef char StackElementType; class Stack { public: Stack(); void push(StackElementType item); StackElementType pop(); StackElementType top(); bool isEmpty(); private: StackElementType stackArray[maxStackSize]; int topIndex; }; Array based stack Stack::Stack() { topIndex = -1; } void Stack::push(StackElementType item) { ++topIndex; // ensure array bounds not exceeded assert(topIndex < maxStackSize); stackArray[topIndex] = item; } StackElementType Stack::pop() { // ensure array bounds not exceeded assert(topIndex >= 0); int returnIndex(topIndex); --topIndex; return stackArray[returnIndex]; } Array based stack StackElementType Stack::top() { // ensure array bounds not exceeded assert(topIndex >= 0); return stackArray[topIndex]; } bool Stack::isEmpty() { return bool(topIndex == -1); } Creating generic classes with templates - Generic classes can be defined by typedef and template. - In a templated class, the type stored by the class becomes a parameter to the class. - How to define a template class? template <class …(your type)…..> class …(class name)… { } Example: template <class stackElementType> class stack { } Creating generic classes with templates - How to define a template constructor? template < class …(your type)….. > className <(your type)> :: classname { } Example: template <class stackElementType> stack < stackElementType> :: stack { } Creating generic classes with templates - How to define a template function? template < class …(your type)….. > return type className <(your function_name (parameter list) type)> :: { … } Example: template <class stackElementType> void stack < stackElementType> :: push (stackElementType item) { … } Creating generic classes with templates - Template class defines a family of classes. - From template of stack class, we can define a stack of int, char, string, etc. These are instances of the stack template. - The definition is in the main function or when we define objects. - Example: stack < int > stack_Object; or stack<int> stack_Object - Compilation of template class create a problem with linker because it is impossible for compiler to create the object file form template class because it does not know how much space for the generic variable (as in typedef). Platform dependent. - All of stack C++ operations are implemented regularly. const int maxStackSize = 1000; template < class StackElementType > class Stack { public: Stack(); void push(StackElementType item); StackElementType pop(); StackElementType top(); bool isEmpty(); bool isFull(); private: StackElementType stackArray[maxStackSize]; int topIndex; }; Code template Code template template < class StackElementType > Stack < StackElementType > ::Stack() { topIndex = -1; } template < class StackElementType > void Stack < StackElementType> ::push(StackElementType item) { ++topIndex; // ensure array bounds not exceeded assert(topIndex < maxStackSize); stackArray[topIndex] = item; } Code template template < class StackElementType > StackElementType Stack < StackElementType > ::pop() { // ensure array bounds not exceeded assert(topIndex >= 0); int returnIndex(topIndex); --topIndex; return stackArray[returnIndex]; } template < class StackElementType > StackElementType Stack < StackElementType > ::top() { assert(topIndex >= 0); return stackArray[topIndex]; } Code template template < class StackElementType > bool Stack < StackElementType> ::isEmpty() { return bool(topIndex == -1); } template < class StackElementType > bool Stack < StackElementType > ::isFull() { return topIndex == maxStackSize - 1; } Implemented the stack: Dynamic linked list - Array based implementation of stack has the same linear array drawbacks. - Linked list based implementation can solve these problems. - In the linked list, the functions’ interfaces are the same as in regular array, but the private part is different. - The LIFO nature of stack makes all push and pop done at the head of the linked list, so one pointed is required. template < class StackElementType > class Stack { public: Stack(); void push(StackElementType e); StackElementType pop(); StackElementType top(); bool isEmpty(); private: struct Node; typedef Node * Link; struct Node { StackElementType data; Link next; }; Link head; }; Code linked list Code linked list template < class StackElementType> Stack < StackElementType > ::Stack() { head = 0; } template < class StackElementType > void Stack < StackElementType > ::push(StackElementType e) { Link addedNode = new Node; assert(addedNode); addedNode->data = e; addedNode->next = head; head = addedNode; } Code linked list template < class StackElementType > StackElementType Stack < StackElementType > ::pop() { assert(head); StackElementType result; Link delNode; delnode= head; result = head->data; head = head->next; delete delnode; return result; } Code linked list template < class StackElementType > StackElementType Stack < StackElementType > ::top() { assert(head); return head->data; } template < class StackElementType > bool Stack < StackElementType > ::isempty() { return head==0; } Example: Calculator - Mathematical expressions can be written using prefix, infix, and postfix (Reverse Polish Notation). - The infix form of writing arithmetic operation is ambiguous as in 3-4-5*3. - Solution is to use parentheses and precedence rules. 1. Parentheses first 2. Inside parentheses Multiply and division first then addition and subtraction 3. For a sequence of operators of equal precedence from left to right. - Reverse Polish Notation (RPN) is easier to implement because it has no Rules. - How to convert the infix form into postfix form? - The evaluation of postfix format can be done by stack data structure: Each time an operand is encountered, it is pushed into the stack; upon reaching an operator, the last two operands are popped off the stack, and the operation is applied. The top of the stack is the right hand operand and the second item is the left hand operand. Evaluation of an RPN expression using a stack expression: stack: 34-53*- 3 34-53*- 34-53*- 3 - 4 = -1 4 3 expression: stack: 34-53*- 5 -1 34-53*- 3 5 34-53*- 5*3= 15 -1 -1 expression: stack: 34-53*- -1 -15 = -16 Final result on top of stack Example: Calculator - To evaluate RPN expressions, we need a way to break the input string into operands and operators. - The input “3 4 – 5 3 * -” has (operand) 3, (operand) 4, (operator) -, (operand) 5, (operand) 3, (operator) *, (operator) -. - Each input is called a token. - We can have enum tokenType {operandToken, operatorToken, eolToken, eofToken, badToken} enum operatorType {none, add, subtract, multiply, divide} - The process of breaking up the string is called lexical analysis or tokenization. - These all tokenization operations can be collected in Token class. (Homework) class Token { Public: tokenType nextToken (); // get next token stream operatorType getOperator (); // return operator double getOperand (); // return operand Assignment Using a Stack, trace the evaluation of the following RPN expressions: 7 3 15 * + 12 10 5 / 12 3 - * + 111111+-*/+ Assignment Write a program that uses a stack to test for balanced bracket pairs. The input strings, all consisting of a single line less than 80 characters long, will include 4 types of brackets: {}, [], <>, () In order for an expression to be parenthesized properly, each lef bracket must be matched with a right bracket of the same type. For example, the expression: {A [B <C> <D> (E) F] (G)} is correct, but {A[B}] is not, because the ‘}’ after ‘B’ pairs with a ‘[‘, which is not allowed. Your program will read strings and determine whether or not they are properly parenthesized, until it gets a string starting with a period (‘.’) at which point it will terminate. You will need to instantiate a Stack of char. Thanks