Anda di halaman 1dari 28

Chapter 4

Abstract Data Types

4.1 Introduction
In this chapter we will start to look at some of the standard data types of computer science.
These types are important in almost all computer languages. We will look at how these
types are provided by the Java library and at how we can implement them ourselves.
The first data types that we look at will all be used to hold collections of data. For
example we may want to use a list of names or a set of numbers.
The Java library provides data types for collections as a number of classes and interfaces
in the java.util package. This is called the collections framework.
The term abstract data type means that we will separate the specification of the data
type from its implementation. The specification includes a description of what the type
does and the methods it provides to do it. The implementation provides the code that
actually does the work.
For most ADTs there are more than one implementation provided. Different implemen-
tations may be more efficient in different circumstances, for example some may use less
memory or be faster at certain operations. Because the specification and implementation
are separate it should be possible for a user of an ADT to change implementation with
very little change to their code.
Java provides two ways of specifying a method independently from the implementation,
interfaces and abstract classes. The collections framework is based on interfaces, but a few
abstract classes are provided for convenience.

4.1.1 Some common ADTs


Collection (Also called a bag or multiset) Just a collection of items. Methods can be
provided to add or remove items and to test if items are in the collection.

List A numbered collection of items. Items can be added or removed at any place in the
list.

1
CHAPTER 4. ABSTRACT DATA TYPES 2

Set A collection of items that are all different.

Stack A last in first out list (LIFO). Items can only be added or removed from the top.

Queue A first in first out list (FIFO). Items can be added at one end and removed at the
other.

4.2 Lists
4.2.1 Introdution to Lists
We will first look at the ADT List in detail.
A list is used to hold a numbered collection of items. For example here is a list of four
Strings

0 first
1 second
2 third
3 last

The strings have a position or index. In Java we number from zero, so first has position
0 and third has position 2.
A typical list has methods to add or remove items from any position. When an item is
removed the items below must move up. When one is added they must move down. There
would also be methods to get an item from any position without removing it, and to set a
value at a position. There would be a method to get the size of the list.
Lists come in many versions. For an immutable list we would be able to get the value
from any position but not to change the list. Other lists may allow changing of the values
but not changing the size of the list.

4.2.2 The List interface


We will start to study ADTs by looking in detail at the Java library List interface.
The List interface is slighly more complicated than the usual ADT List. Firstly it
extends the Collection interface, this means that all the methods that apply to Collections
also apply to lists. Secondly in addition to the basic operations for lists there are a number
of others that are useful additions, but not normally part of the List ADT.
From Java 1.5 lists are parameterised types. This make the description of them more
complicated, but makes them much easier to use.
The parameter is usually called E for element. Remember that when we declare and
use a list we replace all the Es by some type. For instance if we want a list of Strings we
declare List<String>. We then use the String type everywhere E is mentioned.
The Collection<?> types mean that collections of any type can be used.
CHAPTER 4. ABSTRACT DATA TYPES 3

p ub li c i n t e r f a c e List <E > extends Collection <E > {


// From Collection
i n t size ();
boolean isEmpty ();
boolean contains ( Object o );
Iterator <E > iterator ();
Object [] toArray ();
<T > T [] toArray ( T [] a );
boolean add ( Object o );
boolean remove ( Object o );
boolean containsAll ( Collection <? > c );
boolean addAll ( Collection c );
boolean addAll ( i n t index , Collection c );
boolean removeAll ( Collection <? > c );
boolean retainAll ( Collection <? > c );
void clear ();
boolean equals ( Object o );
i n t hashCode ();
// List only
E get ( i n t index );
E set ( i n t index , E element );
void add ( i n t index , E element );
E remove ( i n t index );
i n t indexOf ( Object o );
i n t lastIndexOf ( Object o );
ListIterator <E > listIterator ();
ListIterator <E > listIterator ( i n t index );
List <E > subList ( i n t fromIndex , i n t toIndex );
}
The basic list operations are:

int size() Returns the number of elements in the list.

E get(int index) Returns the element at the given index.

E set(int index, E element) Sets the element at the given index, returns the old value.

void add(int index, E element) Adds a new element at the given index, moves the old
element and any later elements down one.

