Lecture 6 Stacks 1 The Abstract Data Type

Lecture 6: Stacks 1

The Abstract Data Type • Specifications of an abstract data type for a particular problem – Can emerge during the design of the problem’s solution – Examples • read. And. Correct algorithm • display. Backward algorithm 2

Developing an ADT During the Design of a Solution • ADT stack operations – Create an empty stack – Destroy a stack – Determine whether a stack is empty – Add a new item to the stack – Remove the item that was added most recently – Retrieve the item that was added most recently 3

Developing an ADT During the Design of a Solution • A stack – Last-in, first-out (LIFO) property • The last item placed on the stack will be the first item removed – Analogy • A stack of dishes in a cafeteria Figure 6 -1 Stack of cafeteria dishes 4

Refining the Definition of the ADT Stack • Operation Contract for the ADT Stack is. Empty(): boolean {query} push(in new. Item: Stack. Item. Type) throw Stack. Exception pop(out stack. Top: Stack. Item. Type) throw Stack. Exception get. Top(out stack. Top: Stack. Item. Type) {query} throw Stack. Exception 5

Implementations of the ADT Stack • The ADT stack can be implemented using – An array – A linked list – The ADT list • All three implementations use a Stack. Exception class to handle possible exceptions 6

An Array-Based Implementation of the ADT Stack • Private data fields – An array of items of type Stack. Item. Type – The index top to the top item • Compiler-generated destructor and copy constructor Figure 6 -5 An array-based implementation 7

