An interface, sometimes also called an abstract data type, defines the set of operations supported by a data structure and the semantics, or meaning, of those operations. An interface tells us nothing about how the data structure implements these operations; it only provides a list of supported operations along with specifications about what types of arguments each operation accepts and the value returned by each operation.
A data structure implementation, on the other hand, includes the internal representation of the data structure as well as the definitions of the algorithms that implement the operations supported by the data structure. Thus, there can be many implementations of a single interface. For example, in Chapter 2, we will see implementations of the List interface using arrays and in Chapter 3 we will see implementations of the List interface using pointer-based data structures. Each implements the same interface, List, but in different ways.
The Queue interface represents a collection of elements to which we can add elements and remove the next element. More precisely, the operations supported by the Queue interface are
A FIFO (first-in-first-out) Queue,
which is illustrated in
Figure 1.1, removes items in the same order they were added, much
in the same way a queue (or line-up) works when checking out at a cash
register in a grocery store. This is the most common kind of Queue
so the qualifier FIFO is often omitted. In other texts, the
and
operations on a FIFO Queue are often called
and
, respectively.
A priority Queue,
illustrated in Figure 1.2, always
removes the smallest element from the Queue, breaking ties arbitrarily.
This is similar to the way in which patients are triaged in a hospital
emergency room. As patients arrive they are evaluated and then placed in
a waiting room. When a doctor becomes available he or she first treats
the patient with the most life-threatening condition. The
operation on a priority Queue is usually called
in
other texts.
A very common queueing discipline is the LIFO (last-in-first-out)
discipline, illustrated in Figure 1.3. In a LIFO Queue,
the most recently added element is the next one removed. This is best
visualized in terms of a stack of plates; plates are placed on the top of
the stack and also removed from the top of the stack. This structure is
so common that it gets its own name: Stack. Often, when discussing a
Stack, the names of
and
are changed to
and
; this is to avoid confusing the LIFO and FIFO queueing
disciplines.
A Deque
is a generalization of both the FIFO Queue and LIFO Queue (Stack).
A Deque represents a sequence of elements, with a front and a back.
Elements can be added at the front of the sequence or the back of the
sequence. The names of the Deque operations are self-explanatory:
,
,
, and
. It is
worth noting that a Stack can be implemented using only
and
while a FIFO Queue can be implemented using
and
.
This book will talk very little about the FIFO Queue, Stack, or
Deque interfaces. This is because these interfaces are subsumed by the
List interface. A List,
illustrated in Figure 1.4, represents a
sequence,
, of values. The List interface
includes the following operations:
![]() |
![]() |
![]() |
|
![]() |
![]() |
![]() |
|
![]() |
![]() |
![]() |
|
![]() |
![]() |
![]() |
![]() |
Although we will normally not discuss the Stack, Deque and FIFO Queue interfaces in subsequent chapters, the terms Stack and Deque are sometimes used in the names of data structures that implement the List interface. When this happens, it highlights the fact that these data structures can be used to implement the Stack or Deque interface very efficiently. For example, the ArrayDeque class is an implementation of the List interface that implements all the Deque operations in constant time per operation.
The USet
interface represents an unordered set of unique elements, which
mimics a mathematical set. A USet contains
distinct
elements; no element appears more than once; the elements are in no
specific order. A USet supports the following operations:
These definitions are a bit fussy about distinguishing
, the element
we are removing or finding, from
, the element we may remove or find.
This is because
and
might actually be distinct objects that
are nevertheless treated as equal.1.2Such a distinction is useful because it allows for the creation of
dictionaries or maps that map keys onto values.
To create a dictionary/map, one forms compound objects called Pairs,
each of which contains a key and a value. Two Pairs
are treated as equal if their keys are equal. If we store some pair
in a USet and then later call the
method using
the pair
the result will be
. In other
words, it is possible to recover the value,
, given only the key,
.
The SSet interface represents a sorted set of elements. An SSet
stores elements from some total order, so that any two elements
and
can be compared. In code examples, this will be done with a
method called
in which
This version of the
operation is sometimes referred to
as a successor search.
It differs in a fundamental way from
since it returns a meaningful result even when there is
no element equal to
in the set.
The distinction between the USet and SSet
operations is
very important and often missed. The extra functionality provided
by an SSet usually comes with a price that includes both a larger
running time and a higher implementation complexity. For example, most
of the SSet implementations discussed in this book all have
operations with running times that are logarithmic in the size of the set.
On the other hand, the implementation of a USet as a ChainedHashTable
in Chapter 5 has a
operation that runs in constant
expected time. When choosing which of these structures to use, one should
always use a USet unless the extra functionality offered by an SSet
is truly needed.