E remove(int index) Removes the element at the given index and moves any lower ele-
ments up one. Returns the element it removes.
CHAPTER 4. ABSTRACT DATA TYPES 4

All these methods should throw an exception if the index given is out of range. The
range is 0<=index<size() except for add where it is 0<=index<=size() as you are allowed
to add at the end.
The mutator methods set, add, remove would not be used for an immutable list. As
the Java library List interface requires them these methods need to be provided. They
should do nothing except throw a UnsupportedOperationException.
A list is allowed to reject add or set methods if the types of the elements are unsuitable.
If your List represents a list of Strings you can make it reject other types. It should throw
a ClassCastException or a NullPointerException.
Most of the other methods in the Java List iterface could be written using these ba-
sic methods, however it is often possible to write more efficent methods for a particular
implementation.
Most lists would also have:
boolean add(E o) Adds an E to the end of the list. A list version would normally be a
void method. The Java library version returns a boolean to indicate if anything is
added. This is always true for lists, it can be false for sets if the element is already
there.
boolean isEmpty() returns true if the list is empty.
void clear() Remove all the elements.
boolean contains(Object o) Returns true if the list contains this object. (That is if
there is an entry element in the list with element.equals(o) true.

4.2.3 Iterators
We often want to do an operation for every element of the list in order. We can do this
with a for loop, for example if list is a list of strings we can use:

f o r ( i n t i =0; i < list . size (); i ++)


System . out . println (" Element number "+ i +" is "+ list . get ( i ));
This can be slow for some implementations of List. It also cannot be used for collections
as they do not have a get(i) method.
An alternative is to get an iterator for the list. This is an object that extends the
Iterator<E> interface. Think of it as data that remembers where you are in a list. The
interface has methods
boolean hasNext() Returns true if there are more elements in the list to process.
E next() Returns the next element from the list.
remove() Removes the element that was last returned by next(). Throws
UnsupportedOperationException for immutable lists.
CHAPTER 4. ABSTRACT DATA TYPES 5

You should not modify a list while using an iterator except by using its remove()
method.
Our previous example would become:
f o r ( Iterator < String > it = list . iterator (); it . hasNext ();)
System . out . println (" Next element is "+ it . next ());
In Java 1.5 the extended for loop has a special case to cover this common code.

f o r ( String s : list )
System . out . println (" Next element is "+ s );
There is an extended version of an iterator for lists called ListIterator<E> that can
go forwards and backwards and make more modifications to the list.

Constructors
A list class without constructors is not much use to us. Java interfaces cannot include
constructors, so they are not part of the interface. The documentation indicates that a list
class should have at least two constructors

ListClass<E>() A default constructor to create an empty list.

ListClass<E>(Collection<E> c) A class to construct a list containing the elements


in the collection in the order provided by its iterator.

Lists of primitive types


The Java library only provides an interface for lists of Objects. If you want to use lists of
doubles or other primitive types there are two ways to get them.

1. Write your own list interface and implementation.

2. Wrap your primitive type in a wrapper class.

The Java 1.5 autoboxing and unboxing feature makes it easy to use wrapper classes.
List < Integer > list =new ArrayList < Integer >();
list . add (new Integer (27));
list . add (27); // Same as above
i n t m = list . get (0). intValue ();
i n t n = list . get (0); // Same as above
CHAPTER 4. ABSTRACT DATA TYPES 6

4.2.4 List implementation using an array


The obvious way to implement a list is with an array of objects. The Java Library class
ArrayList does this, but we will examine a simpler version to see how it works. To avoid
having to write all the additional methods that the Java List interface provides we can
extend the abstract library class AbstractList<E>. Remember an abstract class is some-
where between a class and an interface. Some of the methods are provided and other
methods are marked as abstract and only have method headers. If we extend an abstract
class we have to provide code for all the abstract methods. We can also override other
methods if we want to.
The AbstractList<E> class has abstract methods for the methods E get(int index)
and int size(). If we want an immutable list we only need to provide these methods, the
others are then provided using these.
For a mutable list we also need to override the methods

E set(int index, E element)

void add(int index, E element)

E remove(int index)

We will also override a few more of the methods to show how they work.
package com328 . structure ;
import java . util . AbstractList ;
import java . util . Collection ;
import java . util . Iterator ;

/* * MyArrayList
* @author C.T. Stretch
*/
p ub li c c l a s s MyArrayList <E > extends AbstractList <E >
{
f i n a l s t a t i c i n t FIRST_SIZE = 16;
p r i v a t e E [] theArray ;
p r i v a t e i n t size ;

/* * Create a list with no elements


* of initial length FIRST_SIZE
*/
@SuppressWarnings (" unchecked ")
p ub li c MyArrayList ()
{
size = 0;
theArray = ( E [])(new Object [ FIRST_SIZE ]);
CHAPTER 4. ABSTRACT DATA TYPES 7

/* * Create a list containing the given collection .


* @param c The collection .
*/
@SuppressWarnings (" unchecked ")
p ub li c MyArrayList ( Collection <? extends E > c )
{
size = c . size ();
theArray =( E [])(new Object [ size ]);
Iterator <? extends E > it = c . iterator ();
i n t i =0;
while ( it . hasNext ())
{
theArray [ i ++]= it . next ();
}
}

@SuppressWarnings (" unchecked ")


p ub li c void add ( i n t index , E element )
{
i f ( index < 0 || index > size )
throw new IndexOutOfBoundsException ("" + index );
i f ( size == theArray . length )
{
i n t newSize = theArray . length *2;
i f ( newSize < FIRST_SIZE ) newSize = FIRST_SIZE ;
E [] newArray = ( E [])new Object [ newSize ];
System . arraycopy ( theArray , 0 , newArray , 0 , theArray . length );
theArray = newArray ;
}
f o r ( i n t i = size ; i > index ; i - -)
{
theArray [ i ] = theArray [ i - 1];
}
theArray [ index ] = element ;
size ++;
}

p ub li c E remove ( i n t index )
{
i f ( index < 0 || index >= size )
throw new IndexOutOfBoundsException ("" + index );
CHAPTER 4. ABSTRACT DATA TYPES 8

E r = theArray [ index ];
f o r ( i n t i = index + 1; i < size ; i ++)
theArray [ i - 1] = theArray [ i ];
size - -;
return r ;
}

p ub li c E set ( i n t index , E element )


{
i f ( index < 0 || index >= size )
throw new IndexOutOfBoundsException ("" + index );
E r = theArray [ index ];
theArray [ index ] = element ;
return r ;
}

p ub li c E get ( i n t index )
{
i f ( index < 0 || index >= size )
throw new IndexOutOfBoundsException ("" + index );
return theArray [ index ];
}

p ub li c i n t size ()
{
return size ;
}

p ub li c boolean isEmpty ()
{
return size ==0;
}

@SuppressWarnings (" unchecked ")


p ub li c void clear ()
{
size =0;
i f ( theArray . length > FIRST_SIZE )
theArray =( E [])new Object [ FIRST_SIZE ];
}

p ub li c boolean contains ( Object obj )


CHAPTER 4. ABSTRACT DATA TYPES 9

{
f o r ( i n t i =0; i < size ; i ++)
{ Object x = theArray [ i ];
i f ( x != n u l l && x . equals ( obj )) return true ;
}
return f a l s e ;
}

p ub li c Iterator <E > iterator ()


{
return new MyIterator ();
}

/* * A string containg the size of the list


* and the length of the array .
* @return a description of the list .
*/
p ub li c String toString ()
{
return " MyArrayList size ="+ size +" length ="+ theArray . length ;
}

/* * An inner class to represent an iterator


* for a MyArrayList
*/
c l a s s MyIterator implements Iterator <E >
{
i n t pos =0;
boolean canRemove = f a l s e ;

p ub l i c void remove ()
{
i f ( canRemove )
{
MyArrayList . t h i s . remove ( - - pos );
canRemove = f a l s e ;
}
else
throw new IllegalStateException ();
}

p ub l i c boolean hasNext ()
{
CHAPTER 4. ABSTRACT DATA TYPES 10

return pos < size ;


}

p ub l i c E next ()
{
canRemove = true ;
return theArray [ pos ++];
}
}
}

4.2.5 Linked lists


We will look at a completely different way of implementing the List interface using linked
lists.
A linked list looks like figure 4.1.

start - next - next - next

data data data

Figure 4.1: A linked list

We can write a linked list using a class Node with two fields:
c l a s s Node <E >
{
Node <E > next ;
E data ;
}
The next field holds a reference to the next node in the chain, the data field holds the
data for that node. The next field of the last node is usually null.
A linked list implementation has advantages and disadvantages over an array imple-
mentation.
Advantages are that we never need to copy the data when we increase the size of the
list, we just add another node. Similarly when we add or remove a node we do not need
to shift the data up or down, we just link in an extra node or unlink a node.
Disadvantages are that to find a node with a given index we need to count through the
list until we get to that index. The linked list also uses more memory than a full array list.
CHAPTER 4. ABSTRACT DATA TYPES 11

Adding and removing


Adding something to the middle of a linked list involves finding the previous node to the
one we want to replace.. A new node is created and its next field is then set to that of the
previous node. The next field of the previous node is then set to the new node.

start - next - next - next


B
B 

data BB  data data
B next 
BN 

data

Figure 4.2: Adding a node

Adding a new node at the start is slightly different. In this case there is no previous
node. We have to link our node using the start variable instead.
To remove a node from the middle we again need to locate the previous node. We
change the next field of the previous node to equal the next field of the node to remove.

@
@
start - next - next - next
@
R
@

data data data

Figure 4.3: Removing a node

Again the first node is a special case where we change the start variable.
Here is the code of a basic linked list implementation. We extend AbstractList to
provide the methods that are not implemented here.
package com328 . structure ;
import java . util . AbstractList ;
import java . util . Collection ;
import java . util . Iterator ;

/* * MyLinkedList
* @author C.T. Stretch
CHAPTER 4. ABSTRACT DATA TYPES 12

*/
p ub li c c l a s s MyLinkedList <E > extends AbstractList <E >
{
i n t size ;
Node <E > start ;

/* * Create a list with no elements


*/
p ub li c MyLinkedList ()
{
}

/* * Create a list containing the given collection .


* @param c The collection .
*/
p ub li c MyLinkedList ( Collection <? extends E > c )
{
Iterator <? extends E > it = c . iterator ();
size = c . size ();
i f ( it . hasNext ())
{
start = new Node <E >( it . next ());
Node <E > n = start ;
while ( it . hasNext ())
{
n . next = new Node <E >( it . next ());
n = n . next ;
}
}
}

p ub li c void add ( i n t index , E element )


{
i f ( index == 0)
{
addFirst ( element );
return ;
}
i f ( index < 0 || index > size )
throw new IndexOutOfBoundsException ("" + index );
Node <E > p = start ;
f o r ( i n t i = 0; i < index - 1; i ++)
p = p . next ;
CHAPTER 4. ABSTRACT DATA TYPES 13

p . next = new Node <E >( p . next , element );


size ++;
}

p ub li c void addFirst ( E element )


{
start = new Node <E >( start , element );
size ++;
}

p ub li c E remove ( i n t index )
{
i f ( index == 0)
return removeFirst ();
i f ( index < 0 || index >= size )
throw new IndexOutOfBoundsException ("" + index );
Node <E > p = start ;
f o r ( i n t i = 0; i < index - 1; i ++)
p = p . next ;
E r = p . next . data ;
p . next = p . next . next ;
size - -;
return r ;
}

p ub li c E removeFirst ()
{
i f ( size == 0)
throw new IndexOutOfBoundsException ("0" );
E r = start . data ;
start = start . next ;
size - -;
return r ;
}

p ub li c E set ( i n t index , E element )


{
i f ( index < 0 || index >= size )
throw new IndexOutOfBoundsException ("" + index );
Node <E > t = start ;
f o r ( i n t i = 0; i < index ; i ++)
t = t . next ;
E r = t . data ;
CHAPTER 4. ABSTRACT DATA TYPES 14

t . data = element ;
return r ;
}

p ub li c E get ( i n t index )
{
i f ( index < 0 || index >= size )
throw new IndexOutOfBoundsException ("" + index );
Node <E > t = start ;
f o r ( i n t i = 0; i < index ; i ++)
t = t . next ;
return t . data ;
}

p ub li c i n t size ()
{
return size ;
}

p ub li c boolean isEmpty ()
{
return size == 0;
}

p ub li c void clear ()
{
size = 0;
start = n u l l ;
}

p ub li c boolean contains ( Object obj )


{
f o r ( Node n = start ; n != n u l l ; n = n . next )
{ i f ( n . data . equals ( obj )) return true ;
}
return f a l s e ;
}

p ub li c Iterator <E > iterator ()


{
return new MyIterator ();
}
CHAPTER 4. ABSTRACT DATA TYPES 15

/* * A string containg the size of the list


* and the length of the array .
* @return a description of the list .
*/
p ub li c String toString ()
{
return " MyLinkedList size =" + size ;
}

/* * An inner class to represent a node


* of the linked list .
*/
p r i v a t e s t a t i c c l a s s Node <E >
{
Node <E > next ;
E data ;

Node ( Node <E > next , E data )


{
t h i s . next = next ;
t h i s . data = data ;
}

Node ( E data )
{
t h i s . data = data ;
}

/* * An inner class to represent an iterator


* for a MyArrayList
*/
p ub li c c l a s s MyIterator implements Iterator <E >
{
Node <E > t = n u l l ; // value just returned by next
Node <E > p = n u l l ; // previous value
boolean canRemove = f a l s e ;

p ub l i c void remove ()
{
i f ( canRemove )
{
CHAPTER 4. ABSTRACT DATA TYPES 16

i f ( p == n u l l )
{ start = start . next ;
t= null ;
}
else
{ p . next = t . next ;
t=p;
}
canRemove = f a l s e ;
}
else
throw new IllegalStateException ();
}

p ub l i c boolean hasNext ()
{
i f ( t == n u l l )
return start != n u l l ;
else
return t . next != n u l l ;
}

p ub l i c E next ()
{
p=t;
i f ( t == n u l l )
t = start ;
else
t = t . next ;
canRemove = true ;
return t . data ;
}
}
}
It is possible to avoid the special cases that occur at the start of the list by adding an
extra header node at the start of each list. The header node does not contain data, but
means that all the nodes containg data have a previous node. In this case the code for
adding changes to:
p ub li c void add ( i n t index , E element )
{
i f ( index < 0 || index > size )
throw new IndexOutOfBoundsException ("" + index );
CHAPTER 4. ABSTRACT DATA TYPES 17

Node p = start ;
f o r ( i n t i = 0; i < index ; i ++)
p = p . next ;
p . next = new Node <E >( p . next , element );
size ++;
}

Doubly linked lists


A doubly linked list has a next field and a previous field, and so can be followed in either
direction. This uses more memory than a singly linked list. It makes it easy to locate the
previous node when adding or removing, but requires that both links are changed in these
operations. Again we can use header nodes at the ends to avoid special cases when adding
or removing at the ends.
The java library LinkedList class uses a doubly linked list. It uses the same header
node at both ends, this makes the chain of nodes into a loop.

- next - next - next - next - next -

prev  prev  prev  prev  prev 


6

data data data data

Figure 4.4: A doubly linked list with 4 entries

We will look at some of the code from the LinkedList class. The full code can be
downloaded from java.sun.com.
The links of the chain are represented by an inner class called Entry. The inner class
is static, which means it does not need an instance of the containing class, and can only
access static variables of the containing class.

p r i v a t e s t a t i c c l a s s Entry <E >


{
E element ;
Entry <E > next ;
Entry <E > previous ;
CHAPTER 4. ABSTRACT DATA TYPES 18

Entry ( E element , Entry <E > next , Entry <E > previous )
{
t h i s . element = element ;
t h i s . next = next ;
t h i s . previous = previous ;
}
}
Adding items is done by calling a private method that adds a new entry before a given
entry. The modCount variable keeps a count of the number of times the list is modified.

p r i v a t e Entry <E > addBefore ( E o , Entry <E > e )


{
Entry <E > newEntry = new Entry <E >( o , e , e . previous );
newEntry . previous . next = newEntry ;
newEntry . next . previous = newEntry ;
size ++;
modCount ++;
return newEntry ;
}
Removing items uses a private method that removes a particular entry.
p r i v a t e E remove ( Entry <E > e )
{
i f ( e == header )
throw new NoSuchElementException ();
E result = e . element ;
e . previous . next = e . next ;
e . next . previous = e . previous ;
e . next = e . previous = n u l l ;
e . element = n u l l ;
size - -;
modCount ++;
return result ;
}
To find an element from its index it uses method entry. This saves time by counting
from the nearest end of the list. (size>>1) is the same as size/2.

p r i v a t e Entry <E > entry ( i n t index )


{
i f ( index < 0 || index >= size )
throw new IndexOutOfBoundsException (" Index : "+ index +
CHAPTER 4. ABSTRACT DATA TYPES 19

" , Size : "+ size );


Entry <E > e = header ;
i f ( index < ( size >> 1))
{
f o r ( i n t i = 0; i <= index ; i ++)
e = e . next ;
}
else
{
f o r ( i n t i = size ; i > index ; i - -)
e = e . previous ;
}
return e ;
}

4.2.6 Efficiencies of list operations


The efficiencies of the list operations are easy to estimate. n is the size of the list. If it is
necessary to shift elements up or down in an array implementation this will be O(n), if it is
necessary to count through a linked list to find an element this will be O(n). In particular
to print a linked list using a for loop requires a count through the array for each element
and so is O(n2 ). When adding to an array list the efficiency depends on whether the array
is already full, hence the question mark.

Array Linked list Doubly linked list


add remove at start O(n) O(1) O(1)
add remove at end O(1)? O(n) O(1)
add remove O(n) O(n) O(n)
get set O(1) O(n) O(n)
print with for loop O(n) O(n2 ) O(n2 )
print with iterator O(n) O(n) O(n)

4.3 Stacks and Queues


4.3.1 Stacks
A stack is a data structure similar to a pile of trays in a cafeteria. You can add or remove
items to the top. This means the first item you remove is the last one you added (LIFO).
The abstract data type stack can be defined by an interface. There is a stack class in
the java library, but no stack interface.
A minimum stack needs the three methods

void push(E x) Add an item to the top of the stack.


CHAPTER 4. ABSTRACT DATA TYPES 20

E pop() Removes and returns the top item.

boolean isEmpty() Returns true if the stack has no items.

It is useful to add a few more methods.


Here is an interface for a stack
package com328 . structure ;

import java . util . NoSuchElementException ;

/* * Stack
* @author C.T. Stretch
*/
p ub li c i n t e r f a c e Stack <E >
{
/* * Add an item to the top of the stack
* @param e The item to add .
*/
p ub li c void push ( E e );
/* * Remove the item from the top of the stack
* @return The item removed
* @throws NoSuchElementException if the stack is empty .
*/
p ub li c E pop ();
/* * Test if the stack is empty .
* @return true for an empty stack .
*/
p ub li c boolean isEmpty ();
/* * Returns the item on top without removing it .
* @return The item on top .
* @throws NoSuchElementException if the stack is empty .
*/
p ub li c E peek ();
/* * Clear the stack .
*/
p ub li c void clear ();
}
We use a stack if we want to store data for later processing and we want to process the
data in the reverse order.
As an example suppose we want to check a mathematical expression or Java code to
see if the brackets nest correctly.
For example (3 + 2 [5 {4 1}]) is correct but (3 + {5 4) 7} is wrong.
CHAPTER 4. ABSTRACT DATA TYPES 21

We can do this using a stack. We scan the string, when we meet an open bracket we
push it on the stack. When we meet a close bracket we pop an open bracket from the stack
and check it is of the matching type. In the second example when we meet the ) we pop a
{ from the stack and see they are not matched.

package com328 . lectures ;


import uucPack . InOut ;

import com328 . structure . ArrayStack ;


import com328 . structure . Stack ;

/* * BracketCheck
* @author C.T. Stretch
*/
p ub li c c l a s s BracketCheck
{

/* * The main method tests the check method .


* First it runs it on the program arguments if any .
* then it runs it on strings from the keyboard .
* It ends if an empty string is entered .
* @param args An array of test strings to try .
*/
p ub li c s t a t i c void main ( String [] args )
{
f o r ( i n t i =0; i < args . length ; i ++)
{
InOut . println ( args [ i ]);
InOut . println ( check ( args [ i ])? " OK ":" Fail " );
}
while ( true )
{
InOut . print (" Enter string to check :> " );
String s = InOut . readString ();
i f ( s . equals ("" )) break ;
InOut . println ( check ( s )? " OK ":" Fail " );
InOut . println (" Enter empty string to quit ." );
}
InOut . println (" Goodbye " );
}

/* * Check a string to see if brackets are correctly matched .


CHAPTER 4. ABSTRACT DATA TYPES 22

* Checks ([{}]) brackets


* @param the string to check .
* @return true if the brackets are correctly nested .
*/
s t a t i c boolean check ( String s )
{
Stack < Bracket > stk =new ArrayStack < Bracket >();
f o r ( i n t i =0; i < s . length (); i ++)
{
char c = s . charAt ( i );
switch ( c )
{
case ( : stk . push (new Bracket ( ( ,) ,i )); break ;
case [ : stk . push (new Bracket ( [ ,] ,i )); break ;
case { : stk . push (new Bracket ( { ,} ,i )); break ;
case ) : case ] : case } :
i f ( stk . isEmpty ())
{
InOut . println (" Extra "+ c +" at position "+ i );
return f a l s e ;
}
Bracket b = stk . pop ();
i f ( b . close != c )
{
InOut . println (" Open "+ b . open +" at "+ b . index +
" closed by "+ c +" at "+ i );
return f a l s e ;
}
}
}
i f ( stk . isEmpty ())
return true ;
Bracket b = stk . pop ();
InOut . println (" Unclosed "+ b . open +" at "+ b . index );
return f a l s e ;
}

/* * This static inner class holds the details


* of an open bracket on the stack .
*/
p r i v a t e s t a t i c c l a s s Bracket
{
char open ;
CHAPTER 4. ABSTRACT DATA TYPES 23

char close ;
i n t index ;

Bracket ( char open , char close , i n t index )


{
t h i s . open = open ;
t h i s . close = close ;
t h i s . index = index ;
}
}
}

Stack implementation
Stacks are easy to implement using either an array or a singly linked list. The code can be
extracted from our list implementations.
For an array implementation it is better to push and pop from the end of the array, as
these items can be used without shifting the rest.
For a linked list we can push and pop from the start, as we can find these without
searching the list.
If we use these ends all the stack operations are O(1), they do not depend on the size
of the stack.
Here is an array implementation:
package com328 . structure ;
import java . util . NoSuchElementException ;

/* * ArrayStack
* @author C.T. Stretch
*/
p ub li c c l a s s ArrayStack <E > implements Stack <E >
{
f i n a l s t a t i c i n t FIRST_LENGTH = 16;
@SuppressWarnings (" unchecked ")
p r i v a t e E [] theArray = ( E [])new Object [ FIRST_LENGTH ];
p r i v a t e i n t size ;

@SuppressWarnings (" unchecked ")


p ub li c void push ( E e )
{
i f ( size == theArray . length )
{
E [] newArray = ( E [])new Object [ theArray . length * 2];
CHAPTER 4. ABSTRACT DATA TYPES 24

System . arraycopy ( theArray , 0 , newArray , 0 , theArray . length );


theArray = newArray ;
}
theArray [ size ++]= e ;
}

p ub li c E pop ()
{
i f ( size ==0) throw new NoSuchElementException (" Empty stack " );
return theArray [ - - size ];
}

p ub li c boolean isEmpty ()
{
return size == 0;
}

p ub li c E peek ()
{
i f ( size ==0) throw new NoSuchElementException (" Empty stack " );
return theArray [ size -1];
}

p ub li c void clear ()
{
size =0;
}
}

4.3.2 Queues
A queue data structure is like a queue to be served. We can add items at the end and
remove them from the front. The first item to be removed is the first to be added (FIFO).
A minimum queue needs the three methods

void add(E x) Add an item to the end of the queue.

E remove() Removes and returns the front item.

boolean isEmpty() Returns true if the queue has no items.

Add is sometimes called enqueue or join.


Remove is sometimes called dequeue or leave
CHAPTER 4. ABSTRACT DATA TYPES 25

It is useful to add a few more methods.


Here is an interface for a queue

package com328 . structure ;

import java . util . NoSuchElementException ;

/* * Queue
* @author C.T. Stretch
*/
p ub li c i n t e r f a c e Queue <E >
{
/* * Add an item to the rear of the queue .
* @param e The item to add
*/
void add ( E e );

/* * Remove an item from the front of the queue .


* @return The item removed .
* @throws NoSuchElementException if the queue is empty .
*/
E remove ();

/* * Test if the queue is empty .


* @return true if the queue is empty .
*/
boolean isEmpty ();

/* * Get the item from the front of the queue without removing it .
* @return the front item .
* @throws NoSuchElementException if the queue is empty .
*/
E examine ();

/* * Clear the queue .


*/
void clear ();
}
Queues are used for many purposes, one example is the event queue in Java GUI
programming. Events such as mouse clicks and key presses are queued in the event queue
so that they can be passed to event listeners in the order they are received.
CHAPTER 4. ABSTRACT DATA TYPES 26

Queue implementations
Queues are a bit harder to implement efficiently as we need to access both ends of a list.
We can use a doubly linked list, which has quick access to both ends of the list. We can
use a circular array. We use an array, but do not move the data when we remove an item
from the front. This means are queue will move up the array.
start start+size

? ?

6 6

remove from here add to here

Figure 4.5: A circular array

To avoid wasting space we allow the front of the queue to wrap to the start. We can
calculate the position after the end of the array as (start + size)%length

(start+size)%length start
? ?

6 6

add to here remove from here

Figure 4.6: Wrapping

With either of these implementations our queue operations are all O(1).
Here is an array implementation:

package com328 . structure ;


import java . util . NoSuchElementException ;

/* * ArrayQueue
* @author C.T. Stretch
*/
p ub li c c l a s s ArrayQueue <E > implements Queue <E >
{

f i n a l s t a t i c i n t FIRST_LENGTH = 16;
@SuppressWarnings (" unchecked ")
CHAPTER 4. ABSTRACT DATA TYPES 27

private E [] theArray = ( E [])new Object [ FIRST_LENGTH ];


private int start = 0;
private int size = 0;
private int length = FIRST_LENGTH ;

@SuppressWarnings (" unchecked ")


p ub li c void add ( E e )
{
i f ( size == length )
{
i n t newLength = length * 2;
E [] newArray = ( E [])new Object [ newLength ];
System . arraycopy ( theArray , start , newArray , 0 , length - start );
System . arraycopy ( theArray , 0 , newArray , length - start , start );
theArray = newArray ;
start = 0;
length = newLength ;
}
theArray [ place ( size ++)] = e ;
}

p r i v a t e i n t place ( i n t i )
{
return ( start + i ) % length ;
}

p ub li c E remove ()
{
i f ( size ==0) throw new NoSuchElementException (" Empty queue " );
E e = theArray [ start ];
start = ( start + 1) % length ;
size - -;
return e ;
}

p ub li c boolean isEmpty ()
{
return size == 0;
}

p ub li c E examine ()
{
i f ( size ==0) throw new NoSuchElementException (" Empty queue " );
CHAPTER 4. ABSTRACT DATA TYPES 28

return theArray [ start ];


}

@SuppressWarnings (" unchecked ")


p ub li c void clear ()
{
size = 0;
start = 0;
theArray = ( E [])new Object [ FIRST_LENGTH ];
length = FIRST_LENGTH ;
}

4.3.3 Other Linear ADTs


We have seen the ADTs List, Stack and Queue. These are all Linear ADTs, versions of
lists where the data is arranged in a line. There are many other linear ADTs, including

Sorted lists A list of comparable items that are always kept in order.

Priority queues Queues where an item has a priority and highest prioriy items are re-
moved first.

Dequeues Double ended queues, pronounced decks, items can be added or removed at
both ends.

Anda mungkin juga menyukai