Implementing Stacks Ellen Walker CPSC 201 Data Structures

  • Slides: 39
Download presentation
Implementing Stacks Ellen Walker CPSC 201 Data Structures Hiram College

Implementing Stacks Ellen Walker CPSC 201 Data Structures Hiram College

Operations on a Stack • • Create an empty stack Is the stack empty?

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

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 Array. List) – Use linked nodes (as inside Linked. List) • 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 List. Stack <

Implementing using a List //Complete code pp. 272 -273 public class List. Stack < E > { private List < E > the. Data; public Stack < E > { the. Data = new Array. List < E>(); } //more methods here }

Array. List Implementation • Push adds to the list – Where? Why? • Pop

Array. List Implementation • Push adds to the list – Where? Why? • Pop removes an item – Which one? • Peek looks at the top item – Where is it? • Array. List takes care of resizing the list, details of implementations

Empty and Push Method Public boolean empty() { return the. Data. size() == 0;

Empty and Push Method Public boolean empty() { return the. Data. size() == 0; } public E push (E obj){ //add at end of Array. List the. Data. add (obj); return (obj); }

Peek public E peek() { if (empty()) throw new Empty. Stack. Exception(); //top is

Peek public E peek() { if (empty()) throw new Empty. Stack. Exception(); //top is at the end of the array return the. Data. get(the. Data. size()-1); }

Pop public E pop() { if (empty()) throw new Empty. Stack. Exception(); //top is

Pop public E pop() { if (empty()) throw new Empty. Stack. Exception(); //top is at the end of the array //recall: remove returns the object removed return the. Data. remove(the. Data. size()-1); }

And now more detail… • Pretend we don’t already have Array. List or Linked.

And now more detail… • Pretend we don’t already have Array. List or Linked. List, 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

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) •

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,

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]=

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]= • 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

Possible Errors • Pop from empty stack • Top of empty stack – Throw Empty. Stack. Exception • Push if stack is full (CAPACITY items) – not a problem if resize is allowed (as in Array. List) – If not, a Full. Stack. Exception could be thrown

Class definition (data members) public class Array. Stack < E >{ // array for

Class definition (data members) public class Array. Stack < E >{ // array for the stack E[ ] the. Data; //index of top item int top. Of. Stack = -1;

Constructor and Check for Empty //Initial capacity private static final int INITIAL_CAPACITY = 10;

Constructor and Check for Empty //Initial capacity private static final int INITIAL_CAPACITY = 10; //Construct an empty stack public Array. Stack() { the. Data = (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 (top.

Push • //Push an item onto the stack public E push(E obj){ if (top. Of. Stack == the. Data. length-1) { reallocate(); //or could throw an exception } //++top increments first, returns new value the. Data[++top. Of. Stack] = obj; return obj }

Pop • //Pop an item from a stack and return it public E Pop(){

Pop • //Pop an item from a stack and return it public E Pop(){ if (empty()) throw new Empty. Stack. Exception(); else return the. Data[top. Of. Stack--]; }

Efficiency of stack • Push: constant time – Even though insert into array is

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:

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)? • is. Empty: (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

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

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 Linked. Stack { private Node<E> top. Of. Stack. Ref =

Class Definition Public class Linked. Stack { private Node<E> top. Of. Stack. Ref = null; … private static class Node<E>{ //same as for linked list } };

empty & peek • //Is the stack empty? Public boolean empty() { return (top.

empty & peek • //Is the stack empty? Public boolean empty() { return (top. Of. Stack. Ref == null); } • //Get item from top Public E peek() { if (empty()) throw new Empty. Stack. Exception(); return top. Of. Stack. Ref. data; }

Push • // push (add to beginning of list) public E push (E obj){

Push • // push (add to beginning of list) public E push (E obj){ top. Of. Stack. Ref = new Node<E>(obj, top. Of. Stack. Ref); return obj; }

Pop • //Pop and recover old top item Public E pop(){ if (empty()) throw

Pop • //Pop and recover old top item Public E pop(){ if (empty()) throw new Empty. Stack. Exception(); else{ E result = top. Of. Stack. Ref. data; top. Of. Stack. Ref = top. Of. Stack. Ref. next; return result; } }

Nodes vs. Arrays • Array is simpler, requires extra space (unused capacity) • Node

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 Array. List by Linked. List on slide 6 to get the list functionality without writing any new code. You must also replace add() by add. First() in push, and remove() by remove. First() in pop.

Postfix Expressions • Also called RPN (reverse Polish notation) – Enter first operand, then

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

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 2, push 6 – Pop 6, pop 1, push 7

Infix Expressions • Infix expressions are the kind we use in Java – 6*3+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

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

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*+

Examples – 6*3+7 63*7+ – 6+3*7 637*+

What about parentheses? • When you see a left parenthesis, push it on the

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*

Example – (6+3) * 7 63+7*

A Search Problem • Given a sequence of flight segments, can you reach one

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 can. Reach(city A, city B){ If (A == B) return

Recursive Solution (pseudocode) bool can. Reach(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 can. Reach(city, B) return true; Return false; }

Stack-based solution (pseudocode) bool can. Reach(city A, city B){ If (A == B) return

Stack-based solution (pseudocode) bool can. Reach(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

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