Algorithms,.Robert.sedgewick,.Kevin.wayne,.4e 2
Algorithms,.Robert.sedgewick,.Kevin.wayne,.4e 2
Algorithms,.Robert.sedgewick,.Kevin.wayne,.4e 2
Several fundamental data types involve collections of objects. Specifically, the set
of values is a collection of objects, and the operations revolve around adding, remov-
ing, or examining objects in the collection. In this section, we consider three such data
types, known as the bag, the queue, and the stack. They differ in the specification of
which object is to be removed or examined next.
Bags, queues, and stacks are fundamental and broadly useful. We use them in imple-
mentations throughout the book. Beyond this direct applicability, the client and imple-
mentation code in this section serves as an introduction to our general approach to the
development of data structures and algorithms.
One goal of this section is to emphasize the idea that the way in which we represent
the objects in the collection directly impacts the efficiency of the various operations.
For collections, we design data structures for representing the collection of objects that
can support efficient implementation of the requisite operations.
A second goal of this section is to introduce generics and iteration, basic Java con-
structs that substantially simplify client code. These are advanced programming-lan-
guage mechanisms that are not necessarily essential to the understanding of algorithms,
but their use allows us to develop client code (and implementations of algorithms) that
is more clear, compact, and elegant than would otherwise be possible.
A third goal of this section is to introduce and show the importance of linked data
structures. In particular, a classic data structure known as the linked list enables im-
plementation of bags, queues, and stacks that achieve efficiencies not otherwise pos-
sible. Understanding linked lists is a key first step to the study of algorithms and data
structures.
For each of the three types, we consider APIs and sample client programs, then
look at possible representations of the data type values and implementations of the
data-type operations. This scenario repeats (with more complicated data structures)
throughout this book. The implementations here are models of implementations later
in the book and worthy of careful study.
120
1.3 ■ Bags, Queues, and Stacks 121
APIs As usual, we begin our discussion of abstract data types for collections by de-
fining their APIs, shown below. Each contains a no-argument constructor, a method to
add an item to the collection, a method to test whether the collection is empty, and a
method that returns the size of the collection. Stack and Queue each have a method to
remove a particular item from the collection. Beyond these basics, these APIs reflect two
Java features that we will describe on the next few pages: generics and iterable collections.
Bag
FIFO queue
this conversion is helpful because it enables us to use generics with primitive types, as
in the following code:
Stack<Integer> stack = new Stack<Integer>();
stack.push(17); // auto-boxing (int -> Integer)
int i = stack.pop(); // auto-unboxing (Integer -> int)
Iterable collections. For many applications, the client’s requirement is just to process
each of the items in some way, or to iterate through the items in the collection. This
paradigm is so important that it has achieved first-class status in Java and many other
modern languages (the programming language itself has specific mechanisms to sup-
port it, not just the libraries). With it, we can write clear and compact code that is free
from dependence on the details of a collection’s implementation. For example, suppose
that a client maintains a collection of transactions in a Queue, as follows:
Queue<Transaction> collection = new Queue<Transaction>();
If the collection is iterable, the client can print a transaction list with a single statement:
for (Transaction t : collection)
{ StdOut.println(t); }
This construct is known as the foreach statement: you can read the for statement as for
each transaction t in the collection, execute the following block of code. This client code
does not need to know anything about the representation or the implementation of the
collection; it just wants to process each of the items in the collection. The same for loop
would work with a Bag of transactions or any other iterable collection. We could hardly
imagine client code that is more clear and compact. As you will see, supporting this
capability requires extra effort in the implementation, but this effort is well worthwhile.
It is interesting to note that the only differences between the APIs for Stack and
Queue are their names and the names of the methods. This observation highlights the
idea that we cannot easily specify all of the characteristics of a data type in a list of
method signatures. In this case, the true specification has to do with the English-lan-
guage descriptions that specify the rules by which an item is chosen to be removed (or
to be processed next in the foreach statement). Differences in these rules are profound,
part of the API, and certainly of critical importance in developing client code.
124 CHAPTER 1 ■ Fundamentals
while (!StdIn.isEmpty())
numbers.add(StdIn.readDouble());
int N = numbers.size();
sum = 0.0;
for (double x : numbers)
sum += (x - mean)*(x - mean);
double std = Math.sqrt(sum/(N-1));
}
}
Mean: 100.60
Std dev: 10.51
126 CHAPTER 1 ■ Fundamentals
FIFO queues. A FIFO queue (or just a queue) is a collection that is based on the first-
in-first-out (FIFO) policy. The policy of doing tasks in the same order that they arrive
is one that we encounter frequently in everyday life:
server
queue of customers from people waiting in line at a theater, to cars wait-
ing in line at a toll booth, to tasks waiting to be ser-
0 1 2 viced by an application on your computer. One bed-
rock principle of any service policy is the perception
new arrival
at the end of fairness. The first idea that comes to mind when
enqueue most people think about fairness is that whoever
3 0 1 2 3
has been waiting the longest should be served first.
new arrival That is precisely the FIFO discipline. Queues are a
at the end
natural model for many everyday phenomena, and
enqueue
0 1 2 3 4
they play a central role in numerous applications.
4
When a client iterates through the items in a queue
first in line with the foreach construct, the items are processed
leaves queue
dequeue in the order they were added to the queue. A typi-
0 0 1 2 3 4 cal reason to use a queue in an application is to save
items in a collection while at the same time preserv-
next in line
leaves queue ing their relative order : they come out in the same
dequeue order in which they were put in. For example, the
1 1 2 3 4 client below is a possible implementation of the
readDoubles() static method from our In class.
A typical FIFO queue The problem that this method solves for the client
is that the client can get numbers from a file into an
array without knowing the file size ahead of time. We enqueue the numbers from the file,
use the size() method from Queue to find the size needed for the array, create the ar-
ray, and then dequeue the num-
bers to move them to the array. public static int[] readInts(String name)
{
A queue is appropriate because In in = new In(name);
it puts the numbers into the ar- Queue<Integer> q = new Queue<Integer>();
ray in the order in which they while (!in.isEmpty())
q.enqueue(in.readInt());
appear in the file (we might use
int N = q.size();
a Bag if that order is immateri-
int[] a = new int[N];
al). This code uses autoboxing for (int i = 0; i < N; i++)
and auto-unboxing to convert a[i] = q.dequeue();
return a;
between the client’s double }
primitive type and and the
queue’s Double wrapper type. Sample Queue client
1.3 ■ Bags, Queues, and Stacks 127
If you multiply 4 by 5, add 3 to 2, multiply the result, and then add 1, you get the value
101. But how does the Java system do this calculation? Without going into the details of
how the Java system is built, we can address the essential ideas by writing a Java program
that can take a string as input (the expression) and produce the number represented by
the expression as output. For simplicity, we begin with the following explicit recursive
definition: an arithmetic expression is either a number, or a left parenthesis followed by
an arithmetic expression followed by an operator followed by another arithmetic ex-
pression followed by a right parenthesis. For simplicity, this definition is for fully paren-
thesized arithmetic expressions, which specify precisely which operators apply to which
operands—you are a bit more familiar with expressions such as 1 + 2 * 3, where we
often rely on precedence rules instead of parentheses. The same basic mechanisms that
we consider can handle precedence rules, but we avoid that complication. For speci-
ficity, we support the familiar binary operators *, +, -, and /, as well as a square-root
operator sqrt that takes just one argument. We could easily allow more operators and
more kinds of operators to embrace a large class of familiar mathematical expressions,
involving trigonometric, exponential, and logarithmic functions. Our focus is on un-
derstanding how to interpret the string of parentheses, operators, and numbers to en-
able performing in the proper order the low-level arithmetic operations that are avail-
able on any computer. Precisely how can we convert an arithmetic expression—a string
of characters—to the value that it represents? A remarkably simple algorithm that was
developed by E. W. Dijkstra in the 1960s uses two stacks (one for operands and one for
operators) to do this job. An expression consists of parentheses, operators, and oper-
ands (numbers). Proceeding from left to right and taking these entities one at a time,
we manipulate the stacks according to four possible cases, as follows:
■ Push operands onto the operand stack.
of operands, and push onto the operand stack the result of applying that opera-
tor to those operands.
After the final right parenthesis has been processed, there is one value on the stack,
which is the value of the expression. This method may seem mysterious at first, but it