• const int MAX_STACK = 20; • typedef int Stack. Item. Type; • • • • class Stack { public: Stack(); // default constructor bool is. Empty() const; void push(Stack. Item. Type new. Item) ; void pop(Stack. Item. Type& stack. Top); void get. Top(Stack. Item. Type& stack. Top) const; private: Stack. Item. Type items[MAX_STACK]; // array of stack items int top; // index to top of stack }; // end class

• Stack: : Stack(): top(-1) • { • }; // end default constructor • bool Stack: : is. Empty() const • { • return top < 0; • }; // end is. Empty

• void Stack: : push(Stack. Item. Type new. Item) throw(Stack. Exception) • {// if stack has no more room for another item • if (top >= MAX_STACK-1) • throw Stack. Exception("Stack. Exception: stack full on push"); • else • { ++top; • items[top] = new. Item; • } // end if • }; // end push • void Stack: : pop() throw(Stack. Exception) • { • if (is. Empty()) • throw Stack. Exception("Stack. Exception: stack empty on pop"); • else • --top; // stack is not empty; pop top • }; // end pop 10

• • • void Stack: : pop(Stack. Item. Type& stack. Top) throw(Stack. Exception) { if (is. Empty()) throw Stack. Exception("Stack. Exception: stack empty on pop"); else { // stack is not empty; retrieve top stack. Top = items[top]; --top; // pop top } // end if }; // end pop • • void Stack: : get. Top(Stack. Item. Type& stack. Top) const throw(Stack. Exception) { if (is. Empty()) throw Stack. Exception("Stack. Exception: stack empty on get. Top"); else // stack is not empty; retrieve top stack. Top = items[top]; }; // end get. Top 11

• • • • int main() { Stack. Item. Type an. Item; Stack a. Stack; for (int i = 0; i< 10; i++) {an. Item = i*10; a. Stack. push(an. Item); // push it onto stack } while (!a. Stack. is. Empty()) { a. Stack. get. Top(an. Item); cout <<an. Item<<endl; a. Stack. pop(); } return 0; }//ex 6 -1. cpp 12

Implementations of the ADT Stack Figure 6 -4 Implementations of the ADT stack that use (a) an array; (b) a linked list; (c) an ADT list 13

A Pointer-Based Implementation of the ADT Stack • A pointer-based implementation – Enables the stack to grow and shrink dynamically • top. Ptr is a pointer to the head of a linked list of items • A copy constructor and destructor must be supplied Figure 6 -6 A pointer-based implementation 14

Pointer-based Stack • • • • class Stack {public: //… Stack(const Stack& a. Stack); //… ~Stack(); //Destructor; private: struct Stack. Node // a node on the stack { Stack. Item. Type item; // a data item on the stack Stack. Node *next; // pointer to next node }; // end struct Stack. Node *top. Ptr; // pointer to first node in the stack }; // end class 15

• • • • • Stack: : Stack() : top. Ptr(NULL) {} // end default constructor Stack: : Stack(const Stack& a. Stack) { if (a. Stack. top. Ptr == NULL) top. Ptr = NULL; // original list is empty else { // copy first node top. Ptr = new Stack. Node; top. Ptr->item = a. Stack. top. Ptr->item; // copy rest of list Stack. Node *new. Ptr = top. Ptr; // new list pointer for (Stack. Node *orig. Ptr = a. Stack. top. Ptr->next; orig. Ptr != NULL; orig. Ptr = orig. Ptr->next) { new. Ptr->next = new Stack. Node; new. Ptr = new. Ptr->next; new. Ptr->item = orig. Ptr->item; } // end for new. Ptr->next = NULL; } // end if } // end copy constructor 16

• Stack: : ~Stack() • {while (!is. Empty()) pop(); } // Assertion: top. Ptr == NULL • bool Stack: : is. Empty() const • { return top. Ptr == NULL; } // end is. Empty • void Stack: : push(Stack. Item. Type new. Item) • { // create a new node • Stack. Node *new. Ptr = new Stack. Node; • // set data portion of new node • new. Ptr->item = new. Item; • // insert the new node • new. Ptr->next = top. Ptr; • top. Ptr = new. Ptr; • } // end push 17

• void Stack: : pop() throw(Stack. Exception) • { • if (is. Empty()) • throw Stack. Exception("Stack. Exception: stack empty on pop"); • else • { // stack is not empty; delete top • Stack. Node *temp = top. Ptr; • top. Ptr = top. Ptr->next; • // return deleted node to system • temp->next = NULL; // safeguard • delete temp; • } // end if • } // end pop 18

• • • • • void Stack: : pop(Stack. Item. Type& stack. Top) throw(Stack. Exception) {if (is. Empty()) throw Stack. Exception("Stack. Exception: stack empty on pop"); else { // stack is not empty; retrieve and delete top stack. Top = top. Ptr->item; Stack. Node *temp = top. Ptr; top. Ptr = top. Ptr->next; temp->next = NULL; // safeguard delete temp; // return deleted node to system } // end if } // end pop void Stack: : get. Top(Stack. Item. Type& stack. Top) const throw(Stack. Exception) { if (is. Empty()) throw Stack. Exception("Stack. Exception: stack empty on get. Top"); else // stack is not empty; retrieve top stack. Top = top. Ptr->item; } // end get. Top //ex 6 -2. cpp 19

An Implementation That Uses the ADT List • The ADT list can represent the items in a stack • Let the item in position 1 of the list be the top – push(new. Item) • insert(1, new. Item) – pop() • remove(1) – get. Top(stack. Top) • retrieve(1, stack. Top) 20

An Implementation That Uses the ADT List Figure 6 -7 An implementation that uses the ADT list 21

Comparing Implementations • Fixed size versus dynamic size – A statically allocated array-based implementation • Fixed-size stack that can get full • Prevents the push operation from adding an item to the stack, if the array is full – A dynamically allocated array-based implementation or a pointer-based implementation • No size restriction on the stack 22

Comparing Implementations • A pointer-based implementation vs. one that uses a pointer-based implementation of the ADT list – Pointer-based implementation is more efficient – ADT list approach reuses an already implemented class • Much simpler to write • Saves programming time 23

List Based Stack • • • Stack {public: //… private: List a. List; // list of stack items }; // others are the same as the Pointer-based 24

• Stack: : Stack() • { } // end default constructor • Stack: : Stack(const Stack& a. Stack): a. List(a. Stack. a. List) • { } // end copy constructor • Stack: : ~Stack() • { } // end destructor • bool Stack: : is. Empty() const • { • return a. List. is. Empty(); • } // end is. Empty 25

• void Stack: : push(Stack. Item. Type new. Item) throw(Stack. Exception) • { try { • a. List. insert(1, new. Item); • } // end try • catch (List. Exception e) • { throw Stack. Exception("Stack. Exception: cannot push item"); • } // end catch • } // end push • void Stack: : pop() throw(Stack. Exception) • { try { • a. List. remove(1); • } // end try • catch (List. Index. Out. Of. Range. Exception e) • { throw Stack. Exception("Stack. Exception: stack empty on pop"); • } // end catch • } // end pop 26

• • • void Stack: : pop(Stack. Item. Type& stack. Top) throw(Stack. Exception) { try { a. List. retrieve(1, stack. Top); a. List. remove(1); } // end try catch (List. Index. Out. Of. Range. Exception e) { throw Stack. Exception("Stack. Exception: stack empty on pop"); } // end catch } // end pop • • • void Stack: : get. Top(Stack. Item. Type& stack. Top) const throw(Stack. Exception) { try { a. List. retrieve(1, stack. Top); } // end try catch (List. Index. Out. Of. Range. Exception e) { throw Stack. Exception("Stack. Exception: stack empty on get. Top"); } // end catch } // end get. Top //ex 6 -3. cpp 27

The STL Class stack • Provides a size function • Has two data type parameters – T, the data type for the stack items – Container, the container class that the STL uses in its implementation of the stack • Uses the keyword explicit in the constructor’s declaration to prevent use of the assignment operator to invoke the constructor 28

• • • • • #include <iostream> #include <stack> using namespace std; int main() { stack<int> a. Stack; int item; // Right now, the stack is empty if (a. Stack. empty()) cout << "The stack is empty. " << endl; for (int j = 0; j < 5; j++) a. Stack. push(j); // places items on top of stack while (!a. Stack. empty()) { cout << a. Stack. top() << endl; a. Stack. pop(); } // end while return 0; } // end main //ex 6 -4. cpp 29

Using the ADT Stack in a Solution • A program can use a stack independently of the stack’s implementation – display. Backward algorithm can be refined using stack operations • Use axioms to define an ADT stack formally – Example: Specify that the last item inserted is the first item to be removed (a. Stack. push(new. Item)). pop()= a. Stack 30

Checking for Balanced Braces • A stack can be used to verify whether a program contains balanced braces – An example of balanced braces abc{defg{ijk}{l{mn}}op}qr – An example of unbalanced braces abc{def}}{ghij{kl}m 31

Checking for Balanced Braces • Requirements for balanced braces – Each time you encounter a “}”, it matches an already encountered “{” – When you reach the end of the string, you have matched each “{” 32

Checking for Balanced Braces Figure 6 -3 Traces of the algorithm that checks for balanced braces 33

34 bool balanced (string str) • { int index=0; • Stack S; • while (str [index]!= '