Upload
destini-warters
View
232
Download
2
Embed Size (px)
Citation preview
Elementary Data Structures
CS 110: Data Structures and Algorithms
First Semester, 2010-2011
Learning Objectives
►Explain and implement Stacks and Queues
►Analyze implementations in terms of efficiency
Elementary Data Structures
►Stack►container of elements that are
inserted and removed last-in first-out (LIFO)
►Queue►container of elements that are
inserted and removed first-in first-out (FIFO)
Stack
►Last-in, First-out (LIFO) structure►Operations
► push: add element into the stack► pop: remove & return topmost element► top: return topmost element► isEmpty: check if the stack has no elements► size: return number of elements in the
stack►Sample uses
► "Back" button of a browser, "Undo" operation, function/method calls
Stack Interfacepublic interface Stack{ public int size(); public boolean isEmpty(); public void push( Object o ); public Object top() throws EmptyStackException; public Object pop() throws EmptyStackException;}
public class EmptyStackException extends RuntimeException{}
Array Implementationpublic class ArrayStack implements Stack{ private int top = -1; private Object S[]; ...}
x y z w
3top
. . .
Array Implementation Details
►An array of objects stores the elements►An integer field points to the topmost
element►Value of top is –1 when the stack is
empty►A constant indicates the size/capacity of
the array►Throw a StackFullException when a
push is attempted on a full array
ArrayStack Classpublic class ArrayStack implements Stack
{
public static final int CAPACITY = 1000;
private Object S[];
private int top;
public ArrayStack()
{
S = new Object[CAPACITY];
top = -1;
}
public boolean isEmpty()
{
return (top < 0);
} …
}
ArrayStack Class continuedpublic class ArrayStack implements Stack
{ …
public int size()
{
return (top + 1);
}
public void push(Object obj) throws FullStackException
{
if (size() == CAPACITY)
throw new FullStackException();
S[++top] = obj;
}
…
}
public class FullStackException extends RuntimeException{}
ArrayStack Class continuedpublic class ArrayStack implements Stack
{ …
public Object top() throws EmptyStackException
{
if (isEmpty())
throw new EmptyStackException();
return S[top];
}
public Object pop() throws EmptyStackException
{
if (isEmpty())
throw new EmptyStackException();
return S[top--];
} …
}
Garbage Collection
►After a pop() operation, array still contains reference to popped element►Succeeding push() operations will
override such references but it is not certain whether pushes will occur after the pops
►Better to set the reference to null so that the object is garbage-collected when no longer in use
Improved pop() Methodpublic class ArrayStack implements Stack{ …
public Object pop() throws EmptyStackException{
Object elem;if (isEmpty())
throw new EmptyStackException();elem = S[top];S[top--] = null; // dereference S[top] for
garbage collection.return elem;
} …}
Using the StackStack s1 = new ArrayStack();String temp;s1.push( "easy" );s1.push( "this" );temp = (String) s1.pop();System.out.print( temp );s1.push( "is" );s1.push( "class" );while ( !s1.isEmpty() ){
temp = (String) s1.pop();System.out.print( " "+ temp );
}System.out.println();
Cast object to String
OK because Strings are Objects
Stack of ints
Stack s2 = new ArrayStack();
s2.push( 5 );
s2.push( 2 );
s2.push( 3 );
int num = (Integer) s2.pop();
System.out.println( num );
Allowed in Java 1.5 because primitive type values are "auto-boxed"
Cast object to Integer type (not int)
Note: In previous Java versions, s2.push( new Integer( 2 ) ); num = ( (Integer) s2.pop() ).intValue();
Time Complexity Analysis
►push() : O(1)►pop() : O(1)►isEmpty() : O(1)►size() : O(1)►top() : O(1)
Array Implementation Alternative
►Make top variable point to next available array position instead of actual topmost element
►top = 0 when empty►top represents size
x
4
top
y z w
. . .
Problems with ArrayStack
►CAPACITY needs to be specified►Consequences►stack may fill up (when size() ==
MAX )►memory is wasted if actual stack
consumption is way below maximum►Need a more "dynamic"
implementation
Linked List Implementation
y
top
z w
null
A stack as a sequence of nodes
The Node Class
public class Node{ private Object element; private Node next; public Node( Object e, Node n ) { element = e; next = n; } public Object getElement() … public Node getNext() … public void setElement( Object newElem ) … public void setNext( Node newNext ) …}
y
Linked List Implementation
►Stack is represented by a Node reference (called top)►This reference is null when stack is
empty►Top refers to the top element only but
links in each node keep the elements together
►An integer field represents the number of elements in the stack
NodeStack Class
public class NodeStack implements Stack{
private Node top;private int size;public NodeStack(){
top = null;size = 0;
}public boolean isEmpty(){
return (top == null);} …
}
NodeStack Class continued
public class NodeStack implements Stack{ …
public int size(){
return size;}public void push( Object obj ){
Node v = new Node( obj, top );top = v;size++;
} …}
Push Operation
y
top
z w
null
3
size
Push Operation
y
top
z w
null
3
size
x
Create node
Push Operation
y
top
z w
null
4
size
x
Update top and size
NodeStack Class continuedpublic class NodeStack implements Stack{ …
public Object top() throws EmptyStackException{
if ( isEmpty() )throw new EmptyStackException();
return top.getElement();}public Object pop() throws EmptyStackException{
if ( isEmpty() )throw new EmptyStackException();
Object temp = top.getElement();top = top.getNext();size--;return temp;
} …}
Pop Operation
y
top
z w
null
4
size
x
Pop Operation
y
top
z w
null
4
size
x
Get top element
temp
Pop Operation
y
top
z w
null
3
size
x
Update top and size
temp
Pop Operation
y
top
z w
null
3
size
x
Node automatically
disposed
temp
x
Pop Operation
y
top
z w
null
3
size
Return element
Using the NodeStack
Stack s2 = new NodeStack();
s2.push( 5 );
s2.push( 2 );
s2.push( 3 );
int num = (Integer) s2.pop();
System.out.println( num );
Only this line is changed
Time Complexity Analysis
►push() : O(1)►pop() : O(1)►isEmpty() : O(1)►size() : O(1)►top() : O(1)
ArrayStack versus NodeStack
►NodeStack uses only the memory that it needs at any given time
►NodeStack has no size limit(just the system’s memory) – FullStackException not thrown
►ArrayStack’s implementation is simpler►Which implementation is more
efficient?
Managing Multiple Implementations
►Note that we now have two implementations of a Stack:public class ArrayStack implements Stack{ … }public class NodeStack implements Stack{ … }
►Consider what code needs to be changed if we shift between implementations► It would be preferable if the code that uses
the stack does not need to be updated
A StackFactory Class
► Use a separate class that produces Stack objects
public class StackFactory { public static Stack createStack() { return new ArrayStack(); // or return new NodeStack(); }}
► Advantage:► if you want to change your implementation, you just need
to change StackFactory► you don’t need to change all calls to new ArrayStack in all
your code!
Using a StackFactory
Stack s2 = StackFactory.createStack();
s2.push( 5 );s2.push( 2 );s2.push( 3 );int num = (Integer) s2.pop();System.out.println( num );
this line need not be changed even if the stack implementation changes
Queue
►First-in, First-out (FIFO) structure►Operations
► enqueue: insert element at rear► dequeue: remove & return front element► front: return front element► isEmpty: check if the queue has no elements► size: return number of elements in the
queue►Sample use
► handling requests and reservations
The Queue Interface
public interface Queue{ public int size(); public boolean isEmpty(); public void enqueue( Object o ); public Object front() throws EmptyQueueException; public Object dequeue() throws EmptyQueueException;}
public class EmptyQueueException extends RuntimeException{}
Array Implementation Possibilities
►On enqueue, place element in the next available slot; on dequeue, remove element at position 0 and move all other elements to the left►Dequeue takes O(n) time
►Have integer pointers to front and rear, increment rear on enqueue, increment front on dequeue, so that both operations are O(1)
Array Implementation of a Queue
►An Object array and two integers►front: index of first element in queue►rear: index of first FREE element in
queue4
rear
. . .
0
front
ArrayQueue
public class ArrayQueue implements Queue {public static final int CAPACITY = 1000;private Object s[];private int front, rear;public ArrayQueue() {
s = new Object[CAPACITY];front = rear = 0;
} ...}
isEmpty and Enqueue
public class ArrayQueue implements Queue { ... public boolean isEmpty() { return ( front == rear ); } public void enqueue( Object o ) throws
FullQueueException {
if ( rear == CAPACITY )throw new FullQueueException();
s[rear++] = o; } ...}
public class FullQueueException extends RuntimeException{}
Enqueue Operation
3
rear
. . .
0
front
Enqueue Operation
4
rear
. . .
0
front
Enqueued object
Dequeue
public class ArrayQueue implements Queue { ... public Object dequeue() throws EmptyQueueException {
if ( isEmpty() )throw new EmptyQueueException();
return s[front++]; } …}
Dequeue Operation
4
rear
. . .
1
front
Return this object
Dequeue Operation
4
rear
. . .
1
front
Remember to set reference in array to null
null
Dequeue with Garbage Collection
public class ArrayQueue implements Queue { ... public Object dequeue() throws EmptyQueueException
{if ( isEmpty() )
throw new EmptyQueueException();Object data = s[front];s[front] = null;front++;return data;
}}
Circular Array
►Suppose many enqueue operations followed by many dequeue operations
►Result: rear approaches CAPACITY but the queue is not really full
►Solution: Circular Array►allow rear (and front) to "wrap around"
the array (if rear = CAPACITY-1, incrementing rear means resetting it to 0)
Circular Array continued► When is the array full?
► Simple answer: when (rear == front)► Problem: this is the same condition as empty
► Solution: Reserve a slot► full: when ( (rear+1) % CAPACITY == front)
(one free slot left)► empty: when ( rear == front )
► Note: "wastes" a slot► alternative: have a boolean field called hasElements► full: when ( hasElements && (rear == front))► But not really better
► hasElements takes up extra space too► Also, need to take care of hasElements in enqueue and
dequeue
Revised Enqueuepublic class ArrayQueue implements Queue {
...public void enqueue( Object o ) throws FullQueueException{
if ((rear+1) % CAPACITY == front)throw new FullQueueException();
s[rear] = o;rear = (rear + 1) % CAPACITY;
} ...}
Revised Dequeue
public class ArrayQueue implements Queue { ...
public Object dequeue() throws EmptyQueueException {
if ( isEmpty() )throw new EmptyQueueException();
Object data = s[front];s[front] = null;front = (front + 1) % CAPACITY;return data;
} …}
Completing the ArrayQueue Class
public class ArrayQueue implements Queue { ...
public int size(){
return (CAPACITY + rear – front) % CAPACITY;}…public Object front() throws EmptyQueueException {
if ( isEmpty() )throw new EmptyQueueException();
return s[front];}
…}
Time Complexity Analysis
►enqueue() : O(1)►dequeue() : O(1)►isEmpty() : O(1)►size() : O(1)►front() : O(1)
Dynamic Implementation
►Queue is represented by a linked sequence of nodes
►Two node references refer to front and rear element, respectively
►Use a size field to monitor number of elements
public class NodeQueue implements Queue
{
private Node front;
private Node rear;
private int size;
}
Linked List Implementation
Enqueue
front
null
rear
Enqueue
front rear
null
null
Dequeue
front
null
rear
Return this object
NodeQueue Considerations
►Exercise: complete the NodeQueue class
►Note that the queue is empty when both front and rear are null
►Need to watch out for special cases►Enqueue from an empty queue►Dequeue from a single-element
queue
Exercise: Balanced Parenthesis
►Write a program which takes in a string then outputs whether (YES) or not (NO) the parentheses are balanced.
►Only consider (),[],{} and <>. Ignore everything else.
Input Output
((A)) YES
(<B[]>c) YES
{<[>]} NO
(() NO
()) NO
Exercise: Postfix Calculator
►Write a program which computes then prints the result of a postfix string or prints ERROR for an invalid string.
►Input numbers are single-digit for simplicity.
Input Output
12+ 3
32+4* 20
1+2 ERROR
90*2-- ERROR