Implementing Stacks & Queues

Preview:

DESCRIPTION

Implementing Stacks & Queues. Outline. ADT Stacks Basic operations Examples of use Implementations Array-based and linked list-based ADT Queues Basic operations Examples of use Implementations Array-based and linked list-based Stack Applications Balanced Symbol Checker - PowerPoint PPT Presentation

Citation preview

IKI 10100: Data Structures & Algorithms

Ruli Manurung(acknowledgments to Denny & Ade Azurat)

1

Fasilkom UI

Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Implementing Stacks & Queues

2Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

ADT Stacks Basic operations Examples of use Implementations

• Array-based and linked list-basedADT Queues Basic operations Examples of use Implementations

• Array-based and linked list-basedStack Applications Balanced Symbol Checker Postfix Machines

Summary

Outline

3Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Linear Data Structures

A collection of components that are arranged along one dimension, i.e in a straight line, or linearly.

Stack: a linear data structure where access is restricted to the most recently inserted item.

Queue: a linear data structure where access is restricted to the least recently inserted item.

Both of these abstract data types can be implemented at a lower level using a list: either an array or a linked list.

4Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Stack

The last item added is pushed (added) to the stack.

The last item added can be popped (removed) from the stack.

The last item added can be topped (accessed) from the stack.

These operations all take constant time: O(1).

A typical stack interface:

void push(Thing newThing);

void pop();

Thing top();

5Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Stack Implementation: Array

A stack can be implemented as an array A and an integer top that records the index of the top of the stack.

For an empty stack, set top to -1.

When push(X) is called, increment top, and write X to A[top].

When pop() is called, decrement top.

When top() is called, return A[top].

6Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Example

top(-1)

A top(0) A

B top(1)

Push A Push B

MyStack myStack = new MyStack();

myStack.push(A);

myStack.push(B);

7Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Array Doubling

When array-based stack is constructed, instantiate an array with a “default” size.

When the array underlying the stack is full (not the stack itself!), we can increase the array through array doubling.

Allocate a new array twice the size, and copy the old array to the first half of the new array:

Thing[] newA = new Thing[oldA.length*2];

for(int ii=0; ii<oldA.length; ii++) newA[ii] = oldA[ii];

oldA = newA;

8Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Running Time

Without array doubling, all stack operations take constant time – O(1).

With array doubling, push() may be O(N), but this happens quite rarely: array doubling due to data size N must be preceded by N/2 push() non-doubling calls. Effectively, still constant time Amortization.

9Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Stack Implementation: Array

public class MyArrayStack<T>{ private T[] array; private int topOfStack; private static final int DEFAULT_CAPACITY = 10;

public MyArrayStack() … public boolean isEmpty() … public void makeEmpty() … public T top() … public void pop() … public T topAndPop() … public void push(T x) …

private void doubleArray() …}

10Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Stack Implementation: Linked List

First item in list = top of stack (if empty: null)push(Thing x): Create a new node containing x Insert it as the first elementpop(): Delete first item (i.e. move “top” to the second

item)top(): Return the data of the first element

d c b a

topOfStack

11Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Stack Implementation: Linked List

public class MyLinkedListStack<T>{ private ListNode<T> topOfStack;

public MyLinkedListStack() … public boolean isEmpty() … public void makeEmpty() … public T top() … public void pop() … public T topAndPop() … public void push(T x) …}

12Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Queue

Last item added is enqueued (added) to the back.First item added is dequeued (removed) from the front.First item added can be accessed: getFront.These operations all take constant time – O(1).

A typical queue interface:void enqueue(Thing newThing);void dequeue();Thing getFront();

13Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Queue Implementation: Simple Idea

Store items in an array A

Maintain index: back

Front of queue = A[0]

Back of queue = A[back]

Enqueue is easy & fast: store at A[back], back++

Dequeue is inefficient: A[1] to A[back] needs to be shifted (and back--) O(N)

X

back

X Y

back

X Y Z

back

Y Z

back

enqueue(X) enqueue(Y) enqueue(Z) dequeue()

14Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Queue Implementation: Better Idea

Add another index: front, which records the front of the queue

Dequeue is now done by incrementing front

Both enqueue and dequeue are now O(1).

But what happens if enqueue and dequeue array.length-1 items?

