Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Implementing Stacks Ellen Walker CPSC 201 Data Structures Hiram College Operations on a Stack • • • • Create an empty stack Is the stack empty? Add a new item to the stack. (Push) Remove the item that was most recently added (Pop) • Retrieve the item that was most recently added (Peek) Implementing a Stack • All items in the stack must be stored • Only the top item needs to be accessible for retrieval • Therefore a stack is a restricted form of list – Use the List interface – Use an array (as inside ArrayList) – Use linked nodes (as inside LinkedList) • Java does none of these - it uses a vector (growable array) – But, to understand stacks better, we will consider the 3 options above Implementing using a List //Complete code pp. 272-273 public class ListStack < E > { private List < E > theData; public Stack < E > { theData = new ArrayList < E>(); } //more methods here } ArrayList Implementation • Push adds to the list – Where? Why? • Pop removes an item – Which one? • Peek looks at the top item – Where is it? • ArrayList takes care of resizing the list, details of implementations Empty and Push Method Public boolean empty() { return theData.size() == 0; } public E push (E obj){ //add at end of ArrayList theData.add (obj); return (obj); } Peek public E peek() { if (empty()) throw new EmptyStackException(); //top is at the end of the array return theData.get(theData.size()-1); } Pop public E pop() { if (empty()) throw new EmptyStackException(); //top is at the end of the array //recall: remove returns the object removed return theData.remove(theData.size()-1); } And now more detail… • Pretend we don’t already have ArrayList or LinkedList, and had to implement all from scratch • Why? – Understand in detail how stacks work – See the differences and tradeoffs between array and linked stacks – Appreciate how much easier it is to implement a stack than a list (either type) Array Implementation • Push: insert element into the array (where?) • Peek: retrieve the most recently inserted element (where is it?) • Pop: remove the most recently inserted element (how)? • Static vs. dynamic array? Array Push, Pop • All stack elements go into the array (in order) • Newest element is last (highest index) in the array • Integer variable “top” indicates index of top • Push increments top • Pop decrements top – Popped elements stay in array until overwritten by new push Choices for top • Top points after the top element – Push: first copy, then increment – Top: A[top-1] – Pop(item): first decrement, then copy • If top points at the top element – Push: first increment, then copy – Top: A[top] – Pop(item): copy item, decrement Example: Top at • Empty stack (top = –1) A[0]= A[1]= A[2]= A[3]= A[4]= • Push ‘a’, Push ‘b’, Push ‘c’ (top=2) A[0]=‘a’ A[1]=‘b’ A[2]=‘c’ A[3]= A[4]= A[3]= A[4]= A[3]= A[4]= • Pop, Pop (top = 0) A[0]=‘a’ A[1]=‘b’ A[2]=‘c’ • Push ‘z’ (top = 1) A[0]=‘a’ A[1]=‘z’ A[2]=‘c’ Possible Errors • Pop from empty stack • Top of empty stack – Throw EmptyStackException • Push if stack is full (CAPACITY items) – not a problem if resize is allowed (as in ArrayList) – If not, a FullStackException could be thrown Class definition (data members) public class ArrayStack < E >{ // array for the stack E[ ] theData; //index of top item int topOfStack = -1; Constructor and Check for Empty //Initial capacity private static final int INITIAL_CAPACITY = 10; //Construct an empty stack public ArrayStack() { theData = (E[]) new Object[INITIAL_CAPACITY]; } //Empty? public boolean empty(){ return top == -1; } Push • //Push an item onto the stack public E push(E obj){ if (topOfStack == theData.length-1) { reallocate(); //or could throw an exception } //++top increments first, returns new value theData[++topOfStack] = obj; return obj } Pop • //Pop an item from a stack and return it public E Pop(){ if (empty()) throw new EmptyStackException(); else return theData[topOfStack--]; } Efficiency of stack • Push: constant time – Even though insert into array is not! (why?) • Pop: constant time – Even though delete from array is not! Node (Linked-List) Implementation • Push: insert element into the list (where?) • Peek: retrieve the most recently inserted element (where is it?) • Pop: remove the most recently inserted element (how)? • isEmpty: (how do you know the stack is empty?) • What special cases require exceptions? Linked implementation • Underlying data structure is a singly linked list • Push inserts at the beginning of the list – Exception: cannot allocate a new node • Pop removes from the beginning of the list – Exception: empty list (stack) • Empty stack when top is null Example (Linked Implementation) • Empty stack • Push ‘a’, Push ‘b’, Push ‘c’ c b • Pop, Pop a • Push ‘z’ z a a Class Definition Public class LinkedStack { private Node<E> topOfStackRef = null; … private static class Node<E>{ //same as for linked list } }; empty & peek • //Is the stack empty? Public boolean empty() { return (topOfStackRef == null); } • //Get item from top Public E peek() { if (empty()) throw new EmptyStackException(); return topOfStackRef.data; } Push • // push (add to beginning of list) public E push (E obj){ topOfStackRef = new Node<E>(obj, topOfStackRef); return obj; } Pop • //Pop and recover old top item Public E pop(){ if (empty()) throw new EmptyStackException(); else{ E result = topOfStackRef.data; topOfStackRef = topOfStackRef.next; return result; } } Nodes vs. Arrays • Array is simpler, requires extra space (unused capacity) • Node uses minimum amount of space • Push and pop are O(1) either way • We never need to access the middle of the list. • Note: replace ArrayList by LinkedList on slide 6 to get the list functionality without writing any new code. You must also replace add() by addFirst() in push, and remove() by removeFirst() in pop. Postfix Expressions • Also called RPN (reverse Polish notation) – Enter first operand, then second operand, then operator – (Operands can be recursive expressions) – Example • • • • 1+(2*3) becomes 1 2 3 * + First operand is 1 Second operand is (recursively) 2 3 * Operator is + Stacks Evaluate Postfix • Reading the data: If you see an operand (number), push it If you see an operator, pop the first two items off the stack perform the operation push the result back on the stack • Example: 1 2 3 * + – Push 1, push 2, push 3, pop 3, pop 2, push 6 – Pop 6, pop 1, push 7 Infix Expressions • Infix expressions are the kind we use in Java – 6*3+7 – 6+3*7 – (6+3) * 7 • Order of operation depends on precedence – Multiplication first, then addition – Parentheses override order – Otherwise, left to right Converting Infix to Postfix • Operands (numbers) stay in the same order • Operators go in after their operands are complete – 6*3+7 - 6 3 * 7 + – 6+3*7 637*+ – (6+3) * 7 6 3 + 7 * • How to decide when to insert the operator? Algorithm for Infix to Postfix • For each token – If it is an operand, insert it immediately in the result – If it is an operator – While stack is not empty AND this operator has lower precedence than top-of-stack operator – pop operator from stack into expression – push this operator • Pop remaining operators into expression Examples – 6*3+7 63*7+ – 6+3*7 637*+ What about parentheses? • When you see a left parenthesis, push it on the stack • When you see a right parenthesis, pop all operators back to the left paren • Why? – Parentheses delimit a new expression – Right paren acts like end of expression (pop all remaining operators at the end) Example – (6+3) * 7 63+7* A Search Problem • Given a sequence of flight segments, can you reach one city from the other? • If there is a flight from A to B, then A and B are adjacent (A->B) • If (A->B) and (B->C), we can reach C from A Recursive Solution (pseudocode) bool canReach(city A, city B){ If (A == B) return true; //trivial problem If (adjacent(A,B)) return true; //direct flight For (each city adjacent to A) if canReach(city, B) return true; Return false; } Stack-based solution (pseudocode) bool canReach(city A, city B){ If (A == B) return true; //trivial problem Else push A onto the stack While(stack is not empty and top != B) if the city has an unvisited neighbor mark the neighbor as visited push the neighbor on the stack else pop the stack; } If the stack is empty return false; Otherwise return true (and the path is on the stack!) } Comments on stack based search • No essential difference between recursive and stack algorithms • This algorithm is called “depth-first search” • Path that is found will not necessarily be the shortest • Order of visits doesn’t matter for correctness, but matters significantly for efficiency