340 likes | 455 Views
Stacks. The unorganized person’s data structure. Stack characteristics. Entries are ordered in terms of access -- both insertion and removal take place at same spot (top of stack) Specialized type of container class; defining characteristic is insertion/removal order
E N D
Stacks The unorganized person’s data structure stacks
Stack characteristics • Entries are ordered in terms of access -- both insertion and removal take place at same spot (top of stack) • Specialized type of container class; defining characteristic is insertion/removal order • LIFO = last in, first out; entries are removed in reverse order of insertion stacks
Stack operations • Push -- insert item on stack • Pop -- remove item from stack • Peek -- examine, but don’t remove item stacks
Stack operations • Important to know if stack is empty -- attempt to remove an item from an empty stack is an underflow error • Depending on implementation, may be necessary to check if stack is full -- attempt to add item to a full stack is an overflow error stacks
Implementation of Stack ADT • Stacks can be array based (static or dynamic) or linked list based • Invariant for static array implementation: • The number of items stored in the stack is found in member variable used • Items are stored in member variable data, a static array with the stack bottom at data[0] and the stack top at data[used - 1] stacks
Stack class -- static array version template <class Item> class Stack { public: enum {CAPACITY = 64}; Stack ( ); // default constructor Item pop ( ); // removes top Item Item peek ( ) const; // reveals top Item . . . stacks
Stack ADT continued void push (const Item & entry); // adds Item to stack size_t size ( ) const {return used;} bool is_empty ( ) const {return used == 0;} private: Item data[CAPACITY]; // the stack itself size_t used; // # of items stored in stack }; stacks
Stack function implementations: constructor // Postcondition: empty stack is created template <class Item> Stack<Item>::Stack( ) { used = 0; } stacks
Pop function // Precondition: stack is not empty // Postcondition: top item is removed template <class Item> Item Stack<Item>::pop ( ) { assert (!is_empty( )); used--; return data[used]; } stacks
Peek function // Precondition: stack is not empty // Postcondition: top item is revealed template <class Item> Item Stack<Item>::peek ( ) const { assert (!is_empty( )); return data[used - 1]; } stacks
Push function // Precondition: stack is not full // Postcondition: an item is inserted on stack template <class Item> void Stack<Item>::push(const Item& entry) { assert (size() < CAPACITY); data[used] = entry; used++; } stacks
Stack application examples • Compilers use stacks for a variety of purposes: • syntax analysis: matching brackets, parentheses, etc. • activation records: structures associated with functions, keeping track of local variables, return address, etc. stacks
Example application: balanced parentheses • Pseudocode algorithm: • scan string left to right • if ‘(‘ is encountered, push on stack • if ‘)’ is encountered, and stack is not empty, pop one ‘(‘ -- if stack is empty, expression is unbalanced • if stack is empty when entire string has been scanned and analyzed, expression is balanced stacks
A program to test for balanced parentheses int main( ) { String user_input; // uses String data type defined in ch. 4 - based on array cout << "Type a string with some parentheses and no white space:\n"; cin >> user_input; if (balanced_parentheses(user_input)) cout << "Those parentheses are balanced.\n"; else cout << "Those parentheses are not balanced.\n"; cout << "That ends this balancing act.\n"; return EXIT_SUCCESS; } stacks
balanced_parentheses function bool balanced_parentheses(const String& expression) // Library facilities used: assert.h, stack1.h, stdlib.h, mystring.h. { // Meaningful names for constants const char LEFT_PARENTHESIS = '('; const char RIGHT_PARENTHESIS = ')'; Stack<char> store; // Stack to store the left parentheses as they occur size_t i; // An index into the String char next; // The next character from the String char discard; // A char popped off the stack and thrown away bool failed = false; // Becomes true if a needed parenthesis is not found . . . stacks
balanced_parentheses continued for (i = 0; !failed && (i < expression.length( )); i++) { next = expression[i]; if (next == LEFT_PARENTHESIS) { if (store.size( ) < store.CAPACITY); store.push(next); } else if ((next == RIGHT_PARENTHESIS) && (!store.is_empty( ))) discard = store.pop( ); else if ((next == RIGHT_PARENTHESIS) && (store.is_empty( ))) failed = true; } return (store.is_empty( ) && !failed); } stacks
Stack ADT as linked list • Can make use of toolkit functions to simplify task • Stack can grow & shrink as needed to accommodate data -- no fixed size • Invariant: • stack items are stored in a linked list • member variable top is head pointer to list stacks
Class definition for new Stack template <class Item> class Stack { public: Stack( ) { top = NULL; } Stack(const Stack& source); ~Stack( ) { list_clear(top); } ... stacks
Stack definition continued void push(const Item& entry); Item pop( ); void operator =(const Stack& source); size_t size( ) const {return list_length(top);} bool is_empty( ) const {return top == NULL;} Item peek( ) const; private: Node<Item> *top; // Points to top of stack }; stacks
Push function template <class Item> void Stack<Item>::push(const Item& entry) { list_head_insert(top, entry); } stacks
Pop function template <class Item> Item Stack<Item>::pop( ) { assert(!is_empty( )); Item answer = top->data; list_head_remove(top); return answer; } stacks
Peek function template <class Item> Item Stack<Item>::peek( ) const { assert(!is_empty( )); return top->data; } stacks
Copy constructor template <class Item> Stack<Item>::Stack(const Stack<Item>& source) { list_copy(source.top, top); } stacks
Assignment operator template <class Item> void Stack<Item>::operator =(const Stack<Item>& source) { if (source.top== top) // Handle self-assignment return; list_clear(top); list_copy(source.top, top); } stacks
A stack-based calculator • Input to program is a fully-parenthesized expression -- examples: ((5.3 * 1.2) / 3.1) (4 - 3) • Two stacks are used -- one for operators, one for operands • Right parenthesis is signal to pop the stacks and evaluate the expression stacks
Algorithm for expression evaluation • Evaluate leftmost, innermost expression; continue evaluating, left to right • Read each part of expression • Push numbers on operand stack, operators on operator stack • When right parenthesis is encountered, pop the stacks, evaluate, and push result on operand stack stacks
Code for stack calculator int main( ) { double answer; cout << "Type a fully parenthesized arithmetic expression:" << endl; answer = read_and_evaluate(cin); cout << "That evaluates to " << answer << endl; return EXIT_SUCCESS; } stacks
Code for stack calculator double read_and_evaluate(istream& ins) { const char DECIMAL = '.'; const char RIGHT_PARENTHESIS = ')'; Stack<double> numbers; Stack<char> operations; double number; char symbol; ... stacks
Code for stack calculator while (!ins.eof( ) && ins.peek( ) != '\n') { if (isdigit(ins.peek( )) || (ins.peek( ) == DECIMAL)) { ins >> number; numbers.push(number); } … stacks
Code for stack calculator else if (strchr("+-*/", ins.peek( )) != NULL) { ins >> symbol; operations.push(symbol); } stacks
Code for stack calculator else if (ins.peek( ) == RIGHT_PARENTHESIS) { cin.ignore( ); evaluate_stack_tops(numbers, operations); } else cin.ignore( ); } // end of while loop return numbers.pop( ); } stacks
Code for stack calculator void evaluate_stack_tops(Stack<double>& numbers, Stack<char>& operations) { double operand1, operand2; operand2 = numbers.pop( ); operand1 = numbers.pop( ); ... stacks
Code for stack calculator switch (operations.pop( )) { case '+': numbers.push(operand1 + operand2); break; case '-': numbers.push(operand1 - operand2); break; ... stacks
Code for stack calculator case '*': numbers.push(operand1 * operand2); break; case '/': numbers.push(operand1 / operand2); break; } // end switch statement } // end function stacks