X Y Z

back

Y Z

back

enqueue(X)enqueue(Y)enqueue(Z) dequeue()

front front

Z

back

dequeue()

front

15Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Queue Implementation: “Circular” Array

After the array.length-1-th item is enqueued, the underlying array is full, even though the queue is not logically, it should be (almost?) empty.

Solution: wraparound

Re-use cells at beginning of array that are ‘empty’ due to dequeue.

When either front or back is incremented and points “outside array” (≥array.length), reset to 0.

16Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Circular Example

Both front and back indexes “wraparound” the array. Think of the array as a circle…

P Q R

front back

P Q R S

frontback

P Q R S

front

T

back

Q R S

front

T

back

R S

front

T

back

S

front

T

back

T

front back

17Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Java Implementation

Fairly straightforward. Basically, maintain Front Back Number of items in queue

When is the underlying array really full?

How do we do array doubling?

18Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Queue Implementation: Array

public class MyArrayQueue<T>{ private T[] array; private int front,back,currentSize; private static final int DEFAULT_CAPACITY = 10;

public MyArrayQueue() … public boolean isEmpty() … public void makeEmpty() … public T getFront() … public void dequeue() … public T getFrontAndDequeue() … public void enqueue(T x) …

private void doubleQueue() … private int increment(int x) …}

19Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Queue Implementation: Linked List

Maintain 2 node references: front & back

An empty queue: front = back = null.

enqueue(Thing X): Create a new node N containing X If queue empty: front = back = N Else append N and update back

dequeue(): Delete first item (referenced by front)

getFront(): Return data of first element

a b c d

front back

20Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Queue Implementation: Linked List

public class MyLinkedListQueue<T>{ private ListNode<T> front; private ListNode<T> back;

public MyLinkedListQueue() … public boolean isEmpty() … public void makeEmpty() … public T getFront() … public void dequeue() … public T getFrontAndDequeue() … public void enqueue(T x) …}

21Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Stack Application: Balanced Symbols

Java uses pairs of symbols to denote blocks: ( and ) { and } [ and ]

An opening symbol must be matched with a closing symbol: { [ ( ) ] } is OK { ( [ ) ] } is NOT OK

A closing symbol, e.g. ‘)’, must match the most recently seen opening symbol, e.g. ‘(‘.

This can be done using a stack.

22Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Balanced Symbol Checker Algorithm

Make an empty stack

Read symbols: If symbol is an opening symbol, push onto stack. If symbol is a closing symbol:

• If stack is empty, report error!

• Else, (top and) pop stack. If popped symbol is not a matching opening symbol, report error!

At the end, if the stack is not empty, report error!

23Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Say we are given an arithmetic expression, and we are asked to evaluate it.Typically, we use infix expressions, of the form operand1 OPERATOR operand2Example: 1 + 2 4 * 3

To process infix expressions we need precedence and associativity: 1 + 2 * 3 10 - 4 - 3 2 ^ 3 ^ 3 1 - 2 - 4 ^ 5 * 3 * 6 / 7 ^ 2 ^ 2

We use brackets: (1-2)-((((4^5)*3)*6)/(7^(2^2)))

Stack Application: Simple Calculator

24Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Postfix Notation

Typically, we use postfix expressions, of the form operand1 operand2 OPERATOR

Example: 1 2 + 4 3 *

With postfix expressions, precedence is unambiguous: 1 - 2 - 4 ^ 5 * 3 * 6 / 7 ^ 2 ^ 2, or (1-2)-((((4^5)*3)*6)/(7^(2^2))), becomes 1 2 - 4 5 ^ 3 * 6 * 7 2 2 ^ ^ / -

25Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Postfix Machines

A postfix machine evaluates a postfix expression: If an operand is seen, push it onto stack If an operator is seen, pop appropriate number of

operands, evaluate operator, push result onto stack

When the complete postfix expression is evaluated, the stack should contain exactly one item: the result.

26Ruli Manurung (Fasilkom UI) IKI10100: Lecture 6th Mar 2007

Both versions, array and linked-list, run in O(1)

Linked-list implementation requires extra overhead due to next reference at each node

(Circular) array implementation of queues can be quite tricky

Array space doubling needs memory at least 3x size of actual data.

Summary