Download Lecture 7

Document related concepts

B-tree wikipedia , lookup

Array data structure wikipedia , lookup

Transcript
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 Interface
public 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 Implementation
public class ArrayStack implements Stack
{
private int top = -1;
private Object S[];
...
}
top
3
...
x
y
z
w
Array Implementation Details
► An
array of objects stores the elements
► An integer field points to the topmost
element
► Value
►A
of top is –1 when the stack is empty
constant indicates the size/capacity of
the array
► Throw a StackFullException when a push
is attempted on a full array
ArrayStack Class
public 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 continued
public 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 continued
public 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() Method
public 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 Stack
Stack s1 = new ArrayStack();
OK because
String temp;
Strings are
s1.push( "easy" );
Objects
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
Stack of ints
Stack s2 = new ArrayStack();
Allowed in Java 1.5
s2.push( 5 );
because primitive type
values are "auto-boxed"
s2.push( 2 );
s2.push( 3 );
int num = (Integer) s2.pop();
System.out.println( num );
Note: In previous Java versions,
s2.push( new Integer( 2 ) );
num = ( (Integer) s2.pop() ).intValue();
Cast object to
Integer type
(not int)
Time Complexity Analysis
► push()
► pop()
:
:
► isEmpty() :
► size() :
► top() :
O(1)
O(1)
O(1)
O(1)
O(1)
Array Implementation Alternative
► Make
top variable point to next available
array position instead of actual topmost
element
► top = 0 when empty
top
► top represents size
4
...
x
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
top
null
y
z
w
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;
y
}
public Object getElement() …
public Node getNext() …
public void setElement( Object newElem ) …
public void setNext( Node newNext ) …
}
Linked List Implementation
► Stack
is represented by a Node reference
(called top)
► This
► Top
reference is null when stack is empty
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
top
size
3
null
y
z
w
Push Operation
top
size
3
null
x
Create node
y
z
w
Push Operation
top
size
4
null
x
Update top
and size
y
z
w
NodeStack Class continued
public 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
top
size
4
null
x
y
z
w
Pop Operation
top
size
4
null
temp
x
Get top
element
y
z
w
Pop Operation
top
size
3
null
temp
x
Update top
and size
y
z
w
Pop Operation
top
size
3
null
temp
x
y
Node automatically
disposed
z
w
Pop Operation
top
size
3
null
x
Return
element
y
z
w
Using the NodeStack
Stack s2 = new NodeStack();
s2.push( 5 );
Only this line is
s2.push( 2 );
changed
s2.push( 3 );
int num = (Integer) s2.pop();
System.out.println( num );
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 );
this line need not
s2.push( 2 );
be changed even
if the stack
s2.push( 3 );
implementation
changes
int num = (Integer) s2.pop();
System.out.println( num );
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
► Have
takes O(n) time
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 queue
front
rear
0
4
...
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
front
rear
0
3
...
Enqueue Operation
front
rear
0
4
...
Enqueued object
Dequeue
public class ArrayQueue implements Queue
{
...
public Object dequeue() throws EmptyQueueException
{
if ( isEmpty() )
throw new EmptyQueueException();
return s[front++];
}
…
}
Dequeue Operation
front
rear
1
4
...
Return this object
Dequeue Operation
front
rear
1
4
null
Remember to set
reference in array
to 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 Enqueue
public 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()
:
► dequeue() :
► isEmpty() :
► size() :
► front() :
O(1)
O(1)
O(1)
O(1)
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
Linked List Implementation
public class NodeQueue implements Queue
{
private Node front;
private Node rear;
private int size;
}
Enqueue
front
rear
null
Enqueue
front
rear
null
null
Dequeue
front
rear
null
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
Input
Output
everything else.
((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