Algorithms.in.C.part.5.3rd.edition.2001.8
Algorithms.in.C.part.5.3rd.edition.2001.8
in C
PART 5
GRAPH ALGORITHMS
Robert Sedgewick
Princeton University
Addison-Wesley
Boston • San Francisco • New York • Toronto • Montreal
London • Munich • Paris • Madrid
Capetown • Sydney • Tokyo • Singapore • Mexico City
Many of the designations used by manufacturers and sellers to distin-
guish their products are claimed as trademarks. Where those designa-
tions appear in this book and we were aware of a trademark claim, the
designations have been printed in initial capital letters or all capitals.
The author and publisher have taken care in the preparation of this
book, but make no expressed or implied warranty of any kind and as-
sume no responsibility for errors or omissions. No liability is assumed
for incidental or consequential damages in connection with or arising
out of the use of the information or programs contained herein.
Copyright
c 2002 by Addison-Wesley
All rights reserved. No part of this publication may be reproduced,
stored in a retrieval system, or transmitted, in any form or by any
means, electronic, mechanical, photocopying, recording, or otherwise,
without the prior written permission of the publisher. Printed in the
United States of America. Published simultaneously in Canada.
The publisher offers discounts on this book when ordered in quantity
for special sales. For more information, please contact: Pearson Edu-
cation Corporate Sales Division, One Lake Street, Upper Saddle River,
NJ 07458, (800) 382-3419, [email protected].
Visit us on the Web at www.awl.com/cseng/ .
Library of Congress Cataloging-in-Publication Data
Sedgewick, Robert, 1946 –
Algorithms in C / Robert Sedgewick. — 3d ed.
500 p. 24 cm.
Includes bibliographical references and index.
Contents: v. 2, pt. 5. Graph algorithms
1. C (Computer program language) 2. Computer algorithms.
I. Title.
QA76.73.C15S43 2002
005.13’3—dc21 97-23418
CIP
ISBN 0201316633
Text printed on recycled and acid-free paper.
6 7 8 9 1011 DOC 09 08 07
6th Printing July 2007
Preface
Algorithms
This book is the second of three volumes that are intended to survey
the most important computer algorithms in use today. The first volume
(Parts 1–4) covers fundamental concepts (Part 1), data structures (Part
2), sorting algorithms (Part 3), and searching algorithms (Part 4);
this volume (Part 5) covers graphs and graph algorithms; and the
(yet to be published) third volume (Parts 6–8) covers strings (Part
6), computational geometry (Part 7), and advanced algorithms and
applications (Part 8).
The books are useful as texts early in the computer science cur-
riculum, after students have acquired basic programming skills and
familiarity with computer systems, but before they have taken spe-
cialized courses in advanced areas of computer science or computer
applications. The books also are useful for self-study or as a refer-
ence for people engaged in the development of computer systems or
applications programs because they contain implementations of useful
algorithms and detailed information on these algorithms’ performance
characteristics. The broad perspective taken makes the series an ap-
propriate introduction to the field.
PREFACE
Scope
This book, Algorithms in C, Third Edition, Part 5: Graph Algorithms,
contains six chapters that cover graph properties and types, graph
search, directed graphs, minimal spanning trees, shortest paths, and
networks. The descriptions here are intended to give readers an un-
derstanding of the basic properties of as broad a range of fundamental
graph algorithms as possible.
iv
You will most appreciate the material here if you have had a
course covering basic principles of algorithm design and analysis and
programming experience in a high-level language such as C, Java, or
C++. Algorithms in C, Third Edition, Parts 1–4 is certainly ade-
quate preparation. This volume assumes basic knowledge about ar-
rays, linked lists, and ADT design, and makes uses of priority-queue,
symbol-table, and union-find ADTs—all of which are described in de-
tail in Parts 1–4 (and in many other introductory texts on algorithms
and data structures).
Basic properties of graphs and graph algorithms are developed
from first principles, but full understanding of the properties of the
algorithms can lead to deep and difficult mathematics. Although the
discussion of advanced mathematical concepts is brief, general, and
descriptive, you certainly need a higher level of mathematical maturity
to appreciate graph algorithms than you do for the topics in Parts 1–4.
Still, readers at various levels of mathematical maturity will be able to
profit from this book. The topic dictates this approach: some elemen-
tary graph algorithms that should be understood and used by everyone
differ only slightly from some advanced algorithms that are not un-
derstood by anyone. The primary intent here is to place important
algorithms in context with other methods throughout the book, not
to teach all of the mathematical material. But the rigorous treatment
demanded by good mathematics often leads us to good programs, so I
have tried to provide a balance between the formal treatment favored
by theoreticians and the coverage needed by practitioners, without
sacrificing rigor.
v
PREFACE
vi
rithms are brought to light on an intuitive level through the visual
dimension provided by these figures.
Characteristics of the algorithms and of the situations in which
they might be useful are discussed in detail. Although not emphasized,
connections to the analysis of algorithms and theoretical computer
science are developed in context. When appropriate, empirical and
analytic results are presented to illustrate why certain algorithms are
preferred. When interesting, the relationship of the practical algo-
rithms being discussed to purely theoretical results is described. Spe-
cific information on performance characteristics of algorithms and im-
plementations is synthesized, encapsulated, and discussed throughout
the book.
Programming Language
The programming language used for all of the implementations is C
(versions of the book in C++ and Java are under development). Any
particular language has advantages and disadvantages; we use C in this
book because it is widely available and provides the features needed
for the implementations here. The programs can be translated easily
to other modern programming languages because relatively few con-
structs are unique to C. We use standard C idioms when appropriate,
but this book is not intended to be a reference work on C program-
ming.
We strive for elegant, compact, and portable implementations,
but we take the point of view that efficiency matters, so we try to
be aware of the code’s performance characteristics at all stages of
development. There are many new programs in this edition, and
many of the old ones have been reworked, primarily to make them
more readily useful as abstract-data-type implementations. Extensive
comparative empirical tests on the programs are discussed throughout
the book.
A goal of this book is to present the algorithms in as simple and
direct a form as possible. The style is consistent whenever possible
so that similar programs look similar. For many of the algorithms,
the similarities remain regardless of which language is used: Dijkstra’s
algorithm (to pick one prominent example) is Dijkstra’s algorithm,
whether expressed in Algol-60, Basic, Fortran, Smalltalk, Ada, Pascal,
vii
PREFACE
Acknowledgments
Many people gave me helpful feedback on earlier versions of this book.
In particular, hundreds of students at Princeton and Brown have suf-
fered through preliminary drafts over the years. Special thanks are due
to Trina Avery and Tom Freeman for their help in producing the first
edition; to Janet Incerpi for her creativity and ingenuity in persuading
our early and primitive digital computerized typesetting hardware and
software to produce the first edition; to Marc Brown for his part in the
algorithm visualization research that was the genesis of so many of the
figures in the book; to Dave Hanson for his willingness to answer all of
my questions about C; and to Kevin Wayne, for patiently answering my
basic questions about networks. I would also like to thank the many
readers who have provided me with detailed comments about various
editions, including Guy Almes, Jon Bentley, Marc Brown, Jay Gischer,
Allan Heydon, Kennedy Lemke, Udi Manber, Dana Richards, John
Reif, M. Rosenfeld, Stephen Seidman, Michael Quinn, and William
Ward.
To produce this new edition, I have had the pleasure of working
with Peter Gordon and Helen Goldstein at Addison-Wesley, who have
patiently shepherded this project as it has evolved from a standard
update to a massive rewrite. It has also been my pleasure to work with
several other members of the professional staff at Addison-Wesley. The
nature of this project made the book a somewhat unusual challenge
for many of them, and I much appreciate their forbearance.
I have gained two new mentors in writing this book, and partic-
ularly want to express my appreciation to them. First, Steve Summit
carefully checked early versions of the manuscript on a technical level,
and provided me with literally thousands of detailed comments, partic-
ularly on the programs. Steve clearly understood my goal of providing
elegant, efficient, and effective implementations, and his comments not
only helped me to provide a measure of consistency across the imple-
mentations, but also helped me to improve many of them substantially.
Second, Lyn Dupre also provided me with thousands of detailed com-
viii
ments on the manuscript, which were invaluable in helping me not only
to correct and avoid grammatical errors, but also—more important—
to find a consistent and coherent writing style that helps bind together
the daunting mass of technical material here. I am extremely grateful
for the opportunity to learn from Steve and Lyn—their input was vital
in the development of this book.
Much of what I have written here I have learned from the teaching
and writings of Don Knuth, my advisor at Stanford. Although Don had
no direct influence on this work, his presence may be felt in the book,
for it was he who put the study of algorithms on the scientific footing
that makes a work such as this possible. My friend and colleague
Philippe Flajolet, who has been a major force in the development of
the analysis of algorithms as a mature research area, has had a similar
influence on this work.
I am deeply thankful for the support of Princeton University,
Brown University, and the Institut National de Recherce en Informa-
tique et Automatique (INRIA), where I did most of the work on the
books; and of the Institute for Defense Analyses and the Xerox Palo
Alto Research Center, where I did some work on the books while
visiting. Many parts of these books are dependent on research that
has been generously supported by the National Science Foundation
and the Office of Naval Research. Finally, I thank Bill Bowen, Aaron
Lemonick, and Neil Rudenstine for their support in building an aca-
demic environment at Princeton in which I was able to prepare this
book, despite my numerous other responsibilities.
Robert Sedgewick
Marly-le-Roi, France, February, 1983
Princeton, New Jersey, January, 1990
Jamestown, Rhode Island, May, 2001
ix
This page intentionally left blank
To Adam, Andrew, Brett, Robbie,
and especially Linda
xi
Notes on Exercises
Classifying exercises is an activity fraught with peril, because readers
of a book such as this come to the material with various levels of
knowledge and experience. Nonetheless, guidance is appropriate, so
many of the exercises carry one of four annotations, to help you decide
how to approach them.
Exercises that test your understanding of the material are marked
with an open triangle, as follows:
17.2 Consider the graph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
Draw the its DFS tree and use the tree to find the graph’s bridges
and edge-connected components.
Most often, such exercises relate directly to examples in the text. They
should present no special difficulty, but working them might teach you
a fact or concept that may have eluded you when you read the text.
Exercises that add new and thought-provoking information to the
material are marked with an open circle, as follows:
xiii
This page intentionally left blank
Contents
Graph Algorithms
xvi
Chapter 21. Shortest Paths 265
21.1 Underlying Principles · 273
21.2 Dijkstra’s algorithm · 280
21.3 All-Pairs Shortest Paths · 290
21.4 Shortest Paths in Acyclic Networks · 300
21.5 Euclidean Networks · 308
21.6 Reduction · 314
21.7 Negative Weights · 331
21.8 Perspective · 350
Index 475
xvii
This page intentionally left blank
P A R T
F I V E
Graph Algorithms
This page intentionally left blank
CHAPTER SEVENTEEN
3
4 CHAPTER SEVENTEEN
17.1 Glossary
A substantial amount of nomenclature is associated with graphs. Most
of the terms have straightforward definitions, and, for reference, it is
convenient to consider them in one place: here. We have already used
some of these concepts when considering basic algorithms in Part 1;
others of them will not become relevant until we address associated
advanced algorithms in Chapters 18 through 22.
Definition 17.1 A graph is a set of vertices plus a set of edges that
connect pairs of distinct vertices (with at most one edge connecting
any pair of vertices).
We use the names 0 through V-1 for the vertices in a V -vertex graph.
The main reason that we choose this system is that we can access
quickly information corresponding to each vertex, using array index-
ing. In Section 17.6, we consider a program that uses a symbol table
to establish a 1–1 mapping to associate V arbitrary vertex names with
the V integers between 0 and V − 1. With that program in hand, we
can use indices as vertex names (for notational convenience) without
loss of generality. We sometimes assume that the set of vertices is
defined implicitly, by taking the set of edges to define the graph and
considering only those vertices that are included in at least one edge.
To avoid cumbersome usage such as “the ten-vertex graph with the
following set of edges,” we do not explicitly mention the number of
vertices when that number is clear from the context. By convention,
we always denote the number of vertices in a given graph by V , and
denote the number of edges by E.
We adopt as standard this definition of a graph (which we first
encountered in Chapter 5), but note that it embodies two technical
simplifications. First, it disallows duplicate edges (mathematicians
8 §17.1 CHAPTER SEVENTEEN
sometimes refer to such edges as parallel edges, and a graph that can
contain them as a multigraph). Second, it disallows edges that connect
vertices to themselves; such edges are called self-loops. Graphs that
have no parallel edges or self-loops are sometimes referred to as simple
graphs.
We use simple graphs in our formal definitions because it is easier
to express their basic properties and because parallel edges and self-
loops are not needed in many applications. For example, we can
bound the number of edges in a simple graph with a given number of
vertices.
Property 17.1 A graph with V vertices has at most V (V −1)/2 edges.
Proof : The total of V 2 possible pairs of vertices includes V self-loops
and accounts twice for each edge between distinct vertices, so the
number of edges is at most (V 2 − V )/2 = V (V − 1)/2.
No such bound holds if we allow parallel edges: a graph that is not
simple might consist of two vertices and billions of edges connecting
them (or even a single vertex and billions of self-loops).
For some applications, we might consider the elimination of par-
allel edges and self-loops to be a data-processing problem that our
implementations must address. For other applications, ensuring that
a given set of edges represents a simple graph may not be worth the
trouble. Throughout the book, whenever it is more convenient to ad-
dress an application or to develop an algorithm by using an extended
definition that includes parallel edges or self-loops, we shall do so.
For example, self-loops play a critical role in a classical algorithm that
we will examine in Section 17.4; and parallel edges are common in
the applications that we address in Chapter 22. Generally, it is clear
from the context whether we intend the term “graph” to mean “simple
graph” or “multigraph” or “multigraph with self-loops.”
Mathematicians use the words vertex and node interchangeably,
but we generally use vertex when discussing graphs and node when
discussing representations—for example, in C data structures. We
normally assume that a vertex can have a name and can carry other
associated information. Similarly, the words arc, edge, and link are all
widely used by mathematicians to describe the abstraction embodying
a connection between two vertices, but we consistently use edge when
discussing graphs and link when discussing C data structures.
GRAPH PROPERTIES AND TYPES §17.1 9
the vertices correspond to points in the plane and the distances between
them are relevant. We refer to such graphs as Euclidean graphs. For
many other applications, such as graphs that represent relationships
0 or schedules, the graphs simply embody connectivity information, and
6 7 8 no particular geometric placement of vertices is ever implied. We
1 2 consider examples of algorithms that exploit the geometric information
in Euclidean graphs in Chapters 20 and 21, but we primarily work with
3 9 10
algorithms that make no use of any geometric information, and stress
4
5 11 12 that graphs are generally independent of any particular representation
in a drawing or in a computer.
Focusing solely on the connections themselves, we might wish to
view the vertex labels as merely a notational convenience, and to regard
10
6 1 8 two graphs as being the same if they differ in only the vertex labels.
7 2 Two graphs are isomorphic if we can change the vertex labels on one
to make its set of edges identical to the other. Determining whether
3 9 0
12 or not two graphs are isomorphic is a difficult computational problem
5 11 4 (see Figure 17.2 and Exercise 17.5). It is challenging because there are
V ! possible ways to label the vertices—far too many for us to try all
the possibilities. Therefore, despite the potential appeal of reducing
0 the number of different graph structures that we have to consider by
6 7 8 treating isomorphic graphs as identical structures, we rarely do so.
1 2
As we saw with trees in Chapter 5, we are often interested in
3 9 10 basic structural properties that we can deduce by considering specific
4 sequences of edges in a graph.
5 11 12
Definition 17.2 A path in a graph is a sequence of vertices in which
each successive vertex (after the first) is adjacent to its predecessor in
Figure 17.2 the path. In a simple path, the vertices and edges are distinct. A cycle
Graph isomorphism examples
is a path that is simple except that the first and final vertices are the
The top two graphs are isomorphic
same.
because we can relabel the ver-
tices to make the two sets of edges We sometimes use the term cyclic path to refer to a path whose first
identical (to make the middle
graph the same as the top graph, and final vertices are the same (and is otherwise not necessarily simple);
change 10 to 4, 7 to 3, 2 to 5, 3 to and we use the term tour to refer to a cyclic path that includes every
1, 12 to 0, 5 to 2, 9 to 11, 0 to 12, vertex. An equivalent way to define a path is as the sequence of
11 to 9, 1 to 7, and 4 to 10). The edges that connect the successive vertices. We emphasize this in our
bottom graph is not isomorphic to
the others because there is no way notation by connecting vertex names in a path in the same way as we
to relabel the vertices to make its connect them in an edge. For example, the simple paths in Figure 17.1
set of edges identical to either. include 3-4-6-0-2, and 9-12-11, and the cycles in the graph include
GRAPH PROPERTIES AND TYPES §17.1 11
vertex
path Figure 17.3
spanning tree Graph terminology
This graph has 55 vertices, 70
edges, and 3 connected compo-
cycle nents. One of the connected com-
ponents is a tree (right). The graph
has many cycles, one of which is
highlighted in the large connected
component (left). The diagram also
tree
depicts a spanning tree in the small
edge
connected component (center).
The graph as a whole does not
clique
have a spanning tree, because it
is not connected.
knots or beads, and the edges were physical connections, such as strings
2
3 or wires, a connected graph would stay in one piece if picked up by
1
any vertex, and a graph that is not connected comprises two or more
4
such pieces.
0
Definition 17.4 An acyclic connected graph is called a tree (see Chap-
5
ter 4). A set of trees is called a forest. A spanning tree of a connected
8
6
graph is a subgraph that contains all of that graph’s vertices and is a
7 single tree. A spanning forest of a graph is a subgraph that contains
2
all of that graph’s vertices and is a forest.
1 3
For example, the graph illustrated in Figure 17.1 has three con-
nected components, and is spanned by the forest 7-8 9-10 9-11 9-12
0 4
0-1 0-2 0-5 5-3 5-4 4-6 (there are many other spanning forests).
7 5 Figure 17.3 highlights these and other features in a larger graph.
6 We explore further details about trees in Chapter 4, and look at
various equivalent definitions. For example, a graph G with V vertices
2
1 is a tree if and only if it satisfies any of the following four conditions:
3
• G has V − 1 edges and no cycles.
0
• G has V − 1 edges and is connected.
4
6 • Exactly one simple path connects each pair of vertices in G.
5
• G is connected, but removing any edge disconnects it.
1 2 Any one of these conditions is necessary and sufficient to prove the
other three, and we can develop other combinations of facts about
0 3
trees from them (see Exercise 17.1). Formally, we should choose one
5 4 condition to serve as a definition; informally, we let them collectively
serve as the definition, and freely engage in usage such as the “acyclic
1
2 connected graph” choice in Definition 17.4.
0 Graphs with all edges present are called complete graphs (see
3 Figure 17.4). We define the complement of a graph G by starting with
4
a complete graph that has the same set of vertices as the original graph,
Figure 17.4 and removing the edges of G. The union of two graphs is the graph
Complete graphs induced by the union of their sets of edges. The union of a graph and
These complete graphs, with ev- its complement is a complete graph. All graphs that have V vertices are
ery vertex connected to every other subgraphs of the complete graph that has V vertices. The total number
vertex, have 10, 15, 21, 28, and of different graphs that have V vertices is 2V (V −1)/2 (the number of
36 edges (bottom to top). Every different ways to choose a subset from the V (V − 1)/2 possible edges).
graph with between 5 and 9 ver-
tices (there are more than 68 bil- A complete subgraph is called a clique.
lion such graphs) is a subgraph of Most graphs that we encounter in practice have relatively few
one of these graphs. of the possible edges present. To quantify this concept, we define the
GRAPH PROPERTIES AND TYPES §17.1 13
17.3 Write down a list of the nonisomorphic cycles of the graph in Fig-
ure 17.1. For example, if your list contains 3-4-5-3, it should not contain
3-5-4-3, 4-5-3-4, 4-3-5-4, 5-3-4-5, or 5-4-3-5.
17.4 Consider the graph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
Determine the number of connected components, give a spanning forest, list
all the simple paths with at least three vertices, and list all the nonisomorphic
cycles (see Exercise 17.3).
16 §17.2 CHAPTER SEVENTEEN
◦ 17.5 Consider the graphs defined by the following four sets of edges:
0-1 0-2 0-3 1-3 1-4 2-5 2-9 3-6 4-7 4-8 5-8 5-9 6-7 6-9 7-8
0-1 0-2 0-3 0-3 1-4 2-5 2-9 3-6 4-7 4-8 5-8 5-9 6-7 6-9 7-8
0-1 1-2 1-3 0-3 0-4 2-5 2-9 3-6 4-7 4-8 5-8 5-9 6-7 6-9 7-8
4-1 7-9 6-2 7-3 5-0 0-2 0-8 1-6 3-9 6-3 2-8 1-5 9-8 4-5 4-7
Which of these graphs are isomorphic to one another? Which of them are
planar?
17.6 Consider the more than 68 billion graphs referred to in the caption to
Figure 17.4. What percentage of them has fewer than nine vertices?
17.7 How many different subgraphs are there in a given graph with V ver-
tices and E edges?
• 17.8 Give tight upper and lower bounds on the number of connected com-
ponents in graphs that have V vertices and E edges.
◦ 17.9 How many different undirected graphs are there that have V vertices
and E edges?
••• 17.10 If we consider two graphs to be different only if they are not isomorphic,
how many different graphs are there that have V vertices and E edges?
17.11 How many V -vertex graphs are bipartite?
(by removing some edges and adding others), and to retrieve the graphs
(in the form of an array of edges).
The ADT in Program 17.1 is primarily a vehicle to allow us to
develop and test algorithms; it is not a general-purpose interface. As
usual, we work with the simplest interface that supports the basic
graph-processing operations that we wish to consider. Defining such
an interface for use in practical applications involves making numerous
tradeoffs among simplicity, efficiency, and generality. We consider a
few of these tradeoffs next; we address many others in the context of
implementations and applications throughout this book.
We assume for simplicity that graph representations include inte-
gers V and E that contain the number of vertices and edges, respectively,
so that we can refer directly to those values by name in ADT implemen-
tations. When convenient, we make other, similar, assumptions about
18 §17.2 CHAPTER SEVENTEEN
#include <stdio.h>
#include "GRAPH.h"
main(int argc, char *argv[])
{ int V = atoi(argv[1]), E = atoi(argv[2]);
Graph G = GRAPHrand(V, E);
if (V < 20)
GRAPHshow(G);
else printf("%d vertices, %d edges, ", V, E);
printf("%d component(s)\n", GRAPHcc(G));
}
#include <stdlib.h>
#include "GRAPH.h"
struct graph { int V; int E; int **adj; };
Graph GRAPHinit(int V)
{ Graph G = malloc(sizeof *G);
G->V = V; G->E = 0;
G->adj = MATRIXint(V, V, 0);
return G;
}
void GRAPHinsertE(Graph G, Edge e)
{ int v = e.v, w = e.w;
if (G->adj[v][w] == 0) G->E++;
G->adj[v][w] = 1;
G->adj[w][v] = 1;
}
void GRAPHremoveE(Graph G, Edge e)
{ int v = e.v, w = e.w;
if (G->adj[v][w] == 1) G->E--;
G->adj[v][w] = 0;
G->adj[w][v] = 0;
}
int GRAPHedges(Edge a[], Graph G)
{ int v, w, E = 0;
for (v = 0; v < G->V; v++)
for (w = v+1; w < G->V; w++)
if (G->adj[v][w] == 1)
a[E++] = EDGE(v, w);
return E;
}
GRAPH PROPERTIES AND TYPES §17.3 23
0
01 1 00 110 00 000
be 0 otherwise. Program 17.3 is an implementation of the graph ADT 1
10 0 00 000 00 000
that uses a direct representation of this matrix. The implementation 2
10 0 00 000 00 000
maintains a two-dimensional array of integers with the entry a[v][w] 3
00 0 01 100 00 000
set to 1 if there is an edge connecting v and w in the graph, and set to 0 4
00 0 10 110 00 000
otherwise. In an undirected graph, each edge is actually represented by 5
10 0 11 000 00 000
two entries: the edge v-w is represented by 1 values in both a[v][w] 6
10 0 01 000 00 000
7
and a[w][v], as is the edge w-v. 00 0 00 000 10 000
8
As mentioned in Section 17.2, we generally assume that the num- 00 0 00 001 00 000
9
ber of vertices is known to the client when the graph is initialized. For 00 0 00 000 00 111
10
many applications, we might set the number of vertices as a compile- 00 0 00 000 01 000
11
time constant and use statically allocated arrays, but Program 17.3 00 0 00 000 01 001
12
takes the slightly more general approach of allocating dynamically the 00 0 00 000 01 010
void GRAPHshow(Graph G)
{ int i, j;
printf("%d vertices, %d edges\n", G->V, G->E);
for (i = 0; i < G->V; i++)
{
printf("%2d:", i);
for (j = 0; j < G->V; j++)
if (G->adj[i][j] == 1) printf(" %2d", j);
printf("\n");
}
}
given a vertex, we can immediately access its list; we use linked lists so
that we can add new edges in constant time. Figure 17.10
Adjacency-lists data structure
Program 17.6 is an implementation of the ADT interface in Pro-
This figure depicts a representa-
gram 17.1 that is based on this approach, and Figure 17.10 depicts an
tion of the graph in Figure 17.1 as
example. To add an edge connecting v and w to this representation an array of linked lists. The space
of the graph, we add w to v’s adjacency list and v to w’s adjacency used is proportional to the number
list. In this way, we still can add new edges in constant time, but the of nodes plus the number of edges.
To find the indices of the vertices
total amount of space that we use is proportional to the number of
connected to a given vertex v , we
vertices plus the number of edges (as opposed to the number of vertices look at the v th position in an ar-
squared, for the adjacency-matrix representation). We again represent ray, which contains a pointer to a
each edge in two different places: an edge connecting v and w is rep- linked list containing one node for
each vertex connected to v . The
resented as nodes on both adjacency lists. It is important to include
order in which the nodes appear
both; otherwise, we could not answer efficiently simple questions such on the lists depends on the method
as, “Which vertices are connected directly to vertex v?” that we use to construct the lists.
28 §17.4 CHAPTER SEVENTEEN
#include <stdlib.h>
#include "GRAPH.h"
typedef struct node *link;
struct node { int v; link next; };
struct graph { int V; int E; link *adj; };
link NEW(int v, link next)
{ link x = malloc(sizeof *x);
x->v = v; x->next = next;
return x;
}
Graph GRAPHinit(int V)
{ int v;
Graph G = malloc(sizeof *G);
G->V = V; G->E = 0;
G->adj = malloc(V*sizeof(link));
for (v = 0; v < V; v++) G->adj[v] = NULL;
return G;
}
void GRAPHinsertE(Graph G, Edge e)
{ int v = e.v, w = e.w;
G->adj[v] = NEW(w, G->adj[v]);
G->adj[w] = NEW(v, G->adj[w]);
G->E++;
}
int GRAPHedges(Edge a[], Graph G)
{ int v, E = 0; link t;
for (v = 0; v < G->V; v++)
for (t = G->adj[v]; t != NULL; t = t->next)
if (v < t->v) a[E++] = EDGE(v, t->v);
return E;
}
GRAPH PROPERTIES AND TYPES §17.4 29
◦ 17.33 Add a function declaration to the graph ADT (Program 17.1) that
removes self-loops and parallel edges. Provide the trivial implementation
of this function for the adjacency-matrix–based ADT implementation (Pro-
gram 17.3), and provide an implementation of the function for the adjacency- 0 1 2 3 4 5 6 7 8 9 10 11 12
list–based ADT implementation (Program 17.6) that uses time proportional 0 1 1 1 0 0 1 1 0 0 0 0 0 0
to E and extra space proportional to V . 1 0 1 0 0 0 0 0 0 0 0 0 0 0
2 0 0 1 0 0 0 0 0 0 0 0 0 0
17.34 Extend your solution to Exercise 17.33 to also remove degree-0 (iso- 3 0 0 0 1 0 0 0 0 0 0 0 0 0
lated) vertices. Note: To remove vertices, you need to rename the other vertices 4 0 0 0 1 1 0 0 0 0 0 0 0 0
and rebuild the data structures—you should do so just once. 5 0 0 0 1 1 1 0 0 0 0 0 0 0
6 0 0 0 0 1 0 1 0 0 0 0 0 0
• 17.35 Write an ADT function for the adjacency-lists representation (Pro- 7 0 0 0 0 0 0 0 1 1 0 0 0 0
gram 17.6) that collapses paths that consist solely of degree-2 vertices. Specif- 8 0 0 0 0 0 0 0 0 1 0 0 0 0
ically, every degree-2 vertex in a graph with no parallel edges appears on some 9 0 0 0 0 0 0 0 0 0 1 1 1 1
10 0 0 0 0 0 0 0 0 0 0 1 0 0
path u-...-w where u and w are either equal or not of degree 2. Replace
11 0 0 0 0 0 0 0 0 0 0 0 1 1
any such path with u-w and then remove all unused degree-2 vertices as in 12 0 0 0 0 0 0 0 0 0 0 0 0 1
Exercise 17.34. Note: This operation may introduce self-loops and parallel
edges, but it preserves the degrees of vertices that are not removed.
17.36 Give a (multi)graph that could result from applying the transformation 0
described in Exercise 17.35 on the sample graph in Figure 17.1. 5 2 1 6
1
2
space E V2 V +E
initialize empty 1 V2 V
copy E V2 E
destroy 1 V E
insert edge 1 1 1
find/remove edge E 1 V
is v isolated? E V 1
path from u to v? E lg∗ V V2 V +E
programs that we consider differ only in the way that such abstract
operations are implemented.
Why not develop these algorithms at a higher level of abstraction,
then discuss different options for representing the data and implement-
ing the associated operations, as we have done in so many instances
throughout this book? The answer to this question is not clearcut. Of-
ten, we are presented with a clear choice between adjacency lists, for
sparse graphs, and adjacency matrices, for dense graphs. We choose
to work directly with one of these two particular representations in
our primary implementations because the performance characteristics
of the algorithms are clearly exposed in code that works with the low-
level representation, and that code is generally no more difficult to read
and understand than code written in terms of higher-level abstractions.
In some instances, our algorithm-design decisions depend on cer-
tain properties of the representation. Working at a higher level of
abstraction might obscure our knowledge of that dependence. If we
know that one representation would lead to poor performance but
another would not, we would be taking an unnecessary risk were we
to consider the algorithm at the wrong level of abstraction. As usual,
our goal is to craft implementations such that we can make precise
statements about performance.
It is possible to address these issues with a rigorous abstract
approach, where we build layers of abstraction that culminate in the
abstract operations that we need for our algorithms. Adding an ADT
operation to test for the existence of an edge is one example (see
Exercise 17.19), and we could also arrange to have representation-
independent code for processing each of the vertices adjacent to a given
vertex (see Exercise 17.60). Such an approach is worthwhile in many
situations. In this book, however, we keep one eye on performance by
using code that directly accesses adjacency matrices when working with
dense graphs and adjacency lists when working with sparse graphs,
augmenting the basic structures as appropriate to suit the task at hand.
All of the operations that we have considered so far are simple,
albeit necessary, data-processing functions; and the essential net re-
sult of the discussion in this section is that basic algorithms and data
structures from Parts 1 through 3 are effective for handling them. As
we develop more sophisticated graph-processing algorithms, we face
more difficult challenges in finding the best implementations for spe-
GRAPH PROPERTIES AND TYPES §17.5 37
◦ 17.38 Why not use a direct representation for graphs (a data structure that
models the graph exactly, with vertices represented as allocated records and
edge lists containing links to vertices instead of vertex names)?
17.39 Write a representation-independent ADT function that returns a pointer
to a vertex-indexed array giving the degree of each vertex in the graph. Hint:
Use GRAPHedges.
17.40 Modify the adjacency-matrix ADT implementation (Program 17.3) to
include in the graph representation a vertex-indexed array that holds the degree
of each vertex. Add an ADT function GRAPHdeg that returns the degree of a
given vertex.
17.41 Do Exercise 17.40 for the adjacency-lists representation.
17.42 Give a row to add to Table 17.1 for the problem of determining the
number of isolated vertices in a graph. Support your answer with function
implementations for each of the three representations.
◦ 17.43 Give a row to add to Table 17.1 for the problem of determining whether
a given digraph has a vertex with indegree V and outdegree 0. Support your
answer with function implementations for each of the three representations.
Note: Your entry for the adjacency-matrix representation should be V .
17.44 Use doubly-linked adjacency lists with cross links as described in the
text to implement a constant-time remove edge function GRAPHremoveE for the
adjacency-lists graph ADT implementation (Program 17.6).
17.45 Add a remove vertex function GRAPHremoveV to the doubly-linked
adjacency-lists graph ADT implementation described in the previous exercise.
◦ 17.46 Modify your solution to Exercise 17.15 to use a dynamic hash table,
as described in the text, such that insert edge and remove edge take constant
amortized time.
GRAPH PROPERTIES AND TYPES §17.5 39
int randV(Graph G)
{ return G->V * (rand() / (RAND_MAX + 1.0)); }
Graph GRAPHrand(int V, int E)
{ Graph G = GRAPHinit(V);
while (G->E < E)
GRAPHinsertE(G, EDGE(randV(G), randV(G)));
return G;
}
For graphs, the latter goal is more elusive than for other domains that
we have considered, although it is still a worthwhile objective. We
shall encounter various different models of randomness, starting with
these two:
Random edges This model is simple to implement, as indicated
by the generator given in Program 17.7. For a given number of vertices
V , we generate random edges by generating pairs of numbers between Figure 17.12
Two random graphs
0 and V − 1. The result is likely to be a random multigraph with self-
loops, rather than a graph as defined in Definition 17.1. A given pair Both of these random graphs have
50 vertices. The sparse graph at
could have two identical numbers (hence, self-loops could occur); and the top has 50 edges, while the
any pair could be repeated multiple times (hence, parallel edges could dense graph at the bottom has
occur). Program 17.7 generates edges until the graph is known to have 500 edges. The sparse graph is
E edges, and leaves to the implementation the decision of whether to not connected, with each vertex
connected only to a few others; the
eliminate parallel edges. If parallel edges are eliminated, the number of dense graph is certainly connected,
edges generated is substantially higher than then number of edges used with each vertex connected to 20
(E) for dense graphs (see Exercise 17.62); so this method is normally others, on the average. These di-
used for sparse graphs. agrams also indicate the difficulty
of developing algorithms that can
Random graph The classic mathematical model for random draw arbitrary graphs (the vertices
graphs is to consider all possible edges and to include each in the here are placed in random posi-
graph with a fixed probability p. If we want the expected number tion).
42 §17.6 CHAPTER SEVENTEEN
vertex defined for each phone number, and an edge for each pair i and
j with the property that i made a telephone call to j within some fixed
900-435-5100 201-332-4562 period. This set of edges represents a huge multigraph. It is certainly
415-345-3030 757-995-5030
757-310-4313 201-332-4562
sparse, since each person places calls to only a tiny fraction of the
747-511-4562 609-445-3260 available telephones. It is representative of many other applications.
900-332-3162 212-435-3562 For example, a financial institution’s credit card and merchant account
617-945-2152 408-310-4150 records might have similar information.
757-995-5030 757-310-4313
212-435-3562 803-568-8358 Function call graph We can associate a graph with any com-
913-410-3262 212-435-3562 puter program with functions as vertices and an edge connecting X
401-212-4152 907-618-9999 and Y whenever function X calls function Y . We can instrument the
201-232-2422 415-345-3120 program to create such a graph (or have a compiler do it). Two com-
913-495-1030 802-935-5112
609-445-3260 415-345-3120 pletely different graphs are of interest: the static version, where we
201-310-3100 415-345-3120 create edges at compile time corresponding to the function calls that
408-310-4150 802-935-5113 appear in the program text of each function; and a dynamic version,
708-332-4353 803-777-5834 where we create edges at run time when the calls actually happen. We
413-332-3562 905-828-8089
815-895-8155 208-971-0020 use static function call graphs to study program structure and dynamic
802-935-5115 408-310-4150 ones to study program behavior. These graphs are typically huge and
708-410-5032 212-435-3562 sparse.
201-332-4562 408-310-4150 In applications such as these, we face massive amounts of data,
815-511-3032 201-332-4562
301-292-3162 505-709-8080 so we might prefer to study the performance of algorithms on real
617-833-2425 208-907-9098 sample data rather than on random models. We might choose to try
800-934-5030 408-310-4150 to avoid degenerate situations by randomly ordering the edges, or by
408-982-3100 201-332-4562 introducing randomness in the decision making in our algorithms, but
415-345-3120 905-569-1313
413-435-4313 415-345-3120 that is a different matter from generating a random graph. Indeed, in
747-232-8323 408-310-4150 many applications, learning the properties of the graph structure is a
802-995-1115 908-922-2239 goal in itself.
Figure 17.14 In several of these examples, vertices are natural named objects,
Transaction graph and edges appear as pairs of named objects. For example, a transaction
A sequence of pairs of numbers graph might be built from a sequence of pairs of telephone numbers,
like this one might represent a and a Euclidean graph might be built from a sequence of pairs of
list of telephone calls in a local cities or towns. Program 17.9 is a client program that we can use to
exchange, or financial transfers build a graph in this common situation. For the client’s convenience,
between accounts, or any sim-
ilar situation involving transac- it takes the set of edges as defining the graph, and deduces the set of
tions between entities with unique vertex names from their use in edges. Specifically, the program reads a
identifiers. The graphs are hardly sequence of pairs of symbols from standard input, uses a symbol table
random—some phones are far
to associate the vertex numbers 0 to V − 1 to the symbols (where V is
more heavily used than others and
some accounts are far more active the number of different symbols in the input), and builds a graph by
than others. inserting the edges, as in Programs 17.7 and 17.8. We could adapt any
GRAPH PROPERTIES AND TYPES §17.6 45
#include <stdio.h>
#include "GRAPH.h"
#include "ST.h"
Graph GRAPHscan(int Vmax, int Emax)
{ char v[100], w[100];
Graph G = GRAPHinit(Vmax);
STinit();
while (scanf("%99s %99s", v, w) == 2)
GRAPHinsertE(G, EDGE(STindex(v), STindex(w)));
return G;
}
Figure 17.16 17.64 Write a client program that generates sparse random graphs for a well-
de Bruijn graphs chosen set of values of V and E, and prints the amount of space that it used
for the graph representation and the amount of time that it took to build it.
A de Bruijn digraph of order n has Test your program with a sparse-graph ADT implementation (Program 17.6)
2n vertices with edges from i to and with the random-graph generator (Program 17.7), so that you can do
2i mod n and (2i + 1) mod n, for meaningful empirical tests on graphs drawn from this model.
all i. Pictured here are the under-
lying undirected de Bruijn graphs 17.65 Write a client program that generates dense random graphs for a well-
of order 6, 5, 4, and 3 (top to bot- chosen set of values of V and E, and prints the amount of space that it used
tom). for the graph representation and the amount of time that it took to build it.
GRAPH PROPERTIES AND TYPES §17.6 49
in Exercise 17.64 (for low densities) and as described in Exercise 17.65 (for
high densities).
◦ 17.79 Design an appropriate extension that allows you to use Program 17.1
to build separation graphs without having to call a function for each implied
edge. That is, the number of function calls required to build a graph should
be proportional to the sum of the sizes of the groups. (Implementations of
graph-processing functions are left with the problem of efficiently handling
implied edges.)
17.80 Give a tight upper bound on the number of edges in any separation
graph with N different groups of k people.
17.81 Draw graphs in the style of Figure 17.16 that, for V = 8, 16, and 32,
have V vertices numbered from 0 to V − 1 and an edge connecting each vertex
i with i/2.
17.82 Modify the ADT interface in Program 17.1 to allow clients to use
symbolic vertex names and edges to be pairs of instances of a generic Vertex
type. Hide the vertex-index representation and the symbol-table ADT usage
completely from clients.
17.83 Add a function to the ADT interface from Exercise 17.82 that supports
a join operation for graphs, and provide implementations for the adjacency-
matrix and adjacency-lists representations. Note: Any vertex or edge in either
graph should be in the join, but vertices that are in both graphs appear only
once in the join, and you should remove parallel edges.
0
Program 17.12 Hamilton path
6
1 2 The function GRAPHpathH searches for a Hamilton path from v to w. It
uses a recursive function that differs from the one in Program 17.11 in
3 just two respects: First, it returns successfully only if it finds a path of
4 length V; second, it resets the visited marker before returning unsuc-
5 cessfully. Do not expect this function to finish except for tiny graphs
(see text).
0-1
1-2 static int visited[maxV];
2-3 int pathR(Graph G, int v, int w, int d)
3-4 { int t;
4-5 if (v == w)
4-6
2-4 { if (d == 0) return 1; else return 0; }
4-3 visited[v] = 1;
4-5 for (t = 0; t < G->V; t++)
4-6 if (G->adj[v][t] == 1)
0-2
2-1
if (visited[t] == 0)
2-3 if (pathR(G, t, w, d-1)) return 1;
3-4 visited[v] = 0;
4-5 return 0;
4-6
}
2-4
4-3 int GRAPHpathH(Graph G, int v, int w)
4-5 { int t;
4-6 for (t = 0; t < G->V; t++) visited[t] = 0;
0-5
return pathR(G, v, w, G->V-1);
5-4
4-2 }
2-1
2-3
4-3 Hamilton tour problem. Is there a cycle that visits every vertex in the
3-2 graph exactly once?
2-1 At first blush, this problem seems to admit a simple solution: We
4-6
can write down the simple recursive program for finding a Hamilton
0-6
path that is shown in Program 17.12. But this program is not likely
Figure 17.19 to be useful for many graphs, because its worst-case running time is
Hamilton-tour–search trace exponential in the number of vertices in the graph.
This trace shows the edges checked
by Program 17.12 when discover- Property 17.3 A recursive search for a Hamilton tour could take
ing that the graph shown at the top exponential time.
has no Hamilton tour. For brevity,
edges to marked vertices are omit- Proof : Consider a graph where vertex V-1 is isolated and the edges,
ted. with the other V − 1 vertices, constitute a complete graph. Pro-
GRAPH PROPERTIES AND TYPES §17.7 55
gram 17.12 will never find a Hamilton path, but it is easy to see by
induction that it will examine all of the (V − 1)! paths in the complete
graph, all of which involve V − 1 recursive calls. The total number of
recursive calls is therefore about V !, or about (V /e)V , which is higher
than any constant to the V th power.
Our implementations Program 17.11 for finding simple paths and
Program 17.12 for finding Hamilton paths are extremely similar, and
both programs terminate when all the elements of the visited array
are set to 1. Why are the running times so dramatically different?
Program 17.11 is guaranteed to finish quickly because it sets at least
one element of the visited array to 1 each time pathR is called.
Program 17.12, on the other hand, can set visited elements back to
0, so we cannot guarantee that it will finish quickly.
When searching for simple paths, in Program 17.11, we know
that, if there is a path from v to w, we will find it by taking one of
the edges v-t from v, and the same is true for Hamilton paths. But
there this similarity ends. If we cannot find a simple path from t to
w, then we can conclude that there is no simple path from v to w that
goes through t; but the analogous situation for Hamilton paths does
not hold. It could be that case that there is no Hamilton path to w
that starts with v-t, but there is one that starts with v-x-t for some
other vertex x. We have to make a recursive call from t corresponding
to every path that leads to it from v. In short, we may have to check
every path in the graph.
It is worthwhile to reflect on just how slow a factorial-time algo-
rithm is. If we could process a graph with 15 vertices in 1 second, it
would take 1 day to process a graph with 19 vertices, over 1 year for
21 vertices, and over 6 centuries for 23 vertices. Faster computers do
not help much, either. A computer that is 200,000 times faster than
our original one would still take more than a day to solve that 23-
vertex problem. The cost to process graphs with 100 or 1000 vertices
is too high to contemplate, let alone graphs of the size that we might
encounter in practice. It would take millions of pages in this book just
to write down the number of centuries required to process a graph that
contained millions of vertices.
In Chapter 5, we examined a number of simple recursive pro-
grams that are similar in character to Program 17.12 but that could
be drastically improved with top-down dynamic programming. This
56 §17.7 CHAPTER SEVENTEEN
0
Program 17.13 Euler path existence
6
1 2 This function, which is based upon the corollary to Property 17.4, tests
whether there is an Euler path from v to w in a connected graph, using
3 the GRAPHdeg ADT function from Exercise 17.40. It takes time propor-
4 tional to V , not including preprocessing time to check connectivity and
5 to build the vertex-degree table used by GRAPHdeg.
1-0-2-1
int GRAPHpathE(Graph G, int v, int w)
0
{ int t;
6 t = GRAPHdeg(G, v) + GRAPHdeg(G, w);
1 2 if ((t % 2) != 0) return 0;
for (t = 0; t < G->V; t++)
3 if ((t != v) && (t != w))
4
if ((GRAPHdeg(G, t) % 2) != 0) return 0;
5
return 1;
6-0-1-2-4-6
}
0
6
1 2
Proof : This statement is equivalent to Property 17.4 in the graph
formed by adding an edge connecting the two vertices of odd degree
3 (the ends of the path).
4
5 Therefore, for example, there is no way for anyone to traverse
2-0-1-2-3-4-2
all the bridges of Königsberg in a continuous path without retracing
their steps, since all four vertices in the corresponding graph have odd
0 degree (see Figure 17.21).
6 As discussed in Section 17.5, we can find all the vertex degrees
1 2
in time proportional to E for the adjacency-lists or set-of-edges rep-
3
resentation and in time proportional to V 2 for the adjacency-matrix
4 representation, or we can maintain a vertex-indexed array with ver-
5 tex degrees as part of the graph representation (see Exercise 17.40).
0-6-4-5-0-2-1-0 Given the array, we can check whether Property 17.4 is satisfied in
time proportional to V . Program 17.13 implements this strategy, and
Figure 17.22 demonstrates that determining whether a given graph has a Euler path
Partial tours is an easy computational problem. This fact is significant because we
Following edges from any vertex have little intuition to suggest that the problem should be easier than
in a graph that has an Euler tour determining whether a given graph has a Hamilton path.
always takes us back to that vertex,
as shown in these examples. The Now, suppose that we actually wish to find a Euler path. We are
cycle does not necessarily use all treading on thin ice because a direct recursive implementation (find
the edges in the graph. a path by trying an edge, then making a recursive call to find a path
GRAPH PROPERTIES AND TYPES §17.7 59
#include "STACK.h"
int path(Graph G, int v)
{ int w;
for (; G->adj[v] != NULL; v = w)
{
STACKpush(v);
w = G->adj[v]->v;
GRAPHremoveE(G, EDGE(v, w));
}
return v;
}
void pathEshow(Graph G, int v, int w)
{
STACKinit(G->E);
printf("%d", w);
while ((path(G, v) == v) && !STACKempty())
{ v = STACKpop(); printf("-%d", v); }
printf("\n");
}
for the rest of the graph) will have the same kind of factorial-time
performance as Program 17.12. We expect not to have to live with
such performance because it is so easy to test whether a path exists,
so we seek a better algorithm. It is possible to avoid factorial-time
blowup with a fixed-cost test for determining whether or not to use an
edge (rather than unknown costs from the recursive call), but we leave
this approach as an exercise (see Exercises 17.96 and 17.97).
Another approach is suggested by the proof of Property 17.4.
Traverse a cyclic path, deleting the edges encountered and pushing
onto a stack the vertices that it encounters, so that (i) we can trace
60 §17.7 CHAPTER SEVENTEEN
0: 2 5 6 0 0: 6 0
Figure 17.23 6 6
1: 2 1:
Euler tour by removing cycles
2: 0 3 4 1 1 2 2: 3 4 1 2
This figure shows how Pro- 3: 4 2 3: 4 2
gram 17.14 discovers an Euler 4: 6 5 3 2 3 4: 3 2 3
tour from 0 back to 0 in a sample 5: 4 0 4 5: 4
graph. Thick black edges are those 6: 4 0 5 6: 0 5
on the tour, the stack contents are
1 1 2 0 5 4 6
listed below each diagram, and ad-
jacency lists for the non-tour edges
are shown at left. 0: 2 5 6 0 0: 0
First, the program adds the 1: 6 1: 6
edge 0-1 to the tour and removes 2: 0 3 4 1 2 2: 3 4 1 2
it from the adjacency lists (in two 3: 4 2 3: 4 2
places) (top left, lists at left). Sec- 4: 6 5 3 2 3 4: 3 2 3
ond, it adds 1-2 to the tour in 5: 4 0 4 5: 4
the same way (left, second from 6: 4 0 5 6: 5
top). Next, it winds up back at 0 1 2 1 2 0 5 4 6 0
but continues to do another cycle
0-5-4-6-0, winding up back at 0 0: 5 6 0 0: 0
with no more edges incident upon 1: 6 1: 6
0 (right, second from top). Then 2: 3 4 2: 3 4
1 2 1 2
it pops the isolated vertices 0 and 3: 4 2 3: 2
6 from the stack until 4 is at the 4: 6 5 3 2 4: 2
3 3
top and starts a tour from 4 (right, 5: 4 0 5:
4 4
third from from top), which takes 6: 4 0 6:
5 5
it to 3, 2, and back to 4, where-
upon it pops all the now-isolated 1 2 0 1 2 0 5 4 3
vertices 4, 2, 3, and so forth. The
sequence of vertices popped from 0: 6 0 0: 0
the stack defines the Euler tour 1: 6 1: 6
0-6-4-2-3-4-5-0-2-1-0 of the 2: 3 4 1 2 2: 4 1 2
whole graph. 3: 4 2 3:
4: 6 5 3 2 3 4: 2 3
5: 4 4 5: 4
6: 4 0 6:
5 5
1 2 0 5 1 2 0 5 4 3 2
0: 6 0 0: 0
1: 6 1: 6
2: 3 4 1 2 2: 1 2
3: 4 2 3:
4: 6 3 2 3 4: 3
5: 5:
4 4
6: 4 0 6:
5 5
1 2 0 5 4 1 2 0 5 4 3 2 4
GRAPH PROPERTIES AND TYPES §17.7 61
back along that path, printing out its edges, and (ii) we can check each
vertex for additional side paths (which can be spliced into the main
path). This process is illustrated in Figure 17.23.
Program 17.14 is an implementation along these lines, for an
adjacency-lists graph ADT. It assumes that a Euler path exists, and it
destroys the graph representation; so the client has responsibility to
use GRAPHpathE, GRAPHcopy, and GRAPHdestroy as appropriate. The
code is tricky—novices may wish to postpone trying to understand it
until gaining more experience with graph-processing algorithms in the
next few chapters. Our purpose in including it here is to illustrate that
good algorithms and clever implementations can be very effective for
solving some graph-processing problems.
Property 17.6 We can find a Euler tour in a graph, if one exists, in
linear time.
We leave a full induction proof as an exercise (see Exercise 17.101).
Informally, after the first call on path, the stack contains a path from
v to w, and the graph that remains (after removal of isolated vertices)
consists of the smaller connected components (sharing at least one
vertex with the path so far found) that also have Euler tours. We pop
isolated vertices from the stack and use path to find Euler tours that
contain the nonisolated vertices, in the same manner. Each edge in the
graph is pushed onto (and popped from) the stack exactly once, so the
total running time is proportional to E.
Despite their appeal as a systematic way to traverse all the edges
and vertices, we rarely use Euler tours in practice because few graphs
have them. Instead, we typically use depth-first search to explore
graphs, as described in detail in Chapter 18. Indeed, as we shall see,
doing depth-first search in an undirected graph amounts to computing
a two-way Euler tour: a path that traverses each edge exactly twice,
once in each direction.
In summary, we have seen in this section that it is easy to find
simple paths in graphs, that it is even easier to know whether we can
tour all the edges of a large graph without revisiting any of them (by
just checking that all vertex degrees are even), and that there is even a
clever algorithm to find such a tour; but that it is practically impossible
to know whether we can tour all the graph’s vertices without revisiting
any. We have simple recursive solutions to all these problems, but the
62 §17.7 CHAPTER SEVENTEEN
Exercises
17.84 Show, in the style of Figure 17.17, the trace of recursive calls (and
vertices that are skipped), when Program 17.11 finds a path from 0 to 5 in
the graph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
17.85 Modify the recursive function in Program 17.11 to print out a trace
like Figure 17.17, using a global variable as described in the text.
◦ 17.88 Modify Program 17.11 such that it takes a third argument d and
tests the existence of a path connecting u and v of length greater than d. In
particular, GRAPHpath(v, v, 2) should be nonzero if and only if v is on a
cycle.
◦ 17.91 Consider the graphs defined by the following four sets of edges:
0-1 0-2 0-3 1-3 1-4 2-5 2-9 3-6 4-7 4-8 5-8 5-9 6-7 6-9 7-8
0-1 0-2 0-3 1-3 0-3 2-5 5-6 3-6 4-7 4-8 5-8 5-9 6-7 6-9 8-8
0-1 1-2 1-3 0-3 0-4 2-5 2-9 3-6 4-7 4-8 5-8 5-9 6-7 6-9 7-8
4-1 7-9 6-2 7-3 5-0 0-2 0-8 1-6 3-9 6-3 2-8 1-5 9-8 4-5 4-7
Which of these graphs have Euler tours? Which of them have Hamilton tours?
◦ 17.92 Give necessary and sufficient conditions for a directed graph to have
a (directed) Euler tour.
17.93 Prove that every connected undirected graph has a two-way Euler
tour.
17.94 Modify the proof of Property 17.4 to make it work for graphs with
parallel edges and self-loops.
17.95 Show that adding one more bridge could give a solution to the
bridges-of-Königsberg problem.
• 17.96 Prove that a connected graph has a Euler path from v to w only if
it has an edge incident on v whose removal does not disconnect the graph
(except possibly by isolating v).
• 17.97 Use Exercise 17.96 to develop an efficient recursive method for find-
ing a Euler tour in a graph that has one. Beyond the basic graph ADT
functions, you may use the ADT functions GRAPHdeg (see Exercise 17.40) and
GRAPHpath (see Program 17.11). Implement and test your program for the
adjacency-matrix graph representation.
17.98 Develop a representation-independent version of Program 17.14 that
uses a general interface for visiting all edges adjacent to a given vertex (see
Exercise 17.60). Note: Be careful of interactions between your code and the
GRAPHremoveE function. Make sure that your implementation works properly
in the presence of parallel edges and self-loops.
17.99 Give an example where the graph remaining after the first call to
path in Program 17.14 is not connected (in a graph that has a Euler tour).
17.100 Describe how to modify Program 17.14 so that it can be used to
detect whether or not a given graph has a Euler tour, in linear time.
17.101 Give a complete proof by induction that the linear-time Euler tour
algorithm described in the text and implemented in Program 17.14 properly
finds a Euler tour.
◦ 17.102 Find the number of V -vertex graphs that have a Euler tour, for as
large a value of V as you can feasibly afford to do the computation.
• 17.103 Run experiments to determine empirically the average length of the
path found by the first call to path in Program 17.14 for various graphs (see
Exercises 17.63–76). Calculate the probability that this path is cyclic.
64 §17.8 CHAPTER SEVENTEEN
◦ 17.106 Modify Program 17.12 to print out the Hamilton tour if it finds one.
• 17.107 Find a Hamilton tour of the graph
1-2 5-2 4-2 2-6 0-8 3-0 1-3 3-6 1-0 1-4 4-0 4-6 6-5 2-6
6-9 9-0 3-1 4-3 9-2 4-9 6-9 7-9 5-0 9-7 7-3 4-5 0-5 7-8
or show that none exists.
•• 17.108 Determine how many V -vertex graphs have a Hamilton tour, for as
large a value of V as you can feasibly afford to do the computation.
vertices anywhere, so we can solve this problem for many graphs, but
it is impossible to solve for many other graphs. A remarkable classical
result known as Kuratowski’s theorem provides an easy test for de-
termining whether a graph is planar: it says that the only graphs that
cannot be drawn with no edge intersections are those that contain some
subgraph that, after removing vertices of degree 2, is isomorphic to
one of the graphs in Figure 17.24. A straightforward implementation
of that test, even without taking the vertices of degree 2 into consid-
eration, would be too slow for large graphs (see Exercise 17.111), but
in 1974 R. Tarjan developed an ingenious (but intricate) algorithm for
solving the problem in linear time, using a depth-first search scheme
that extends those that we consider in Chapter 18. Tarjan’s algorithm
does not necessarily give a practical layout; it just certifies that a layout
exists. As discussed in Section 17.1, developing a visually pleasing lay-
out in applications where vertices do not necessarily relate directly to
the physical world has turned out to be a challenging research problem.
Matching Given a graph, what is the largest subset of its edges
with the property that no two are connected to the same vertex? This
classical problem is known to be solvable in time proportional to a 1
polynomial function of V and E, but a fast algorithm that is suitable 2
for huge graphs is still an elusive research goal. The problem is easier
0
to solve when restricted in various ways. For example, the problem of
matching students to available positions in selective institutions is an 3
4
example of bipartite matching: We have two different types of vertices
(students and institutions) and we are concerned with only those edges
0 1 2
that connect a vertex of one type with a vertex of the other type. We
see a solution to this problem in Chapter 22.
The solutions to some tractable problems have never been written 3 4 5
down as programs, or have running times so high that we could not
contemplate using them in practice. The following example is in this
Figure 17.24
class. It also demonstrates the capricious nature of the mathematical Forbidden subgraphs in pla-
reality of the difficulty of graph processing. nar graphs
Even cycles in digraphs Does a given digraph have a cycle Neither of these graphs can be
of even length? This question would seem simple to resolve because drawn in the plane without inter-
the corresponding question for undirected graphs is easy to solve (see secting edges, nor can any graph
that contains either of these graphs
Section 18.4), as is the question of whether a digraph has a cycle of odd as a subgraph (after we remove
length. However, for many years, the problem was not sufficiently well vertices of degree two); but all
understood for us even to know whether or not there exists an efficient other graphs can be so drawn.
68 §17.8 CHAPTER SEVENTEEN
tween those results and the properties of graphs that arise in practice
are little understood. The development of general schemes such as the
network-simplex algorithm has been an extremely effective approach
to dealing with such problems.
An intractable graph-processing problem is one for which there
is no known algorithm that is guaranteed to solve the problem in a rea-
sonable amount of time. Many such problems have the characteristic
that we can use a brute-force method where we can try all possibilities
to compute the solution—we consider them to be intractable because
there are far too many possibilities to consider. This class of problems
is extensive, and includes many important problems that we would
like to know how to solve. The term NP-hard describes the prob-
lems in this class. Most experts believe that no efficient algorithms
exist for these problems. We consider the bases for this terminology
and this belief in more detail in Part 8. The Hamilton path problem
that we discussed in Section 17.7 is a prime example of an NP-hard
graph-processing problem, as are those on the following list.
Longest path What is the longest simple path connecting two
given vertices in a graph? Despite its apparent similarity to shortest-
paths problems, this problem is a version of the Hamilton tour prob-
lem, and is NP-hard.
Colorability Is there a way to assign one of k colors to each of
the vertices of a graph such that no edge connects two vertices of the
same color? This classical problem is easy for k = 2 (see Section 18.4),
but it is NP-hard for k = 3.
Independent set What is the size of the largest subset of the
vertices of a graph with the property that no two are connected by an
edge? Just as we saw when contrasting the Euler and Hamilton tour
problems, this problem is NP-hard, despite its apparent similarity to
the matching problem, which is solvable in polynomial time.
Clique What is size of the largest clique (complete subgraph) in
a given graph? This problem generalizes part of the planarity problem,
because if the largest clique has more than four nodes, the graph is not
planar.
These problems are formulated as existence problems—we are
asked to determine whether or not a particular subgraph exists. Some
of the problems ask for the size of the largest subgraph of a particular
type, which we can do by framing an existence problem where we test
70 §17.8 CHAPTER SEVENTEEN
This table summarizes the discussion in the text about the relative diffi-
culty of various classical graph-processing problems, comparing them in
rough subjective terms. These examples indicate not only the range of
difficulty of the problems but also that classifying a given problem can
be a challenging task.
E T I ?
undirected graphs
connectivity ∗
general connectivity ∗
Euler tour ∗
Hamilton tour ∗
bipartite matching ∗
maximum matching ∗
planarity ∗
maximum clique ∗
2-colorability ∗
3-colorability ∗
shortest paths ∗
longest paths ∗
vertex cover ∗
isomorphism ∗
digraphs
transitive closure ∗
strong connectivity ∗
odd-length cycle ∗
even-length cycle ∗
weighted graphs
minimum spanning tree ∗
traveling salesperson ∗
networks
shortest paths (nonnegative weights) ∗
shortest paths (negative weights) ∗
maximum flow ∗
assignment ∗
minimum-cost flow ∗
Key:
E Easy—efficient classical algorithm known (see reference)
T Tractable—solution exists (implementation difficult)
I Intractable—no efficient solution known (NP-hard)
? Unknown
GRAPH PROPERTIES AND TYPES §17.8 73
have a running time that depends on only the number of vertices and
edges, rather than on the graph structure; so we can concentrate on
streamlining implementations and still can predict performance with
confidence.
In summary, there is a wide spectrum of problems and algorithms
known for graph processing. Table 17.2 summarizes some of the in-
formation that we have discussed. Every problem comes in different
versions for different types of graphs (directed, weighted, bipartite,
planar, sparse, dense), and there are thousands of problems and algo-
rithms to consider. We certainly cannot expect to solve every problem
that we might encounter, and some problems that appear to be simple
are still baffling the experts. Despite a natural a priori expectation
that we should have no problem distinguishing easy problems from
intractable ones, the many examples that we have discussed illustrate
that placing a problem even into these rough categories can turn into
a significant research challenge.
As our knowledge about graphs and graph algorithms expands,
given problems may move among these categories. Despite a flurry of
research activity in the 1970s and intensive work by many researchers
since then, the possibility still remains that all the problems that we
are discussing will someday be categorized as “easy” (solvable by an
algorithm that is compact, efficient, and possibly ingenious).
Having developed this context, we shall press on to consider
numerous useful graph-processing algorithms. Problems that we can
solve do arise often, the graph algorithms that we study serve well in
a great variety of applications, and these algorithms serve as the basis
for attacking numerous other problems that we need to handle even if
we cannot guarantee efficient solutions.
Exercises
• 17.109 Prove that neither of the two graphs depicted in Figure 17.24 is
planar.
17.114 What is the size of the largest clique in a de Bruijn graph of order n?
CHAPTER EIGHTEEN
Graph Search
75
76 §18.1 CHAPTER EIGHTEEN
One trick for exploring a maze without getting lost that has been
known since antiquity (dating back at least to the legend of Theseus
and the Minotaur) is to unroll a ball of string behind us. The string
guarantees that we can always find a way out, but we are also interested
in being sure that we have explored every part of the maze, and we
do not want to retrace our steps unless we have to. To accomplish
these goals, we need some way to mark places that we have been. We
could use the string for this purpose as well, but we use an alternative
approach that models a computer implementation more closely.
We assume that there are lights, initially off, in every intersection,
and doors, initially closed, at both ends of every passage. We further
assume that the doors have windows and that the lights are sufficiently
strong and the passages sufficiently straight that we can determine,
by opening the door at one end of a passage, whether or not the
intersection at the other end is lit (even if the door at the other end is
closed). Our goals are to turn on all the lights and to open all the doors.
To reach them, we need a set of rules to follow, systematically. The
following maze-exploration strategy, which we refer to as Trémaux
exploration, has been known at least since the nineteenth century (see
reference section):
(i) If there are no closed doors at the current intersection, go to step
(iii). Otherwise, open any closed door to any passage leading out
of the current intersection (and leave it open).
(ii) If you can see that the intersection at the other end of that passage
is already lighted, try another door at the current intersection
(step (i)). Otherwise (if you can see that the intersection at the
other end of the passage is dark), follow the passage to that
intersection, unrolling the string as you go, turn on the light, and
go to step (i).
(iii) If all the doors at the current intersection are open, check whether
you are back at the start point. If so, stop. If not, use the string
to go back down the passage that brought you to this intersection
for the first time, rolling the string back up as you go, and look
for another closed door there (that is, return to step (i)).
Figures 18.2 and 18.3 depict a traversal of a sample graph and show
that, indeed, every light is lit and every door is opened for that ex-
ample. The figures depict just one of many possible outcomes of the
exploration, because we are free to open the doors in any order at each
78 §18.1 CHAPTER EIGHTEEN
Figure 18.2 0 2
0 2
Trémaux maze exploration
example 6
6
In this diagram, places that we 1 7
1 7
have not visited are shaded (dark) 3
3
and places that we have visited are
white (light). We assume that there 5 4
5 4
are lights in the intersections, and
that, when we have opened doors
into lighted intersections on both 0 2
0 2
ends of a passage, the passage is
lighted. To explore the maze, we 6
6
begin at 0 and take the passage to 1 7
2 (left, top). Then we proceed to 1 7
6, 4, 3 and 5, opening the doors to 3
3
the passages, lighting the intersec-
5 4
tions as we proceed through them, 5 4
and leaving a string trailing behind
us (left). When we open the door
0 2
that leads to 0 from 5, we can see 0 2
that 0 is lighted, so we skip that
passage (top right). Similarly, we 6
6
skip the passage from 5 to 4 (right, 1 7
1 7
second from top), leaving us with 3
nowhere to go from 5 except back 3
to 3 and then back to 4, rolling up 5 4
5 4
our ball of string. When we open
the doorway from the passage from
4 to 5, we can see that 5 is lighted 0 2
0 2
through the open door at the other
end, and we therefore skip that 6
passage (right, bottom). We never 6
1 7
walked down the passage connect- 1 7
ing 4 and 5, but we lighted it by 3
3
opening the doors at both ends.
5 4
5 4
0 2
0 2
6
6
1 7
1 7
3
3
5 4
5 4
GRAPH SEARCH §18.1 79
0 2
0 2 Figure 18.3
Trémaux maze exploration
6
6 example (continued)
1 7 Next, we proceed to 7 (top left),
1 7
3 open the door to see that 0 is
3
lighted (left, second from top),
5 4
5 4 and then proceed to 1 (left, third
from top). At this point, most of
the maze is traversed, and we use
0 2 our string to take us back to the
0 2
beginning, moving from 1 to 7 to 4
6 to 6 to 2 to 0. Back at 0, we com-
6
1 7 plete our exploration by checking
1 7 the passages to 5 (right, second
3 from bottom) and 7 (bottom right),
3
leaving all passages and intersec-
5 4
5 4 tions lighted. Again, the passages
connecting 0 to 5 and 0 to 7 are
both lighted because we opened
0 2
0 2 the doors at both ends, but we did
not walk through them.
6
6
1 7
1 7
3
3
5 4
5 4
0 2
0 2
6
6
1 7
1 7
3
3
5 4
5 4
0 2
0 2
6
6
1 7
1 7
3
3
5 4
5 4
80 §18.1 CHAPTER EIGHTEEN
From the detailed example in Figures 18.2 and 18.3, we see that
there are four different possible situations that arise for each passage
that we consider taking:
(i) The passage is dark, so we take it.
(ii) The passage is the one that we used to enter (it has our string in
Figure 18.4 it), so we use it to exit (and we roll up the string).
Decomposing a maze (iii) The door at the other end of the passage is closed (but the inter-
To prove by induction that section is lit), so we skip the passage.
Trémaux exploration takes us ev- (iv) The door at the other end of the passage is open (and the inter-
erywhere in a maze (top), we
break it into two smaller pieces, by section is lit), so we skip it.
removing all edges connecting the The first and second situations apply to any passage that we traverse,
first intersection with any intersec- first at one end and then at the other end. The third and fourth
tion that can be reached from the
situations apply to any passage that we skip, first at one end and
first passage without going back
through the first intersection (bot- then at the other end. Next, we see how this perspective on maze
tom). exploration translates directly to graph search.
GRAPH SEARCH §18.2 81
Exercises
18.1 Assume that intersections 6 and 7 (and all the hallways connected to
them) are removed from the maze in Figures 18.2 and 18.3, and a hallway
is added that connects 1 and 2. Show a Trémaux exploration of the resulting
maze, in the style of Figures 18.2 and 18.3.
◦ 18.2 Which of the following could not be the order in which lights are turned
on at the intersections during a Trémaux exploration of the maze depicted in
Figures 18.2 and 18.3?
0-7-4-5-3-1-6-2
0-2-6-4-3-7-1-5
0-5-3-4-7-1-6-2
0-7-4-6-2-1-3-5
• 18.3 How many different ways are there to traverse the maze depicted in
Figures 18.2 and 18.3 with a Trémaux exploration?
0 2
Program 18.1 Depth-first search (adjacency-matrix)
6 This code is intended for use with a generic graph-search ADT function
1 7 that initializes a counter cnt to 0 and all of the entries in the vertex-
3 indexed array pre to -1, then calls search once for each connected com-
ponent (see Program 18.3), assuming that the call search(G, EDGE(v,
5 4 v)) marks all vertices in the same connected component as v (by setting
their pre entries to be nonnegative).
Here, we implement search with a recursive function dfsR that
01234567 visits all the vertices connected to e.w by scanning through its row in
0-0 0******* the adjacency matrix and calling itself for each edge that leads to an
0-2 0*1***** unmarked vertex.
2-0
2-6 0*1***2* #define dfsR search
6-2 void dfsR(Graph G, Edge e)
6-4 0*1*3*2* { int t, w = e.w;
4-3 0*143*2*
3-4
pre[w] = cnt++;
3-5 0*14352* for (t = 0; t < G->V; t++)
5-0 if (G->adj[w][t] != 0)
5-3 if (pre[t] == -1)
5-4
dfsR(G, EDGE(w, t));
4-5
4-6 }
4-7 0*143526
7-0
7-1 07143526 and 18.3 (see also Figure 18.17). Figure 18.6 depicts the same process
1-7 using standard graph drawings.
7-4
0-5 These figures illustrate the dynamics of a recursive DFS and show
0-7 the correspondence with Trémaux exploration of a maze. First, the
vertex-indexed array corresponds to the lights in the intersections:
Figure 18.5
DFS trace When we encounter an edge to a vertex that we have already visited
(see a light at the end of the passage), we do not make a recursive
This trace shows the order in
which DFS checks the edges and call to follow that edge (go down that passage). Second, the function
vertices for the adjacency-matrix call–return mechanism in the program corresponds to the string in
representation of the graph corre- the maze: When we have processed all the edges adjacent to a vertex
sponding to the example in Fig- (explored all the passages leaving an intersection), we “return” (in
ures 18.2 and 18.3 (top) and traces
the contents of the pre array (right) both senses of the word).
as the search progresses (asterisks In the same way that we encounter each passage in the maze
represent -1, for unseen vertices). twice (once at each end), we encounter each edge in the graph twice
There are two lines in the trace for
(once at each of its vertices). In Trémaux exploration, we open the
every graph edge (once for each
orientation). Indentation indicates doors at each end of each passage; in DFS of an undirected graph, we
the level of recursion. check each of the two representations of each edge. If we encounter
GRAPH SEARCH §18.2 83
Figure 18.6
Depth-first search
0 2 0 2 0
0 These diagrams are a graphical
2
6 6
view of the process depicted in
1 7 1 7 6 Figure 18.5, showing the DFS
4
recursive-call tree as it evolves.
3 3
Thick black edges in the graph cor-
3 respond to edges in the DFS tree
5 4 5 4
shown to the right of each graph
diagram. Shaded edges are the
candidates to be added to the tree
next. In the early stages (left) the
tree grows down in a straight line,
0 as we make recursive calls for 0,
0 2 0 0 2
2, 6, and 4 (left). Then we make
2
6
2
6
recursive calls for 3, then 5 (right,
1 7 1 7
6 top two diagrams); and return from
4 those calls to make a recursive call
3 3
for 7 from 4 (right, second from
3
5 4 5 4 bottom) and to 1 from 7 (right, bot-
5 tom).
0
0 2 0 0 2
2
2
6 6
6
1 7 6 1 7
4
3 3
3 7
5 4 5 4
5
0
0 2 0 0 2
2
2
6 6
6
1 7 6 1 7
4
3 4 3
3 7
5 4 5 4
5 1
84 §18.2 CHAPTER EIGHTEEN
18.8 Show, in the style of Figure 18.7, a trace of the recursive function calls
made for a standard adjacency-lists DFS of the graph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
Figure 18.9 0 0 0
DFS tree representations
2 5 7 2 2
If we augment the DFS recursive-
call tree to represent edges that are 0 6 6 6
checked but not followed, we get
a complete description of the DFS 2 4 4 4
process (left). Each tree node has
3 5 6 7 3 7 3 7
a child representing each of the
nodes adjacent to it, in the order 4 5 0 1 4 5 0 1 5 1
they were considered by the DFS,
and a preorder traversal gives the 0 3 4 7 0 4
0 7 9 Figure 18.11
DFS forest
1 2 5 6 8 10 11 12
The DFS forest at the top repre-
0 0 0 3 4 7 9 9 12
sents a DFS of an adjacency-matrix
4 5 9 11 representation of the graph at the
3 5 6
bottom right. The graph has three
connected components, so the
0 4
forest has three trees. The pre
array is a preorder numbering of
0 the nodes in the tree (the order in
6 7 8 0 1 2 3 4 5 6 7 8 9 10 11 12 which they are examined by the
1 2 pre 0 1 2 4 5 3 6 7 8 9 10 11 12 DFS) and the st array is a parent-
st 0 0 0 5 3 0 4 7 7 9 9 9 11 link representation of the forest.
3 9 10 cc 0 0 0 0 0 0 0 1 1 2 2 2 2
4 The cc array associates each ver-
5 11 12 tex with a connected-component
index (see Program 18.4). As in
Figure 18.9, edges to circles are
tree edges; edges that go to squares
corresponds to preorder tree traversal. In Section 18.6, we examine are back edges; and shaded nodes
the graph-searching analog to level-order tree traversal and explore indicate that the incident edge was
encountered earlier in the search,
its relationship to DFS; in Section 18.7, we examine a general schema
in the other direction.
that encompasses any traversal method.
When traversing graphs, we have been assigning preorder num-
bers to the vertices in the order that we start processing them (just after
entering the recursive search function). We can also assign postorder
numbers to vertices, in the order that we finish processing them (just
before returning from the recursive search function). When processing
a graph, we do more than simply traverse the vertices—as we shall
see, the preorder and postorder numbering give us knowledge about
global graph properties that helps us to accomplish the task at hand.
Preorder numbering suffices for the algorithms that we consider in this
chapter, but we use postorder numbering in later chapters.
We describe the dynamics of DFS for a general undirected graph
with a DFS forest that has one DFS tree for each connected component.
An example of a DFS forest is illustrated in Figure 18.11.
With an adjacency-lists representation, we visit the edges con-
nected to each vertex in an order different from that for the adjacency-
matrix representation, so we get a different DFS forest, as illustrated
in Figure 18.12. DFS trees and forests are graph representations that
describe not only the dynamics of DFS but also the internal representa-
tion of the graph. For example, by reading the children of any node in
Figure 18.12 from left to right, we see the order in which they appear
96 §18.4 CHAPTER EIGHTEEN
Figure 18.12 0 7 9
Another DFS forest
5 2 1 6 8 11 10 12
This forest describes depth-first
search of the same graph as Fig- 0 4 3 0 0 7 9 12 9
18.15 Draw the DFS forest that results from a standard adjacency-lists DFS
of the graph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
Figure 18.13
Depth-first search
This figure illustrates the progress
of DFS in a random Euclidean
near-neighbor graph (left). The
figures show the DFS tree ver-
tices and edges in the graph as the
search progresses through 1/4, 1/2,
3/4, and all of the vertices (top to
bottom). The DFS tree (tree edges
only) is shown at the right. As is
evident from this example, the
search tree for DFS tends to be
quite tall and thin for this type of
graph (as it is for many other types
of graphs commonly encountered
in practice). We normally find a
vertex nearby that we have not
seen before.
GRAPH SEARCH §18.5 99
0 2
0 Figure 18.14
A two-way Euler tour
2 5 7
6 0 6
Depth-first search gives us a way
1 to explore any maze, traversing
7 2 4
both passages in each direction.
3 7 3 5 6
We modify Trémaux exploration
4 0 1 4 5 to take the string with us wherever
7 0 3 4 we go and take a back-and-forth
4
5 trip on passages without any string
0 1 2 3 4 5 6 7 in them that go to intersections
pre 0 5 1 6 3 7 2 4 that we have already visited. This
figure shows a different traversal
order than shown in Figures 18.2
and 18.3, primarily so that we
choose to ignore the back links (first encounter) and to go back and can draw the tour without cross-
forth on down links (second encounter) (see Exercise 18.23). ing itself. This ordering might re-
Spanning tree Given a connected graph with V vertices, find a sult, for example, if the edges were
processed in some different order
set of V − 1 edges that connects the vertices. DFS solves this problem when building an adjacency-lists
because any DFS tree is a spanning tree. Our DFS implementations representation of the graph, or,
make precisely V −1 recursive calls for a connected graph, one for each we might explicitly modify DFS
edge on a spanning tree, and can be easily instrumented to produce a to take the geometric placement
of the nodes into account (see Ex-
parent-link representation of the tree, as discussed at the beginning of ercise 18.24). Moving along the
Section 18.4. If the graph has C connected components, we get a span- lower track leading out of 0, we
ning forest with V − C edges. In an ADT function implementation, we move from 0 to 2 to 6 to 4 to 7,
might choose to add the parent-link array to the graph representation, then take a trip from 7 to 0 and
back because pre[0] is less than
in the style of Program 18.4, or to compute it in a client-supplied array. pre[7]. Then we go to 1, back
Vertex search How many vertices are in the same connected to 7, back to 4, to 3, to 5, from
component as a given vertex? We can solve this problem easily by start- 5 to 0 and back, from 5 to 4 and
back, back to 3, back to 4, back to
ing a DFS at the vertex and counting the number of vertices marked. In
6, back to 2, and back to 0. This
a dense graph, we can speed up the process considerably by stopping path may be obtained by a recur-
the DFS after we have marked V vertices—at that point, we know that sive pre- and postorder walk of the
no edge will take us to a vertex that we have not yet seen, so we will DFS tree (ignoring the shaded ver-
tices that represent the second time
be ignoring all the rest of the edges. This improvement is likely to
we encounter the edges) where we
allow us to visit all vertices in time proportional to V log V , not E (see print out the vertex name, recur-
Section 18.8). sively visit the subtrees, then print
Two-colorability, bipartiteness, odd cycle Is there a way to out the vertex name again.
assign one of two colors to each vertex of a graph such that no edge
connects two vertices of the same color? Is a given graph bipartite
(see Section 17.1)? Does a given graph have a cycle of odd length?
These three problems are all equivalent: The first two are different
nomenclature for the same problem; any graph with an odd cycle is
104 §18.5 CHAPTER EIGHTEEN
18.26 Modify your solution to Exercise 18.25 such that you can use it for
huge graphs, where you might not have the space to make a copy of the
list nodes corresponding to each edge. That is, use the list nodes that were
allocated to build the graph, and destroy the original adjacency-lists graph
representation.
◦ 18.28 Explain why the approach taken in Program 18.6 does not generalize
to give an efficient method for determining whether a graph is three-colorable.
18.29 Most graphs are not two-colorable, and DFS tends to discover that
fact quickly. Run empirical tests to study the number of edges examined by
106 §18.6 CHAPTER EIGHTEEN
Program 18.6, for graphs of various sizes, drawn from various graph models
(see Exercises 17.63–76).
◦ 18.30 Prove that every connected graph has a vertex whose removal will not
disconnect the graph, and write a DFS function that finds such a vertex. Hint:
Consider the leaves of the DFS tree.
18.31 Prove that every graph with more than one vertex has at least two
vertices whose removal will not increase the number of connected components.
0 Figure 18.17
DFS tree for finding bridges
5 1 6
Nodes 5, 7, and 12 in this DFS
0 4 3 2 0
tree for the graph in Figure 18.16
3 9 5 11 6 1 all have the property that no back
edge connects a descendant with
4 5 4 11 7 2 0
an ancestor, and no other nodes
9 12 4 10 6 8 have that property. Therefore, as
indicated, breaking the edge be-
11 8 7
tween one of these nodes and its
10 7 parent would disconnect the sub-
tree rooted at that node from the
0 1 2 3 4 5 6 7 8 9 10 11 12
pre 0 7 8 3 2 1 9 10 12 4 11 5 6 rest of the graph. That is, the edges
low 0 0 0 1 1 1 0 10 10 2 10 2 6 0-5, 11-12, and 6-7 are bridges.
We use the vertex-indexed array
low to keep track of the lowest
preorder number referenced by any
graph remains connected when we remove any single edge. In some back edge in the subtree rooted at
contexts, it is more natural to emphasize our ability to disconnect the the vertex. For example, the value
graph rather than the graph’s ability to stay connected, so we freely of low[9] is 2 because one of the
back edges in the subtree rooted
use alternate terminology that provides this emphasis: We refer to
at 9 points to 4 (the vertex with
a graph that is not edge-connected as an edge-separable graph, and preorder number 2), and no other
we call bridges separation edges. If we remove all the bridges in an back edge points higher in the tree.
edge-separable graph, we divide it into edge-connected components or Nodes 5, 7, and 12 are the ones
for which the low value is equal to
bridge-connected components: maximal subgraphs with no bridges.
the pre value.
Figure 18.16 is a small example that illustrates these concepts.
Finding the bridges in a graph seems, at first blush, to be a
nontrivial graph-processing problem, but it actually is an application
of DFS where we can exploit basic properties of the DFS tree that we
have already noted. Specifically, back edges cannot be bridges because
we know that the two nodes they connect are also connected by a path
in the DFS tree. Moreover, we can add a simple test to our recursive
DFS function to test whether or not tree edges are bridges. The basic
idea, stated formally next, is illustrated in Figure 18.17.
Property 18.5 In any DFS tree, a tree edge v-w is a bridge if and
only if there are no back edges that connect a descendant of w to an
ancestor of w.
Proof : If there is such an edge, v-w cannot be a bridge. Conversely, if
v-w is not a bridge, then there has to be some path from w to v in the
graph other than w-v itself. Every such path has to have some such
edge.
108 §18.6 CHAPTER EIGHTEEN
12 Figure 18.18
11 Another DFS tree for finding
9 12 4 bridges
4 11 This diagram shows a different
3 9 5 11 DFS tree for than the one in Fig-
4 5 ure 18.17 for the graph in Fig-
ure 18.16, where we starting the
0 4 3
search at a different node. Al-
5 1 6
though we visit the nodes and
2 0
edges in a completely different or-
6 1 der, we still find the same bridges
7 2 0 (of course). In this tree, 0, 7, and
10 6 8 11 are the ones for which the low
8 7 value is equal to the pre value, so
10 7
the edges connecting each of them
to their parents (12-11, 5-0, and
0 1 2 3 4 5 6 7 8 9 10 11 12 6-7, respectively) are bridges.
pre 6 7 8 4 3 5 9 10 12 2 11 1 0
low 6 6 6 3 1 3 6 10 10 1 10 1 0
the adjacency list, keeping track of the minimum of the numbers that
we can reach by following each edge. For tree edges, we do the
computation recursively; for back edges, we use the preorder number
of the adjacent vertex. If the call to the recursive function for an edge
w-v does not uncover a path to a node with a preorder number less
than v’s preorder number, then w-v is a bridge.
Property 18.6 We can find a graph’s bridges in linear time.
Proof : Program 18.7 is a minor modification to DFS that involves
adding a few constant-time tests, so it follows directly from Proper-
ties 18.3 and 18.4 that finding the bridges in a graph requires time
proportional to V 2 for the adjacency-matrix representation and to
V + E for the adjacency-lists representation.
In Program 18.7, we use DFS to discover properties of the graph.
The graph representation certainly affects the order of the search, but
it does not affect the results because bridges are a characteristic of the
graph rather than of the way that we choose to represent or search
the graph. As usual, any DFS tree is simply another representation
of the graph, so all DFS trees have the same connectivity properties.
The correctness of the algorithm depends on this fundamental fact.
For example, Figure 18.18 illustrates a different search of the graph,
110 §18.6 CHAPTER EIGHTEEN
starting from a different vertex, that (of course) finds the same bridges.
Despite Property 18.6, when we examine different DFS trees for the
same graph, we see that some search costs may depend not just on
properties of the graph, but also on properties of the DFS tree. For
example, the amount of space needed for the stack to support the
recursive calls is larger for the example in Figure 18.18 than for the
example in Figure 18.17.
As we did for regular connectivity in Program 18.4, we may wish
to use Program 18.7 to build an ADT function for testing whether a
graph is edge-connected or to count the number of edge-connected
components. If desired, we can proceed as for Program 18.4 to create
ADT functions that gives clients the ability to call a linear-time pre-
processing function, then respond in constant time to queries that ask
whether two vertices are in the same edge-connected component (see
Exercise 18.35).
We conclude this section by considering other generalizations
of connectivity, including the problem of determining which vertices
are critical to keeping a graph connected. By including this material
biconnected component here, we keep in one place the basic background material for the more
complex algorithms that we consider in Chapter 22. If you are new
to connectivity problems, you may wish to skip to Section 18.7 and
return here when you study Chapter 22.
bridge When we speak of it removing a vertex, we also mean that we
edge-connected
remove all its incident edges. As illustrated in Figure 18.19, removing
component either of the vertices on a bridge would disconnect a graph (unless the
articulation point bridge were the only edge incident on one or both of the vertices), but
there are also other vertices, not associated with bridges, that have the
same property.
Definition 18.6 An articulation point in a graph is an vertex that, if
Figure 18.19 removed, would separate a connected graph into at least two disjoint
Graph separability terminol- subgraphs.
ogy
We also refer to articulation points as separation vertices or cut vertices.
This graph has two edge-connected
components and one bridge. The
We might use the term “vertex connected” to describe a graph that has
edge-connected component above no separation vertices, but we use different terminology based on a
the bridge is also biconnected; the related characterization that turns out to be equivalent.
one below the bridge consists of
two biconnected components that Definition 18.7 A graph is said to be biconnected if every pair of
are joined at an articulation point. vertices is connected by two disjoint paths.
GRAPH SEARCH §18.6 111
similar to Program 18.7, with an extra test to check whether the root
of the DFS tree is an articulation point (see Exercise 18.42). Develop-
ing code to print out the biconnected components is also a worthwhile
exercise that is only slightly more difficult than the corresponding code
for edge connectivity (see Exercise 18.43).
Proof : As for Property 18.7, this fact follows from the observation that
the solutions to Exercises 18.42 and 18.43 involve minor modifications
to DFS that amount to adding a few constant-time tests per edge.
Draw the standard adjacency-lists DFS tree. Use it to find the articulation
points and the biconnected components.
18.37 Do the previous exercise using the standard adjacency-matrix DFS tree.
18.38 Prove that every edge in a graph either is a bridge or belongs to exactly
one biconnected component.
• 18.39 Prove that any graph with no articulation points is biconnected. Hint:
Given a pair of vertices s and t and a path connecting them, use the fact
that none of the vertices on the path are articulation points to construct two
disjoint paths connecting s and t.
18.40 Modify Program 18.3 to derive a program for determining whether a
graph is biconnected, using a brute-force algorithm that runs in time propor-
tional to V (V + E). Hint: If you mark a vertex as having been seen before
you start the search, you effectively remove it from the graph.
◦ 18.41 Extend your solution to Exercise 18.40 to derive a program that deter-
mines whether a graph is 3-connected. Give a formula describing the approx-
imate number of times your program examines a graph edge, as a function of
V and E.
18.42 Prove that the root of a DFS tree is an articulation point if and only if
it has two or more (internal) children.
• 18.43 Write an ADT function for the adjacency-lists representation that prints
the biconnected components of the graph.
18.44 What is the minimum number of edges that must be present in any
biconnected graph with V vertices?
18.45 Modify Programs 18.3 and 18.7 to implement an ADT function that
determines whether a graph is edge-connected (returning as soon as it identifies
a bridge if the graph is not). Run empirical tests to study the number of edges
examined by your function, for graphs of various sizes, drawn from various
graph models (see Exercises 17.63–76).
◦ 18.46 Instrument Programs 18.3 and 18.7 to print out the number of artic-
ulation points, bridges, and biconnected components.
• 18.47 Run experiments to determine empirically the average values of the
quantities described in Exercise 18.46 for graphs of various sizes, drawn from
various graph models (see Exercises 17.63–76).
18.48 Give the edge connectivity and the vertex connectivity of the graph
0-1 0-2 0-8 2-1 2-8 8-1 3-8 3-7 3-6 3-5 3-4 4-6 4-5 5-6 6-7 7-8.
that no other path connecting those vertices has fewer edges. The
classical method for accomplishing this task, called breadth-first search
(BFS), is also the basis of numerous algorithms for processing graphs,
so we consider it in detail in this section. DFS offers us little assistance
in solving this problem, because the order in which it takes us through
the graph has no relationship to the goal of finding shortest paths. In
contrast, BFS is based on this goal. To find a shortest path from v to w,
we start at v and check for w among all the vertices that we can reach
by following one edge, then we check all the vertices that we can reach
by following two edges, and so forth.
When we come to a point during a graph search where we have
more than one edge to traverse, we choose one and save the others
to be explored later. In DFS, we use a pushdown stack (that is man-
aged by the system to support the recursive search function) for this
purpose. Using the LIFO rule that characterizes the pushdown stack
corresponds to exploring passages that are close by in a maze: We
choose, of the passages yet to be explored, the one that was most re-
cently encountered. In BFS, we want to explore the vertices in order of
their distance from the start. For a maze, doing the search in this order
might require a search team; within a computer program, however, it
is easily arranged: We simply use a FIFO queue instead of a stack.
Program 18.8 is an implementation of BFS for graphs represented
with an adjacency matrix. It is based on maintaining a queue of all
edges that connect a visited vertex with an unvisited vertex. We put
a dummy self-loop to the start vertex on the queue, then perform the
following steps until the queue is empty:
• Take edges from the queue until finding one that points to an
unvisited vertex.
• Visit that vertex; put onto the queue all edges that go from that
vertex to unvisited vertices.
Figure 18.21 shows the step-by-step development of BFS on a sample
graph.
As we saw in Section 18.4, DFS is analogous to one person ex-
ploring a maze. BFS is analogous to a group of people exploring by
fanning out in all directions. Although DFS and BFS are different in
many respects, there is an essential underlying relationship between
the two methods—one that we noted when we briefly considered the
methods in Chapter 5. In Section 18.8, we consider a generalized
116 §18.7 CHAPTER EIGHTEEN
Figure 18.21
Breadth-first search
This figure traces the operation of
BFS on our sample graph. We be- 0 2
0 0 2 0
gin with all the edges adjacent to 2 5 7
6 6
the start vertex on the queue (top 6
1 7 1 7
left). Next, we move edge 0-2
from the queue to the tree and pro- 3 3
cess its incident edges 2-0 and 5 4 5 4
2-6 (second from top, left). We do
not put 2-0 on the queue because 0-2 0-5 0-7 5-3 5-4 7-1 7-4 6-4
0 is already on the tree. Third, we
move edge 0-5 from the queue to
the tree; again 5’s incident edge
(to 0) leads nowhere new, but we
0 2 0 0 2 0
add 5-3 and 5-4 to the queue
(third from top, left). Next, we add 2 2 5 7
6 6
0-7 to the tree and put 7-1 on the 6 3
1 7 1 7
queue (bottom left).
The edge 7-4 is printed in 3 3
gray because we could also avoid 5 4 5 4
putting it on the queue, since there
is another edge that will take us 0-5 0-7 2-6 5-4 7-1 7-4 6-4 3-4
to 4 that is already on the queue.
To complete the search, we take
the remaining edges off the queue,
completely ignoring the gray edges 0
0 2 0 0 2
when they come to the front of
the queue (right). Edges enter and 2 5 2 5 7
6 6
leave the queue in order of their 1 7 1 7
6 3 4
distance from 0.
3 3
5 4 5 4
0 2 0 0 2 0
2 5 7 2 5 7
6 6
6 3 4 1
1 7 1 7
3 3
5 4 5 4
Property 18.11 BFS visits all the vertices and edges in a graph in
time proportional to V 2 for the adjacency-matrix representation and
to V + E for the adjacency-lists representation.
Figure 18.23
All-pairs shortest paths exam-
0 2
0
0 2
4 ple
6
7 5 2 6 7 6 5 3 These figures depict the result of
1 7 1 7
1 0 2 doing BFS from each vertex, thus
4 1 3 6 computing the shortest paths con-
3 3
necting all pairs of vertices. Each
5 4 5 4
search gives a BFS tree that defines
the shortest paths connecting all
graph vertices to the vertex at the
0 2 0 2 5 root. The results of all the searches
1
are summarized in the two matri-
6 6 4 3 0
7 ces at the bottom. In the left ma-
1 7 1 7
4 0
7 6 2 trix, the entry in row v and column
3 3
1 w gives the length of the shortest
6 5 3 2
5 4 5 4 path from v to w (the depth of v
in w’s tree). Each row of the right
matrix contains the st array for the
corresponding search. For exam-
0 2 0 2 6
2 ple, the shortest path from 3 to 2
4 2 has three edges, as indicated by
6 6 0 6
1 7 1 7
the entry in row 3 and column 2 of
7 5 3 0
4 7 5 the left matrix. The third BFS tree
3 3
1 from the top on the left tells us that
3 1
5 4 5 4 the path is 3-4-6-2, and this in-
formation is encoded in row 2 in
the right matrix. The matrix is not
0 2 0 2 7
necessarily symmetric when there
3 is more than one shortest path, be-
4 1 0 cause the paths found depend on
6 5 4 6
1 7 1 7 6 5 3 2 the BFS search order. For example,
0 7 6
3 3 the BFS tree at the bottom on the
2 1 left and row 3 of the right matrix
5 4 5 4
tell us that the shortest path from 2
to 3 is 2-0-5-3.
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
0 0 2 1 2 2 1 2 1 0 0 7 0 5 7 0 2 0
1 2 0 3 3 2 3 3 1 1 7 1 0 4 7 4 4 1
2 1 3 0 3 2 2 1 2 2 2 7 2 4 6 0 2 0
3 2 3 3 0 1 1 2 2 3 5 7 0 3 3 3 4 4
4 2 2 2 1 0 1 1 1 4 7 7 6 4 4 4 4 4
5 1 3 2 1 1 0 2 2 5 5 7 0 5 5 5 4 4
6 2 3 1 2 1 2 0 2 6 2 7 6 4 6 4 6 4
7 1 1 2 2 1 2 2 0 7 7 7 0 4 7 4 4 7
122 §18.7 CHAPTER EIGHTEEN
Figure 18.24
Breadth-first search shortest-path lengths in constant time and the paths themselves in time
This figure illustrates the progress proportional to their length (see Exercise 18.53).
of BFS in random Euclidean near- These BFS-based solutions are effective, but we do not consider
neighbor graph (left), in the same implementations in any further detail here because they are special
style as Figure 18.13. As is evident
from this example, the search tree
cases of algorithms that we consider in detail in Chapter 21. The term
for BFS tends to be quite short and shortest paths in graphs is generally taken to describe the correspond-
wide for this type of graph (and ing problems for digraphs and networks. Chapter 21 is devoted to this
many other types of graphs com- topic. The solutions that we examine there are strict generalizations
monly encountered in practice).
That is, vertices tend to be con- of the BFS-based solutions described here.
nected to one another by rather The basic characteristics of BFS search dynamics contrast sharply
short paths. The contrast between with those for DFS search, as illustrated in the large graph depicted in
the shapes of the DFS and BFS
Figure 18.24, which you should compare with Figure 18.13. The tree
trees is striking testimony to the
differing dynamic properties of the is shallow and broad, and demonstrates a set of facts about the graph
algorithms. being searched different from those shown by DFS. For example,
• There exists a relatively short path connecting each pair of ver-
tices in the graph.
• During the search, most vertices are adjacent to numerous unvis-
ited vertices.
Again, this example is typical of the behavior that we expect from BFS,
but verifying facts of this kind for graph models of interest and graphs
that arise in practice requires detailed analysis.
GRAPH SEARCH §18.7 123
DFS wends its way through the graph, storing on the stack the
points where other paths branch off; BFS sweeps through the graph,
using a queue to remember the frontier of visited places. DFS explores
the graph by looking for new vertices far away from the start point,
taking closer vertices only when dead ends are encountered; BFS com-
pletely covers the area close to the starting point, moving farther away
only when everything nearby has been examined. The order in which
the vertices are visited depends on the graph structure and representa-
tion, but these global properties of the search trees are more informed
by the algorithms than by the graphs or their representations.
The key to understanding graph-processing algorithms is to real-
ize that not only are various different search strategies effective ways
to learn various different graph properties, but also we can imple-
ment many of them uniformly. For example, the DFS illustrated in
Figure 18.13 tells us that the graph has a long path, and the BFS illus-
trated in Figure 18.24 tells us that it has many short paths. Despite
these marked dynamic differences, DFS and BFS are similar, essen-
tially differing in only the data structure that we use to save edges that
are not yet explored (and the fortuitous circumstance that we can use
a recursive implementation for DFS, to have the system maintain an
implicit stack for us). Indeed, we turn next to a generalized graph-
search algorithm that encompasses DFS, BFS, and a host of other use-
ful strategies, and will serve as the basis for solutions to numerous
classic graph-processing problems.
Exercises
18.49 Draw the BFS forest that results from a standard adjacency-lists BFS of
the graph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
18.50 Draw the BFS forest that results from a standard adjacency-matrix BFS
of the graph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
18.51 Give the all-shortest-path matrices (in the style of Figure 18.23) for the
graph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4,
assuming that you use the adjacency-matrix representation.
124 §18.8 CHAPTER EIGHTEEN
18.54 What does the BFS tree tell us about the distance from v to w when
neither is at the root?
18.55 Develop a graph ADT function that returns the path length that suf-
fices to connect any pair of vertices. (This quantity is known as the graph’s
diameter). Note: You need to define a convention for the return value in the
case that the graph is not connected.
18.56 Give a simple optimal recursive algorithm for finding the diameter of a
tree (see Exercise 18.55).
Figure 18.25
Stack-based DFS
Together with Figure 18.21, this
0 2 0 0 2 0 figure illustrates that BFS and DFS
7 differ only in the underlying data
6 6
4 structure. For BFS, we used a
1 7 1 7
6 queue; for DFS we use a stack. We
3 3 begin with all the edges adjacent
2
5 4 5 4
to the start vertex on the stack (top
left). Second, we move edge 0-7
0-2 0-5 0-7 0-2 0-5 7-1 4-3 4-5 from the stack to the tree and push
onto the stack its incident edges
that go to vertices not yet on the
tree 7-1, 7-4, and 7-6 (second
from top, left). The LIFO stack dis-
0 2 0 0 2 0 cipline implies that, when we put
7 7 an edge on the stack, any edges
6 6
4 that points to the same vertex are
1 7 1 7
6 5 obsolete and will be ignored when
3 3 they reach the top of the stack.
2
5 4 5 4 Such edges are printed in gray.
Third, we move edge 7-6 from the
0-2 0-5 7-1 7-4 0-2 0-5 7-1 4-3 5-3 stack to the tree and push its inci-
dent edges on the stack (third from
top, left). Next, we pop 4-6 push
its incident edges on the stack, two
of which will take us to new ver-
0 2 0 0 2 0 tices (bottom left). To complete
7 7 the search, we take the remaining
6 6
1 7
4 1 7
4 edges off the stack, completely ig-
6 5 noring the gray edges when they
3 3
2 3 come to the top of the stack (right).
5 4 5 4
0-2 0-5 7-1 4-3 4-5 4-6 0-2 0-5 7-1 4-3
0 2 0 0 2 0
7 7
6 6
4 4 1
1 7 1 7
6 6 5
3 3
2 3
5 4 5 4
to a start vertex on the fringe and an empty tree, perform the following
operation until the fringe is empty:
Move an edge from the fringe to the tree. If the vertex to
which it leads is unvisited, visit that vertex, and put onto
the fringe all edges that lead from that vertex to unvisited
vertices.
This strategy describes a family of search algorithms that will visit all
the vertices and edges of a connected graph, no matter what type of
generalized queue we use to hold the fringe edges.
When we use a queue to implement the fringe, we get BFS, the
topic of Section 18.6. When we use a stack to implement the fringe, we
get DFS. Figure 18.25, which you should compare with Figures 18.6
and 18.21, illustrates this phenomenon in detail. Proving this equiva-
lence between recursive and stack-based DFS is an interesting exercise
in recursion removal, where we essentially transform the stack under-
lying the recursive program into the stack implementing the fringe (see
unseen vertex Exercise 18.61). The search order for the DFS depicted in Figure 18.25
differs from the one depicted in Figure 18.6 only because the stack dis-
cipline implies that we check the edges incident on each vertex in the
fringe edge reverse of the order that we encounter them in the adjacency matrix (or
the adjacency lists). The basic fact remains that, if we change the data
tree vertex structure used by Program 18.8 to be a stack instead of a queue (which
is trivial to do because the ADT interfaces of the two data structures
tree edge
differ in only the function names), then we change that program from
fringe vertex BFS to DFS.
Now, as we discussed in Section 18.7, this general method may
not be as efficient as we would like, because the fringe becomes clut-
tered up with edges that point to vertices that are moved to the tree
Figure 18.26 during the time that the edge is on the fringe. For FIFO queues, we
Graph search terminology
avoid this situation by marking destination vertices when we put edges
During a graph search, we main- on the queue. We ignore edges to fringe vertices because we know that
tain a search tree (black) and a
fringe (gray) of edges that are can- they will never be used: The old one will come off the queue (and
didates to be next added to the the vertex visited) before the new one does (see Program 18.9). For
tree. Each vertex is either on the a stack implementation, we want the opposite: When an edge is to
tree (black), the fringe (gray), or be added to the fringe that has the same destination vertex as the one
not yet seen (white). Tree vertices
are connected by tree edges, and already there, we know that the old edge will never be used, because
each fringe vertex is connected by the new one will come off the stack (and the vertex visited) before the
a fringe edge to a tree vertex. old one. To encompass these two extremes and to allow for fringe
GRAPH SEARCH §18.8 127
#include <stdlib.h>
#include "GQ.h"
static Item *s;
static int N;
void RQinit(int maxN)
{ s = malloc(maxN*sizeof(Item)); N = 0; }
int RQempty()
{ return N == 0; }
void RQput(Item x)
{ s[N++] = x; }
void RQupdate(Item x)
{ }
Item RQget()
{ Item t;
int i = N*(rand()/(RAND_MAX + 1.0));
t = s[i]; s[i] = s[N-1]; s[N-1] = t;
return s[--N];
}
edges on the fringe, then ignore those that go to tree vertices when
we take them off. The disadvantage of this approach, as with BFS,
is that the maximum queue size has to be E instead of V . Or, we
could handle updates implicitly in the ADT implementation, just by
specifying that no two edges with the same destination vertex can
be on the queue. But the simplest way for the ADT implementation
to do so is essentially equivalent to using a vertex-indexed array (see
Exercises 4.51 and 4.54), so the test fits more comfortably into the
client graph-search program.
The combination of Program 18.10 and the generalized-queue
abstraction gives us a general and flexible graph-search mechanism.
To illustrate this point, we now consider briefly two interesting and
useful alternatives to BFS and DFS.
130 §18.8 CHAPTER EIGHTEEN
Figure 18.29
Randomized graph search
This figure illustrates the progress
of randomized graph searching
(left), in the same style as Fig-
ures 18.13 and 18.24. The search
tree shape falls somewhere be-
tween the BFS and DFS shapes.
The dynamics of these three al-
gorithms, which differ only in the
data structure for work to be com-
pleted, could hardly be more dif-
ferent.
132 §18.8 CHAPTER EIGHTEEN
• 18.69 Write a client program that does dynamic graphical animations of gen-
eralized graph search for graphs that have (x, y) coordinates associated with
each vertex (see Exercises 17.55 through 17.59). Test your program on ran-
dom Euclidean neighbor graphs, using as many points as you can process in
a reasonable amount of time. Your program should produce images like the
snapshots shown in Figures 18.13, 18.24, and 18.29, although you should
feel free to use colors instead of shades of gray to denote tree, fringe, and
unseen vertices and edges.
situation where, for many applications, we are on thin ice with respect
to all three properties listed in the previous paragraph:
• The mathematical analysis is challenging, and many basic ana-
lytic questions remain unanswered.
• There is a huge number of different types of graphs, and we
cannot reasonably test our algorithms on all of them.
• Characterizing the types of graphs that arise in practical problems
is, for the most part, a poorly understood problem.
Graphs are sufficiently complicated that we often do not fully under-
stand the essential properties of the ones that we see in practice or of
the artificial ones that we can perhaps generate and analyze.
The situation is perhaps not as bleak as just described for one
primary reason: Many of the graph algorithms that we consider are
optimal in the worst case, so predicting performance is a trivial ex-
ercise. For example, Program 18.7 finds the bridges after examining
each edge and each vertex just once. This cost is the same as the cost of
building the graph data structure, and we can confidently predict, for
example, that doubling the number of edges will double the running
time, no matter what kind of graphs we are processing.
When the running time of an algorithm depends on the structure
of the input graph, predictions are much harder to come by. Still,
when we need to process huge numbers of huge graphs, we want
efficient algorithms for the same reasons that we want them for any
other problem domain, and we will continue to pursue the goals of
understanding the essential properties of algorithms and applications,
striving to identify those methods that can best handle the graphs that
might arise in practice.
To illustrate some of these issues, we revisit the study of graph
connectivity, a problem that we considered already in Chapter 1 (!).
Connectivity in random graphs has fascinated mathematicians for
years, and it has been the subject of an extensive literature. That
literature is beyond the scope of this book, but it provides a backdrop
that validates our use of the problem as the basis for some experimen-
tal studies that help us understand the basic algorithms that we use
and the types of graphs that we are considering.
For example, growing a graph by adding random edges to a set of
initially isolated vertices (essentially, the process behind Program 17.7)
is a well-studied process that has served as the basis for classical ran-
GRAPH SEARCH §18.9 135
This table shows the number of connected components and the size of the
largest connected component for 100000-vertex graphs drawn from two
different distributions. For the random graph model, these experiments
support the well-known fact that the graph is highly likely to consist
primarily of one giant component if the average vertex degree is larger
than a small constant. The right two columns give experimental results
when we restrict the edges to be chosen from those that connect each
vertex to just one of 10 specified neighbors.
E C L C L
1000 99000 5 99003 3
2000 98000 4 98010 4
5000 95000 6 95075 5
10000 90000 8 90300 7
20000 80002 16 81381 9
50000 50003 1701 57986 27
100000 16236 79633 28721 151
200000 1887 98049 3818 6797
500000 4 99997 19 99979
1000000 1 100000 1 100000
Key:
C number of connected components
L size of largest connected component
dom graph theory. It is well known that, as the number of edges grows,
the graph coalesces into just one giant component. The literature on
random graphs gives extensive information about the nature of this
process, for example:
Proof : This fact was established by seminal work of Erdös and Renyi
in 1960. The proof is beyond the scope of this book (see reference
section).
This property tells us that we can expect large nonsparse random
graphs to be connected. For example, if V > 1000 and E > 10V , then
μ > 10 − 12 ln 1000 > 6.5 and the average number of vertices not in
the giant component is (almost surely) less than e−13 < .000003. If we
generate a million random 1000-vertex graphs of density greater than
10, we might get a few with a single isolated vertex, but the rest will
all be connected.
Figure 18.30 compares random graphs with random neighbor
graphs, where we allow only edges that connect vertices whose indices
are within a small constant of one another. The neighbor-graph model
yields graphs that are evidently substantially different in character from
random graphs. We eventually get a giant component, but it appears
suddenly, when two large components merge.
Table 18.2 shows that these structural differences between ran-
dom graphs and neighbor graphs persist for V and E in ranges of prac-
tical interest. Such structural differences certainly may be reflected in
the performance of our algorithms.
Table 18.3 gives empirical results for the cost of finding the num-
ber of connected components in a random graph, using various al-
gorithms. Although the algorithms perhaps are not subject to direct
comparison for this specific task because they are designed to handle
different tasks, these experiments do validate a subset of the general
conclusions that we have drawn.
First, it is plain from the table that we should not use the
adjacency-matrix representation for large sparse graphs (and cannot
use it for huge ones), not just because the cost of initializing the array
is prohibitive, but also because the algorithm inspects every entry in
the array, so its running time is proportional to the size (V 2 ) of the
array rather than to the number of 1s in it (E). The table shows, for
example, that it takes about as long to process a graph that contains
1000 edges as it does to process one that contains 100000 edges when
we are using an adjacency matrix.
Second, it is also plain from Table 18.3 that the cost of allocating
memory for the list nodes is significant when we build adjacency lists
for large sparse graphs. The cost of building the lists is more than five
GRAPH SEARCH §18.9 137
Figure 18.30
Connectivity in random
graphs
This figures show the evolution
of two types of random graphs at
10 equal increments as a total of
2E edges are added to initially
empty graphs. Each plot is a his-
togram of the number of vertices
in components of size 1 through
V − 1 (left to right). We start out
with all vertices in components of
size 1 and end with nearly all ver-
tices in a giant component. The
plot at left is for a standard random
graph: the giant component forms
quickly, and all other components
are small. The plot at right is for a
random neighbor graph: compo-
nents of various sizes persist for a
longer time.
138 §18.9 CHAPTER EIGHTEEN
This table shows relative timings for various algorithms for the task of
determining the number of connected components (and the size of the
largest one) for graphs with various numbers of vertices and edges. As
expected, algorithms that use the adjacency-matrix representation are
slow for sparse graphs, but competitive for dense graphs. For this spe-
cialized task, the union-find algorithms that we considered in Chapter 1
are the fastest, because they build a data structure tailored to solve the
problem and do not need otherwise to represent the graph. Once the
data structure representing the graph has been built, however, DFS and
BFS are faster, and more flexible. Adding a test to stop when the graph
is known to consist of a single connected component significantly speeds
up DFS and union-find (but not BFS) for dense graphs.
E U U* I D D* I D D* B B*
5000 vertices
500 1 0 255 312 356 1 0 0 0 1
1000 0 1 255 311 354 1 0 0 0 1
5000 1 2 258 312 353 2 2 1 2 1
10000 3 3 258 314 358 5 2 1 2 1
50000 12 6 270 315 202 25 6 4 5 6
100000 23 7 286 314 181 52 9 2 10 11
500000 117 5 478 248 111 267 54 16 56 47
100000 vertices
5000 5 3 3 8 7 24 24
10000 4 5 6 7 7 24 24
50000 18 18 26 12 12 28 28
100000 34 35 51 28 24 34 34
500000 133 137 259 88 89
Key:
U weighted quick union with halving (Program 1.4)
I initial construction of the graph representation
D recursive DFS (Programs 18.1 and 18.2)
B BFS (Programs 18.9 and 18.10)
* exit when graph is found to be fully connected
GRAPH SEARCH §18.9 139
times the cost of traversing them. In the typical situation where we are
going to perform numerous searches of various types after building the
graph, this cost is acceptable. Otherwise, we might consider preallo-
cating the array to reduce the cost of memory allocation, as discussed
in Chapter 2.
Third, the absence of numbers in the DFS columns for large
sparse graphs is significant. These graphs cause excessive recursion
depth, which (eventually) cause the program to crash. If we want to
use DFS on such graphs, we need to use the nonrecursive version that
we discussed in Section 18.7.
Fourth, the table shows that the union-find–based method from
Chapter 1 is faster than DFS or BFS, primarily because it does not
have to represent the entire graph. Without such a representation,
however, we cannot answer simple queries such as “Is there an edge
connecting v and w?” so union-find–based methods are not suitable
if we want to do more than what they are designed to do (answer “Is
there a path between v and w?” queries intermixed with adding edges).
Once the internal representation of the graph has been built, it is not
worthwhile to implement a union-find algorithm just to determine
whether it is connected, because DFS or BFS can provide the answer
about as quickly.
When we run empirical tests that lead to tables of this sort,
various anomalies might require further explanation. For example,
on many computers, the cache architecture and other features of the
memory system might have dramatic impact on performance for large
graphs. Improving performance in critical applications may require
detailed knowledge of the machine architecture in addition to all the
factors that we are considering.
Careful study of these tables will reveal more properties of these
algorithms than we are able to address. Our aim is not to do an ex-
haustive comparison but to illustrate that, despite the many challenges
that we face when we compare graph algorithms, we can and should
run empirical studies and make use of any available analytic results,
both to get a feeling for the algorithms’ important characteristics and
to predict performance.
Exercises
◦ 18.70 Do an empirical study culminating in a table like Table 18.2 for the
problem of determining whether or not a graph is bipartite (two-colorable).
140 §18.9 CHAPTER EIGHTEEN
18.71 Do an empirical study culminating in a table like Table 18.2 for the
problem of determining whether or not a graph is biconnected.
◦ 18.72 Do an empirical study to find the expected size of the second largest
connected component in sparse graphs of various sizes, drawn from various
graph models (see Exercises 17.63–76).
18.73 Write a program that produces plots like those in Figure 18.30, and
test it on graphs of various sizes, drawn from various graph models (see
Exercises 17.63–76).
◦ 18.74 Modify your program from Exercise 18.73 to produce similar his-
tograms for the sizes of edge-connected components.
•• 18.75 The numbers in the tables in this section are results for only one sample.
We might wish to prepare a similar table where we run 1000 experiments for
each entry and give the sample mean and standard deviation, but we probably
could not include nearly as many entries. Would this approach be a better use
of computer time? Defend your answer.
CHAPTER NINETEEN
141
142 CHAPTER NINETEEN
19.6 How many digits do we need to express the number of digraphs that
have V vertices as a base-10 number?
◦ 19.7 Draw the nonisomorphic digraphs that contain 3 vertices.
••• 19.8 How many different digraphs are there with V vertices and E edges if
we consider two digraphs to be different only if they are not isomorphic?
◦ 19.9 Compute an upper bound on the percentage of 10-vertex digraphs that
could ever be examined by any computer, under the assumptions described in
the text and the additional ones that the universe has less than 1080 electrons
and that the age of the universe will be less than 1020 years.
0
6 7 8 Program 19.1 Reversing a digraph (adjacency lists)
1 2 Given a digraph represented with adjacency lists, this function creates a
new adjacency-lists representation of a digraph that has the same vertices
3 9 10 and edges but with the edge directions reversed.
4
5 11 12 Graph GRAPHreverse(Graph G)
{ int v; link t;
0 1 2 3 4 5 6 7 8 9 10 11 12 Graph R = GRAPHinit(G->V);
0 1 0 1 0 0 0 0 0 0 0 0 0 0 for (v = 0; v < G->V; v++)
1 1 1 0 0 0 0 0 0 0 0 0 0 0
2 0 0 1 1 1 0 0 0 0 0 0 0 0 for (t = G->adj[v]; t != NULL; t = t->next)
3 0 0 1 1 1 0 0 0 0 0 0 0 0 GRAPHinsertE(R, EDGE(t->v, v));
4 0 0 0 0 1 1 1 0 0 0 0 0 0
5 1 0 0 1 0 1 0 0 0 0 0 0 0 return R;
6 1 0 0 0 0 0 1 1 0 0 0 0 0 }
7 0 0 0 0 0 0 0 1 1 0 0 0 0
8 0 0 0 0 0 0 0 1 1 0 0 0 0
9 0 0 0 0 0 0 1 0 1 1 0 0 1
10 0 0 0 0 0 0 0 0 0 1 1 0 0 grams for generating, building, and showing graphs (Programs 17.1
11 0 0 0 0 1 0 0 0 0 1 0 1 0
12 0 0 0 0 0 0 0 0 0 0 1 1 1 through 17.9), we remove the statement
G->adj[w] = NEW(v, G->adj[w]);
0
0 2 from the function GRAPHinsertE in the adjacency-lists version (Pro-
1
1 0 gram 17.6), remove the references to G->adj[w][v] from the functions
2
2 4 3 GRAPHinsertE and GRAPHremoveE in the adjacency-matrix version
3
3 4 2 (Program 17.3), and make the appropriate adjustments to GRAPHedges
4
4 6 5 in both versions.
5
5 3 0 The indegree of a vertex in a digraph is the number of directed
6
6 7 0 edges that lead to that vertex. The outdegree of a vertex in a digraph
7
7 8 is the number of directed edges that emanate from that vertex. No
8
8 7 vertex is reachable from a vertex of outdegree 0, which is called a
9
9 12 8 6 sink; a vertex of indegree 0, which is called a source, is not reachable
10
10 9 from any other vertex. A digraph where self-loops are allowed and
11
11 9 4 every vertex has outdegree 1 is called a map (a function from the set
12
12 11 10 of integers from 0 to V − 1 onto itself). We can easily compute the
indegree and outdegree of each vertex, and find sources and sinks, in
Figure 19.5 linear time and space proportional to V , using vertex-indexed arrays
Digraph reversal (see Exercise 19.19).
Reversing the edges of a digraph The reverse of digraph is the digraph that we obtain by switching
corresponds to transposing the
adjacency matrix but requires re- the direction of all the edges. Figure 19.5 shows the reverse and its
building the adjacency lists (see representations for the digraph of Figure 19.1. We use the reverse in
Figures 19.1 and 19.4). digraph algorithms when we need to know from where edges come
DIGRAPHS AND DAGS §19.1 147
because our standard representations tell us only where the edges go.
For example, indegree and outdegree change roles when we reverse a
digraph.
For an adjacency-matrix representation, we could compute the
reverse by making a copy of the array and transposing it (interchanging
its rows and columns). If we know that the graph is not going to be
modified, we can actually use the reverse without any extra computa-
tion by simply interchanging our references to the rows and columns
when we want to refer to the reverse. That is, an edge s-t in a digraph
G is indicated by a 1 in G->adj[s][t], so, if we were to compute the
reverse R of G, it would have a 1 in R->adj[t][s]; we do not need
to do so, however, because, if we want to check whether there is an
edge from s to t in the reverse of G, we can just check G->adj[t][s].
This opportunity may seem obvious, but it is often overlooked. For
an adjacency-lists representation, the reverse is a completely different
data structure, and we need to take time proportional to the number
of edges to build it, as shown in Program 19.1.
Yet another option, which we shall address in Chapter 22, is
to maintain two representations of each edge, in the same manner as
we do for undirected graphs (see Section 17.3) but with an extra bit
that indicates edge direction. For example, to use this method in an
adjacency-lists representation we would represent an edge s-t by a 0
6 7 8
node for t on the adjacency list for s (with the direction bit set to
1 2
indicate that to move from s to t is a forward traversal of the edge)
and a node for s on the adjacency list for t (with the direction bit set to 3 9 10
indicate that to move from t to s is a backward traversal of the edge). 4
This representation supports algorithms that need to traverse edges in 5 11 12
digraphs in both directions. It is also generally convenient to include
pointers connecting the two representations of each edge in such cases. 2-3 11-12 3-5 6-4
We defer considering this representation in detail to Chapter 22, where 0-6 9-12 8-7 6-9
0-1 9-10 5-4 7-6
it plays an essential role. 2-0 9-11 0-5
In digraphs, by analogy to undirected graphs, we speak of di-
rected cycles, which are directed paths from a vertex back to itself, Figure 19.6
A directed acyclic graph
and simple directed paths and cycles, where the vertices and edges are (DAG)
distinct. Note that s-t-s is a cycle of length 2 in a digraph but that
This digraph has no cycles, a prop-
cycles in undirected graphs must have at least three distinct vertices. erty that is not immediately appar-
In many applications of digraphs, we do not expect to see any ent from the edge list or even from
cycles, and we work with yet another type of combinatorial object. examining its drawing.
148 §19.1 CHAPTER NINETEEN
Note that no DAG that contains more than one vertex is strongly
connected.
Like simple connectivity in undirected graphs, this relation is
transitive: If s is strongly connected to t and t is strongly connected
to u, then s is strongly connected to u. Strong connectivity is an
equivalence relation that divides the vertices into equivalence classes
containing vertices that are strongly connected to each other. (See
Section 19.4 for a detailed discussion of equivalence relations.) Again,
strong connectivity provides a property for digraphs that we take for
granted with respect to connectivity in undirected graphs.
Property 19.1 A digraph that is not strongly connected comprises a
set of strongly connected components (strong components, for short),
which are maximal strongly connected subgraphs, and a set of directed
edges that go from one component to another.
Proof : Like components in undirected graphs, strong components in
digraphs are induced subgraphs of subsets of vertices: Each vertex is
in exactly one strong component. To prove this fact, we first note that
every vertex belongs to at least one strong component, which contains
(at least) the vertex itself. Then we note that every vertex belongs to
at most one strong component: If a vertex were to belong to two dif-
ferent ones, then there would be paths through that vertex connecting
vertices in those components to each other, in both directions, which
contradicts the maximality of both components.
For example, a digraph that consists of a single directed cycle
has just one strong component. At the other extreme, each vertex in
a DAG is a strong component, so each edge in a DAG goes from one
component to another. In general, not all edges in a digraph are in
the strong components. This situation is in contrast to the analogous
situation for connected components in undirected graphs, where every
vertex and every edge belongs to some connected component, but
similar to the analogous situation for edge-connected components in
undirected graphs. The strong components in a digraph are connected
by edges that go from a vertex in one component to a vertex in another,
but do not go back again.
Property 19.2 Given a digraph D, define another digraph K(D)
with one vertex corresponding to each strong component of D and
one edge in K(D) corresponding to each edge in D that connects
150 §19.1 CHAPTER NINETEEN
But our primary aim is to address the fact that reachability queries
in digraphs are more difficult to handle than connectivity or strong con-
nectivity queries. In this chapter, we examine classical algorithms that
require preprocessing time proportional to V E and space proportional
to V 2 , develop implementations that can achieve constant-time reach-
ability queries with linear space and preprocessing time for some di-
graphs, and study the difficulty of achieving this optimal performance
for all digraphs.
Exercises
19.10 Give the adjacency-lists structure that is built by Program 17.6, modi-
fied as described in the text to process digraphs, for the digraph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
19.11 Implement an ADT for sparse digraphs, based on Program 17.6, mod-
ified as described in the text. Include a random-digraph generator based on
Program 17.7. Write a client program to generate random digraphs for a well-
chosen set of values of V and E, such that you can use it to run meaningful
empirical tests on digraphs drawn from this model.
19.12 Implement an ADT for dense digraphs, based on Program 17.3, mod-
ified as described in the text. Include a random-digraph generator based on
Program 17.8. Write a client program to generate random graphs for a well-
chosen set of values of V and E, such that you can use it to run meaningful
empirical tests on graphs drawn from this model.
◦ 19.13 Write a program
√ that
√ generates random digraphs by connecting ver-
tices arranged in a V -by- V grid to their neighbors, with edge directions
randomly chosen (see Figure 19.3).
◦ 19.14 Augment your program from Exercise 19.13 to add R extra random
edges (all possible eges equally likely). For large R, shrink the grid so that the
total number of edges remains about V . Test your program as described in
Exercise 19.11.
◦ 19.15 Modify your program from Exercise 19.14 such that an extra edge
goes from a vertex s to a vertex t with probability inversely proportional to
the Euclidean distance between s and t.
◦ 19.16 Write a program that generates V random intervals in the unit interval,
all of length d, then builds a digraph with an edge from interval s to interval t if
and only if at least one of the endpoints of s falls within t (see Exercise 17.73).
Determine how to set d so that the expected number of edges is E. Test your
program as described in Exercise 19.11 (for low densities) and as described
in Exercise 19.12 (for high densities).
• 19.17 Write a program that chooses V vertices and E edges from the real
digraph that you found for Exercise 19.1. Test your program as described
152 §19.2 CHAPTER NINETEEN
in Exercise 19.11 (for low densities) and as described in Exercise 19.12 (for
high densities).
• 19.18 Write a program that produces each of the possible digraphs with V
vertices and E edges with equal likelihood (see Exercise 17.69). Test your
program as described in Exercise 19.11 (for low densities) and as described
in Exercise 19.12 (for high densities).
19.19 Add digraph ADT functions that return the number of sources and
sinks in the digraph. Modify the adjacency-lists ADT implementation (see
Exercise 19.11) such that you can implement the functions in constant time.
◦ 19.20 Use your program from Exercise 19.19 to find the average number of
sources and sinks in various types of digraphs (see Exercises 19.11–18).
19.21 Show the adjacency-lists structure that is produced when you use Pro-
gram 19.1 to find the reverse of the digraph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
• 19.27 Give a kernel DAG of the grid digraph shown in Figure 19.3.
19.28 How many digraphs have V vertices, all of outdegree k?
• 19.29 What is the expected number of different adjacency-list representations
of a random digraph? Hint: Divide the total number of possible representa-
tions by the total number of digraphs.
(recursively) visit all the vertices that can be reached from each of the
0 7
vertices on its adjacency list.
5 1 6 6 8
In an undirected graph, we have two representations of each edge,
4 9 4 7 9
but the second representation that is encountered in a DFS always leads
3 11 2
to a marked vertex and is ignored (see Section 18.2). In a digraph,
5 2 12
we have just one representation of each edge, so we might expect DFS
0 3 9
algorithms to be more straightforward. But digraphs themselves are
11 10
more complicated combinatorial objects than undirected graphs, so
12
this expectation is not justified. For example, the search trees that
0 1 2 3 4 5 6 7 8 9 10 11 12
we use to understand the operation of the algorithm have a more pre 0 9 4 3 2 1 10 11 12 7 8 5 6
complicated structure for digraphs than for undirected graphs. This post 10 8 0 1 6 7 9 12 11 3 2 5 4
Figure 19.11 1 2 7 6 7
DFS forests for a digraph 0 3 6 8 9 4 6 8
5 1 6 7 9 11 10 3 11 2 7 9
These forests describes depth-first
4 9 4 12 12 5 2
search of the same graph as Fig- 9 4 0 3
3 11 2
ure 19.9, when the graph search 5 1 6
5 2 12
function checks the vertices (and 9
calls the recursive function for 11 10 7
the unvisited ones) in the order 12 6 8
s, s+1, ..., V-1, 0, 1, ..., s-1 9 4 7 9
11 10 12 12 3 11 2
12 12 5 2
0 3
9
DIGRAPHS AND DAGS §19.2 157
edge. Suppose that v is the first of the vertices on the cycle that is
visited by the DFS. That vertex has the lowest preorder number of all
the vertices on the cycle. The edge that points to it will therefore be a
back edge: It will be encountered during the recursive call for v (for
a proof that it must be, see Property 19.5); and it points from some
node on the cycle to v, a node with a lower preorder number (see
Property 19.3).
We can convert any digraph into a DAG by doing a DFS and
removing any graph edges that correspond to back edges in the DFS.
For example, Figure 19.9 tells us that removing the edges 2-0, 3-5,
2-3, 9-11, 10-12, 4-2, and 8-7 makes the digraph in Figure 19.1 a
DAG. The specific DAG that we get in this way depends on the graph
representation and the associated implications for the dynamics of the
DFS (see Exercise 19.38). This method is a useful way to generate
large arbitrary DAGs randomly (see Exercise 19.79) for use in testing
DAG-processing algorithms.
Directed cycle detection is a simple problem, but contrasting
the solution just described with the solution that we considered in
Chapter 18 for undirected graphs gives insight into the necessity to
consider the two types of graphs as different combinatorial objects,
even though their representations are similar and the same programs
work on both types for some applications. By our definitions, we seem
to be using the same method to solve this problem as for cycle detection
in undirected graphs (look for back edges), but the implementation
that we used for undirected graphs would not work for digraphs.
For example, in Section 18.5 we were careful to distinguish between
parent links and back links, since the existence of a parent link does
not indicate a cycle (cycles in undirected graphs must involve at least
three vertices). But to ignore links back to a node’s parents in digraphs
would be incorrect; we do consider a doubly-connected pair of vertices
in a digraph to be a cycle. Theoretically, we could have defined back
edges in undirected graphs in the same way as we have done here, but
then we would have needed an explicit exception for the two-vertex
case. More important, we can detect cycles in undirected graphs in
time proportional to V (see Section 18.5), but we may need time
proportional to E to find a cycle in a digraph (see Exercise 19.33).
The essential purpose of DFS is to provide a systematic way to
visit all the vertices and all the edges of a graph. It therefore gives
158 §19.2 CHAPTER NINETEEN
the top level (the roots of the DFS trees) in a particular order that
immediately exposes the strong components.
Exercises
19.30 Add code to Program 19.2 to print out an indented DFS trace, as
described in the commentary to Figure 19.10.
19.31 Draw the DFS forest that results from a standard adjacency-lists DFS
of the digraph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
19.32 Draw the DFS forest that results from a standard adjacency-matrix DFS
of the digraph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
◦ 19.33 Describe a family of digraphs with V vertices and E edges for which a
standard adjacency-lists DFS requires time proportional to E for cycle detec-
tion.
19.34 Show that, during a DFS in a digraph, no edge connects a node to
another node whose preorder and postorder numbers are both smaller.
◦ 19.35 Show all possible DFS forests for the digraph
0-1 0-2 0-3 1-3 2-3.
Tabulate the number of tree, back, cross, and down edges for each forest.
19.36 If we denote the number of tree, back, cross, and down edges by t, b,
c, and d, respectively, then we have t + b + c + d = E and t < V for any DFS
of any digraph with V vertices and E edges. What other relationships among
these variables can you infer? Which of the values are dependent solely on
graph properties and which are dependent on dynamic properties of the DFS?
19.37 Prove that every source in a digraph must be a root of some tree in the
forest corresponding to any DFS of that digraph.
◦ 19.38 Construct a connected DAG that is a subgraph of Figure 19.1 by
deleting five edges (see Figure 19.11).
19.39 Define a digraph ADT function that provides the capability for a client
to check that a digraph is indeed a DAG, and provide a DFS-based implemen-
tation for the adjacency-matrix representation.
19.40 Use your solution to Exercise 19.39 to estimate (empirically) the prob-
ability that a random digraph with V vertices and E edges is a DAG for various
types of digraphs (see Exercises 19.11–18).
19.41 Run empirical studies to determine the relative percentages of tree,
back, cross, and down edges when we run DFS on various types of digraphs
(see Exercises 19.11–18).
DIGRAPHS AND DAGS §19.3 161
◦ 19.45 Give rules corresponding to Trémaux traversal for a maze where all the
passages are one-way.
0 1 2 3 4 5 0 1 2 3 4 5 Figure 19.14
1 2 1 2
0 0 0 1 0 0 1 0 0 1 0 0 1 0 Squaring an adjacency matrix
1 1 0 0 0 0 0 1 0 0 1 0 0 1
0 3 2 0 1 0 0 0 0 2 1 0 0 0 0 0 0 3 If we put 0s on the diagonal of a
3 0 0 1 0 1 0 3 0 1 0 0 0 1 digraph’s adjacency matrix, the
4 0 0 0 0 0 1 4 0 0 0 0 1 0
5 4 5 0 0 0 0 1 0 5 0 0 0 0 0 1 5 4 square of the matrix represents a
graph with an edge correspond-
0 1 2 3 4 5 0 1 2 3 4 5 ing to each path of length 2 (top).
1 2 0 1 0 1 0 0 1 0 1 1 1 0 1 1 1 2 If we put 1s on the diagonal, the
1 1 1 0 0 0 0 1 1 1 1 0 0 1 square of the matrix represents a
0 3 2 0 1 1 0 0 0 2 1 1 1 0 0 0 0 3 graph with an edge correspond-
3 0 0 1 1 1 0 3 0 1 1 1 1 1 ing to each path of length 1 or 2
4 0 0 0 0 1 1 4 0 0 0 0 1 1
5 4 5 0 0 0 0 1 1 5 0 0 0 0 1 1 5 4 (bottom).
closure with just one operation of this kind, building up the transitive
closure from the adjacency matrix in place, as follows:
0 1 2 3 4 5
1 2 0 1 0 1 0 0 1 for (i = 0; i < V; i++)
1 1 1 0 0 0 0 for (s = 0; s < V; s++)
0 3 2 0 1 1 0 0 0
3 0 0 1 1 1 0 for (t = 0; t < V; t++)
4 0 0 0 0 1 1 if (A[s][i] && A[i][t]) A[s][t] = 1;
5 4 5 0 0 0 0 1 1
This classical method, invented by S. Warshall in 1962, is the method
0 1 2 3 4 5 of choice for computing the transitive closure of dense digraphs. The
1 2 0 1 1 1 0 1 1 code is similar to the code that we might try to use to square a Boolean
1 1 1 1 0 0 1
0 3 2 1 1 1 0 0 0 matrix in place: The difference (which is significant!) lies in the order
3 0 1 1 1 1 1
4 0 0 0 0 1 1
of the for loops.
5 4 5 0 0 0 0 1 1
Property 19.7 With Warshall’s algorithm, we can compute the tran-
0 1 2 3 4 5 sitive closure of a digraph in time proportional to V 3 .
1 2 0 1 1 1 0 1 1
1 1 1 1 0 1 1 Proof : The running time is immediately evident from the structure of
0 3 2 1 1 1 0 0 1
3 1 1 1 1 1 1
the code. We prove that it computes the transitive closure by induction
5 4
4 0 0 0 0 1 1 on i. After the first iteration of the loop, the matrix has a 1 in row
5 0 0 0 0 1 1
s and column t if and only if we have either the paths s-t or s-0-t.
0 1 2 3 4 5 The second iteration checks all the paths between s and t that include
1 2 0 1 1 1 0 1 1 1 and perhaps 0, such as s-1-t, s-1-0-t, and s-0-1-t. We are led to
1 1 1 1 0 1 1
0 3 2 1 1 1 0 1 1 the following inductive hypothesis: the ith iteration of the loop sets
3 1 1 1 1 1 1 the bit in row s and column t in the matrix to 1 if and only if there
4 0 0 0 0 1 1
5 4 5 0 0 0 0 1 1 is a directed path from s to t in the digraph that does not include any
vertices with indices greater than i (except possibly the endpoints s
Figure 19.15 and t). As just argued, the condition is true when i is 0, after the first
Adjacency matrix powers and iteration of the loop. Assuming that it is true for the ith iteration of the
directed paths loop, there is a path from s to t that does not include any vertices with
This sequence shows the first, indices greater than i+1 if and only if (i) there is a path from s to t that
second, third, and fourth pow- does not include any vertices with indices greater than i, in which case
ers (right, top to bottom) of the
adjacency matrix at the top right, A[s][t] was set on a previous iteration of the loop (by the inductive
which gives graphs with edges for hypothesis); or (ii) there is a path from s to i+1 and a path from i+1
each of the paths of lengths less to t, neither of which includes any vertices with indices greater than
than 1, 2, 3, and 4, respectively, i (except endpoints), in which case A[s][i+1] and A[i+1][t] were
(left, top to bottom) in the graph
that the matrix represents. The bot- previously set to 1 (by hypothesis), so the inner loop sets A[s][t].
tom graph is the transitive closure We can improve the performance of Warshall’s algorithm with a
for this example, since there are no
paths of length greater than 4 that simple transformation of the code: We move the test of A[s][i] out
connect vertices not connected by of the inner loop because its value does not change as t varies. This
shorter paths. move allows us to avoid executing the t loop entirely when A[s][i] is
DIGRAPHS AND DAGS §19.3 165
0 1 2 3 4 5
1 2 0 1 0 1 0 0 1 Figure 19.16
1 1 1 0 0 0 0 Warshall’s algorithm
0 3 2 0 1 1 0 0 0
3 0 0 1 1 1 0 This sequence shows the devel-
4 0 0 0 0 1 1 opment of the transitive closure
5 4 5 0 0 0 0 1 1 (bottom) of an example digraph
(top) as computed with Warshall’s
0 1 2 3 4 5 0 1 2 3 4 5 algorithm. The first iteration of
1 2 0 1 0 1 0 0 1 1 2 0 1 1 1 0 0 1 the loop (left column, top) adds
1 1 1 1 0 0 1 1 1 1 1 0 0 1 the edges 1-2 and 1-5 because
0 3 2 0 1 1 0 0 0 0 3 2 1 1 1 0 0 1 of the paths 1-0-2 and 1-0-5,
3 0 0 1 1 1 0 3 1 1 1 1 1 1
which include vertex 0 (but no
4 0 0 0 0 1 1 4 0 0 0 0 1 1
5 4 5 0 0 0 0 1 1 5 4 5 0 0 0 0 1 1 vertex with a higher number); the
second iteration of the loop (left
0 1 2 3 4 5 0 1 2 3 4 5 column, second from top) adds
1 2 0 1 0 1 0 0 1 1 2 0 1 1 1 0 0 1 the edges 2-0 and 2-5 because
1 1 1 1 0 0 1 1 1 1 1 0 0 1 of the paths 2-1-0 and 2-1-0-5,
0 3 2 1 1 1 0 0 1 0 3 2 1 1 1 0 0 1 which include vertex 1 (but no ver-
3 0 0 1 1 1 0 3 1 1 1 1 1 1
4 0 0 0 0 1 1 4 0 0 0 0 1 1 tex with a higher number); and
5 4 5 0 0 0 0 1 1 5 4 5 0 0 0 0 1 1 the third iteration of the loop (left
column, bottom) adds the edges
0 1 2 3 4 5 0 1 2 3 4 5 0-1, 3-0, 3-1, and 3-5 because
1 2 0 1 1 1 0 0 1 1 2 0 1 1 1 0 1 1 of the paths 0-2-1, 3-2-1-0,
1 1 1 1 0 0 1 1 1 1 1 0 1 1 3-2-1, and 3-2-1-0-5, which in-
0 3 2 1 1 1 0 0 1 0 3 2 1 1 1 0 1 1
clude vertex 2 (but no vertex with
3 1 1 1 1 1 1 3 1 1 1 1 1 1
4 0 0 0 0 1 1 4 0 0 0 0 1 1 a higher number). The right col-
5 4 5 0 0 0 0 1 1 5 4 5 0 0 0 0 1 1 umn shows the edges added when
paths through 3, 4, and 5 are con-
sidered. The last iteration of the
loop (right column, bottom) adds
zero. The savings that we achieve from this improvement depends on the edges from 0, 1, and 2, to 4,
the digraph and is substantial for many digraphs (see Exercises 19.54 because the only directed paths
from those nodes to 4 include 5,
and 19.55). Program 19.3 implements this improvement and packages the highest-numbered vertex.
Warshall’s method as a pair of ADT functions for digraphs such that
clients can preprocess a digraph (compute the transitive closure), then
compute the answer to any reachability query in constant time.
We are interested in pursuing more efficient solutions, particu-
larly for sparse digraphs. We would like to reduce both the prepro-
cessing time and the space because both make the use of Warshall’s
method prohibitively costly for huge sparse digraphs.
In modern applications, abstract data types provide us with the
ability to separate out the idea of an operation from any particular
implementation, so we can focus on efficient implementations. For the
transitive closure, this point of view leads to a recognition that we do
not necessarily need to compute the entire matrix to provide clients
166 §19.3 CHAPTER NINETEEN
The same argument holds for any linear-time generalized search (see
Section 18.8 and Exercise 19.69).
Program 19.4 is an implementation of this search-based transitive-
closure algorithm. The result of running this program on the sample
digraph in Figure 19.1 is illustrated in the first tree in each forest in
Figure 19.11. The implementation is packaged in the same way as
we packaged Warshall’s algorithm in Program 19.3: a preprocessing
function that computes the transitive closure, and a function that can
determine whether any vertex is reachable from any other in constant
time by testing the indicated entry in the transitive-closure array.
For sparse digraphs, this search-based approach is the method
of choice. For example, if E is proportional to V , then Program 19.4
computes the transitive closure in time proportional to V 2 . How can
it do so, given the reduction to Boolean matrix multiplication that we
DIGRAPHS AND DAGS §19.3 171
This table shows running times that exhibit dramatic performance dif-
ferences for various algorithms for computing the transitive closure of
random digraphs, both dense and sparse. For all but the adjacency-lists
DFS, the running time goes up by a factor of 8 when we double V ,
which supports the conclusion that it is essentially proportional to V 3 .
The adjacency-lists DFS takes time proportional to V E, which explains
the running time roughly increasing by a factor of 4 when we double both
V and E (sparse graphs) and by a factor of about 2 when we double E
(dense graphs), except that list-traversal overhead degrades performance
for high-density graphs.
V W W* A L E W W* A L
Key:
W Warshall’s algorithm (Section 19.3)
W* Improved Warshall’s algorithm (Program 19.3)
A DFS, adjacency-matrix representation (Exercise 19.64)
L DFS, adjacency-lists representation (Program 19.4)
• 19.50 Show how to construct a digraph with V vertices and E edges with the
property that the number of edges in the transitive closure is proportional to
t, for any t between E and V 2 . As usual, assume that E > V .
19.51 Give a formula for the number of edges in the transitive closure of a
digraph that is a directed forest as a function of structural properties of the
forest.
19.52 Show, in the style of Figure 19.15, the process of computing the tran-
sitive closure of the digraph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4
through repeated squaring.
19.53 Show, in the style of Figure 19.16, the process of computing the tran-
sitive closure of the digraph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4
with Warshall’s algorithm.
◦ 19.54 Give a family of sparse digraphs for which the improved version of
Warshall’s algorithm for computing the transitive closure (Program 19.3) runs
in time proportional to V E.
◦ 19.55 Find a sparse digraph for which the improved version of Warshall’s
algorithm for computing the transitive closure (Program 19.3) runs in time
proportional to V 3 .
◦ 19.56 Develop an ADT for integer matrices with appropriate implementations
such that we can have a single client program that encompasses both Warshall’s
algorithm and Floyd’s algorithm. (This exercise is a version of Exercise 19.57
for people who are more familiar with abstract data types than with abstract
algebra.)
• 19.57 Use abstract algebra to develop a generic algorithm that encompasses
both Warshall’s algorithm and Floyd’s algorithm. (This exercise is a version of
Exercise 19.56 for people who are more familiar with abstract algebra than
with abstract data types.)
◦ 19.58 Show, in the style of Figure 19.16, the development of the all-shortest
paths matrix for the example graph in the figure as computed with Floyd’s
algorithm.
19.59 Is the Boolean product of two symmetric matrices symmetric? Explain
your answer.
19.60 Modify Programs 19.3 and 19.4 to provide implementations of a di-
graph ADT function that returns the number of edges in the transitive closure
of the digraph.
174 §19.4 CHAPTER NINETEEN
Exercises
19.70 Show that “has the same remainder after dividing by k” is a transitive
relation (and therefore is an equivalence relation) on the set of integers.
19.71 Show that “is in the same edge-connected component as” is an equiva-
lence relation among vertices in any graph.
19.72 Show that “is in the same biconnected component as” is not an equiv-
alence relation among vertices in all graphs.
178 §19.5 CHAPTER NINETEEN
◦ 19.75 Using an online dictionary, build a graph that represents the equivalence
relation “has k letters in common with” among words. Determine the number
of equivalence classes for k = 1 through 5.
19.76 The cardinality of a partial order is its number of ordered pairs. What
is the cardinality of the subset containment partial order for an n-element set?
19.77 Show that “is a factor of” is a partial order among integers.
19.5 DAGs
In this section, we consider various applications of directed acyclic
graphs (DAGs). We have two reasons to do so. First, because they
serve as implicit models for partial orders, we work directly with DAGs
in many applications and need efficient algorithms to process them.
Second, these various applications give us insight into the nature of
DAGs, and understanding DAGs is essential to understanding general
digraphs.
Since DAGs are a special type of digraph, all DAG-processing
problems trivially reduce to digraph-processing problems. Although
we expect processing DAGs to be easier than processing general di-
graphs, we know when we encounter a problem that is difficult to solve
on DAGs we should not expect to do any better solving the same prob-
lem on general digraphs. As we shall see, the problem of computing
the transitive closure lies in this category. Conversely, understanding
the difficulty of processing DAGs is important because every digraph
has a kernel DAG (see Property 19.2), so we encounter DAGs even
when we work with digraphs that are not DAGs.
The prototypical application where DAGs arise directly is called
scheduling. Generally, solving scheduling problems has to do with ar-
ranging for the completion of a set of tasks, under a set of constraints,
by specifying when and how the tasks are to be performed. Con-
straints might involve functions of the time taken or other resources
consumed by the tasks. The most important type of constraints are
DIGRAPHS AND DAGS §19.5 179
a standard DFS and checking that the DFS forest has no back edges.
A separate ADT for DAGs should include an ADT function allowing
client programs to perform such a check (see Exercise 19.78).
In a sense, DAGs are part tree, part graph. We can certainly
take advantage of their special structure when we process them. For
example, we can view a DAG almost as we view a tree, if we wish.
The following simple program is like a recursive tree traversal:
void traverseR(Dag D, int w)
{ link t;
visit(w);
for (t = D->adj[w]; t != NULL; t = t->next)
traverseR(D, t->v);
8
}
5 3 The result of this program is to traverse the vertices of the DAG D as
though it were a tree rooted at w. For example, the result of traversing
3 2 2 1
the two DAGs in Figure 19.18 with this program would be the same.
2 1 1 1 1 1
We rarely use a full traversal, however, because we normally want to
take advantage of the same economies that save space in a DAG to
1 1 save time in traversing it (for example, by marking visited nodes in
a normal DFS). The same idea applies to a search, where we make a
8 recursive call for only one link incident on each vertex. In such an
5 algorithm, the search cost will be the same for the DAG and the tree,
3
but the DAG uses far less space.
2
Because they provide a compact way to represent trees that have
identical subtrees, we often use DAGs instead of trees when we rep-
1
resent computational abstractions. In the context of algorithm de-
1
sign, the distinction between the DAG representation and the tree
representation of a program in execution is the essential distinction
Figure 19.18 behind dynamic programming (see, for example, Figure 19.18 and
DAG model of Fibonacci Exercise 19.81). DAGs are also widely used in compilers as interme-
computation
diate representations of arithmetic expressions and programs (see, for
The tree at the top shows the de-
pendence of computing each Fi- example, Figure 19.19), and in circuit-design systems as intermediate
bonacci number on computing its representations of combinational circuits.
two predecessors. The DAG at the Along these lines, an important example that has many applica-
bottom shows the same depen-
dence with only a fraction of the tions arises when we consider binary trees. We can apply the same
nodes. restriction to DAGs that we applied to trees to define binary trees:
DIGRAPHS AND DAGS §19.5 181
- - Figure 19.19
* * * * DAG representation of an
arithmetic expression
c + + + c +
Both of these DAGs are represen-
a b a b + e + e
tations of the arithmetic expression
a b a b (c*(a+b))-((a+b))*((a+b)+e)).
In the binary parse tree at left, leaf
nodes represent operands and in-
ternal nodes each represent op-
Definition 19.6 A binary DAG is a directed acyclic graph with two erators to be applied to the ex-
pressions represented by their two
edges leaving each node, identified as the left edge and the right edge,
subtrees (see Figure 5.31). The
either or both of which may be null. DAG at right is a more compact
representation of the same tree.
The distinction between a binary DAG and a binary tree is that in the More important, we can com-
binary DAG we can have more than one link pointing to a node. As pute the value of the expression
did our definition for binary trees, this definition models a natural rep- in time proportional to the size
resentation, where each node is a structure with a left link and a right of the DAG, which is typically
significantly less than the size of
link that point to other nodes (or are null), subject to only the global the tree (see Exercises 19.114
restriction that no directed cycles are allowed. Binary DAGs are sig- and 19.115).
nificant because they provide a compact way to represent binary trees
in certain applications. For example, we can compress an existence
trie into a binary DAG without changing the search implementation,
as shown Figure 19.20 and Program 19.5.
An equivalent application is to view the trie keys as correspond-
ing to rows in the truth table of a Boolean function for which the
function is true (see Exercises 19.87 through 19.90). The binary DAG
is a model for an economical circuit that computes the function. In
this application, binary DAGs are known as binary decision diagrams
(BDD)s.
Motivated by these applications, we turn, in the next two sec-
tions, to the study of DAG-processing algorithms. Not only do these
algorithms lead to efficient and useful DAG ADT function implemen-
tations, but also they provide insight into the difficulty of process-
ing digraphs. As we shall see, even though DAGs would seem to
be substantially simpler structures than general digraphs, some basic
problems are apparently no easier to solve.
Exercises
19.78 Define an ADT interface that is suitable for processing DAGs, and
build adjacency-lists and adjacency-matrix implementations. Include an ADT
function for verifying that the DAG has no cycles, implemented with DFS.
182 §19.5 CHAPTER NINETEEN
19.85 In the style of Figure 19.20, give the existence trie and corresponding
binary DAG for the keys 01001010 10010101 00100001 11101100 01010001
00100001 00000111 01010011 .
19.86 Implement an ADT based on building an existence trie from a set of
32-bit keys, compressing it as a binary DAG, then using that data structure to
support existence queries.
◦ 19.87 Draw the BDD for the truth table for the odd parity function of four
variables, which is 1 if and only if the number of variables that have the value
1 is odd.
19.88 Write a function that takes a 2n -bit truth table as argument and returns
the corresponding BDD. For example, given the input 1110001000001100,
your program should return a representation of the binary DAG in Fig-
ure 19.20. 0
n 6 7 8
19.89 Write a function that takes a 2 -bit truth table as argument, computes 1 2
every permutation of its argument variables, and, using your solution to Ex-
ercise 19.88, finds the permutation that leads to the smallest BDD.
3 9 10
• 19.90 Run empirical studies to determine the effectiveness of the strategy of 4
Exercise 19.90 for various Boolean functions, both standard and randomly 5 11 12
generated.
19.91 Write a program like Program 19.5 that supports common subexpres-
sion removal: Given a binary tree that represents an arithmetic expression, 0
compute a binary DAG that represents the same expression with common 6 5 4
subexpressions removed. 1 2
◦ 19.92 Draw all the nonisomorphic DAGs with two, three, four, and five
vertices. 3 9 10
7
•• 19.93 How many different DAGs are there with V vertices and E edges? 8 11 12
••• 19.94 How many different DAGs are there with V vertices and E edges, if
we consider two DAGs to be different only if they are not isomorphic? 0 1 2 3 4 5 6 7 8 9 10 11 12
tsI 0 1 2 3 7 8 6 5 4 9 10 11 12
Figure 19.22
Topological sorting (rear-
rangement).
This diagram shows another way to 0 1 2 3 8 7 6 4 5 9 10 11 12
look at the topological sort in Fig-
ure 19.21, where we specify a way
to rearrange the vertices, rather 0 1 2 3 4 5 6 7 8 9 10 11 12
than relabel them. When we place ts 0 1 2 3 8 7 6 4 5 9 10 11 12
the vertices in the order specified tsI 0 1 2 3 7 8 6 5 4 9 10 11 12
in the array ts, from left to right,
then all directed edges point from
left to right. The inverse of the per-
mutation ts is the permutation tsI
Topological sort (rearrange) Given a DAG, rearrange its ver-
that specifies the relabeling de- tices on a horizontal line such that all the directed edges point from
scribed in Figure 19.21. left to right (see Figure 19.22).
As indicated in Figure 19.22, it is easy to establish that the re-
labeling and rearrangement permutations are inverses of one another:
Given a rearrangement, we can obtain a relabeling by assigning the la-
bel 0 to the first vertex on the list, 1 to the second label on the list, and
so forth. For example, if an array ts has the vertices in topologically
sorted order, then the loop
for (i = 0; i < V; i++) tsI[ts[i]] = i;
defines a relabeling in the vertex-indexed array tsI. Conversely, if we
have the relabeling in an array tsI, then we can get the rearrangement
with the loop
for (i = 0; i < V; i++) ts[tsI[i]] = i;
which puts the vertex that would have label 0 first in the list, the vertex
that would have label 1 second in the list, and so forth. Most often,
we use the term topological sort to refer to the rearrangement version
of the problem.
In general, the vertex order produced by a topological sort is not
unique. For example,
8 7 0 1 2 3 6 4 9 10 11 12 5
0 1 2 3 8 6 4 9 10 11 12 5 7
0 2 3 8 6 4 7 5 9 10 1 11 12
8 0 7 6 2 3 4 9 5 1 11 12 10
are all topological sorts of the example DAG in Figure 19.6 (and there
are many others). In a scheduling application, this situation arises
whenever one task has no direct or indirect dependence on another and
DIGRAPHS AND DAGS §19.6 185
Figure 19.23
Reverse topological sort.
In this reverse topological sort of
5 12 11 10 9 4 6 3 2 1 0 7 8 our sample digraph, the edges all
point from right to left. Number-
ing the vertices as specified by
0 1 2 3 4 5 6 7 8 9 10 11 12 the inverse permutation tsI gives
ts 5 12 11 10 9 4 6 3 2 1 0 7 8 a graph where every edge points
tsI 10 9 8 7 5 0 6 11 12 4 3 2 1 from a higher-numbered vertex to a
lower-numbered vertex.
thus they can be performed either before or after the other (or even in
parallel). The number of possible schedules grows exponentially with
the number of such pairs of tasks.
As we have noted, it is sometimes useful to interpret the edges in
a digraph the other way around: We say that an edge directed from
s to t means that vertex s “depends” on vertex t. For example, the
vertices might represent terms to be defined in a book, with an edge
from s to t if the definition of s uses t. In this case, it would be
useful to find an ordering with the property that every term is defined
before it is used in another definition. Using this ordering corresponds
to positioning the vertices in a line such that edges all go from right
to left—a reverse topological sort. Figure 19.23 illustrates a reverse
topological sort of our sample DAG.
Now, it turns out that we have already seen an algorithm for
reverse topological sorting: our standard recursive DFS! When the
input graph is a DAG, a postorder numbering puts the vertices in
reverse topological order. That is, we number each vertex as the final
action of the recursive DFS function, as in the post array in the DFS
implementation in Program 19.2. As illustrated in Figure 19.24, using
this numbering is equivalent to numbering the nodes in the DFS forest
in postorder. Taking the vertices in this example in order of their
postorder numbers, we get the vertex order in Figure 19.23—a reverse
topological sort of the DAG.
Proof : Suppose that s and t are two vertices such that s appears before
t in the postorder numbering even though there is a directed edge s-t
in the graph. Since we are finished with the recursive DFS for s at the
186 §19.6 CHAPTER NINETEEN
every DAG has at least one sink. It follows that every DAG also has
at least one source: its reverse’s sink.
0 0
6 7 8 6 7 8
Figure 19.25
1 2 1 2
Topologically sorting a DAG
by removing sources
3 9 10 3 9 10 Since it is a source (no edges point
4 4 to it), 0 can appear first in a topo-
5 11 12 5 11 12 logical sort of this example graph
(left, top). If we remove 0 (and
0 0 all the edges that point from it to
6 7 8 6 7 8 other vertices), then 1 and 2 be-
1 2 1 2 come sources in the resulting DAG
(left, second from top), which we
3 9 10 3 9 10 can then sort using the same al-
4 4 gorithm. This figure illustrates the
5 11 12 5 11 12 operation of Program 19.8, which
picks from among the sources (the
0 0 shaded nodes in each diagram) us-
6 7 8 6 7 8 ing the FIFO discipline, though any
1 2 1 2 of the sources could be chosen at
each step. See Figure 19.26 for the
3 9 10 3 9 10 contents of the data structures that
4 4 control the specific choices that
5 11 12 5 11 12 the algorithm makes. The result of
the topological sort illustrated here
0 0 is the node order 0 8 2 1 7 3 6 5 4
6 7 8 6 7 8 9 11 10 12.
1 2 1 2
3 9 10 3 9 10
4 4
5 11 12 5 11 12
0 0
6 7 8 6 7 8
1 2 1 2
3 9 10 3 9 10
4 4
5 11 12 5 11 12
0 0
6 7 8 6 7 8
1 2 1 2
3 9 10 3 9 10
4 4
5 11 12 5 11 12
190 §19.6 CHAPTER NINETEEN
Exercises
19.95 Add a topological sort function to your DAG ADT from Exer-
cise 19.78, then add an ADT function that checks whether or not a given
permutation of a DAG’s vertices is a proper topological sort of that DAG.
19.96 How many different topological sorts are there of the DAG that is
depicted in Figure 19.6?
192 §19.6 CHAPTER NINETEEN
19.97 Give the DFS forest and the reverse topological sort that results from
doing a standard adjacency-lists DFS (with postorder numbering) of the DAG
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4 4-3 2-3.
◦ 19.98 Give the DFS forest and the topological sort that results from building
a standard adjacency-lists representation of the DAG
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4 4-3 2-3,
then using Program 19.1 to build the reverse, then doing a standard adjacency-
lists DFS with postorder numbering.
• 19.99 Prove the correctness of each of the three suggestions given in the
text for modifying DFS with postorder numbering such that it computes a
topological sort instead of a reverse topological sort.
19.100 Give the DFS forest and the topological sort that results from do-
ing a standard adjacency-matrix DFS with implicit reversal (and postorder
numbering) of the DAG
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4 4-3 2-3
(see Program 19.7).
• 19.101 Given a DAG, does there exist a topological sort that cannot re-
sult from applying a DFS-based algorithm, no matter what order the vertices
adjacent to each vertex are chosen? Prove your answer.
19.102 Show, in the style of Figure 19.26, the process of topologically sort-
ing the DAG
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4 4-3 2-3
with the source-queue algorithm (Program 19.8).
19.103 Give the topological sort that results if the data structure used in the
example depicted in Figure 19.25 is a stack rather than a queue.
• 19.104 Given a DAG, does there exist a topological sort that cannot result
from applying the source-queue algorithm, no matter what queue discipline is
used? Prove your answer.
19.105 Modify the source-queue topological-sort algorithm to use a gener-
alized queue. Use your modified algorithm with a LIFO queue, a stack, and a
randomized queue.
19.106 Use Program 19.8 to provide an implementation for the ADT func-
tion for verifying that a DAG has no cycles (see Exercise 19.78).
◦ 19.107 Convert the source-queue topological-sort algorithm into a sink-
queue algorithm for reverse topological sorting.
19.108 Write a program that generates all possible topological orderings of
a given DAG, or, if the number of such orderings exceeds a bound taken as an
argument, prints that number.
DIGRAPHS AND DAGS §19.7 193
19.109 Write a program that converts any digraph with V vertices and E
edges into a DAG by doing a DFS-based topological sort and changing the
orientation of any back edge encountered. Prove that this strategy always
produces a DAG.
•• 19.110 Write a program that produces each of the possible DAGs with V
vertices and E edges with equal likelihood (see Exercise 17.69).
19.111 Give necessary and sufficient conditions for a DAG to have just one
possible topologically sorted ordering of its vertices.
19.112 Run empirical tests to compare the topological-sort algorithms given
in this section for various DAGs (see Exercise 19.2, Exercise 19.79, Ex-
ercise 19.109, and Exercise 19.110). Test your program as described in
Exercise 19.11 (for low densities) and as described in Exercise 19.12 (for
high densities).
19.113 Modify Program 19.8 so that it computes the number of different
simple paths from any source to each vertex in a DAG.
◦ 19.114 Write a program that evaluates DAGs that represent arithmetic ex-
pressions (see Figure 19.19). Use the adjacency-lists graph ADT, extended to
include a double corresponding to each vertex (to hold its value). Assume
that values corresponding to leaves have been established.
◦ 19.115 Describe a family of arithmetic expressions with the property that
the size of the expression tree is exponentially larger than the size of the cor-
responding DAG (so the running time of your program from Exercise 19.114
for the DAG is proportional to the logarithm of the running time for the tree).
◦ 19.116 Write a program that finds the longest simple directed path in a DAG,
in time proportional to V . Use your program to implement an ADT function
that finds a Hamilton path in a given DAG, if it has one.
void DAGtc(Dag D)
{ int v;
D->tc = MATRIXint(D->V, D->V, 0);
for (v = 0; v < D->V; v++) pre[v] = -1;
for (v = 0; v < D->V; v++)
if (pre[v] == -1) TCdfsR(D, EDGE(v, v));
}
void TCdfsR(Dag D, Edge e)
{ int u, i, v = e.w;
pre[v] = cnt++;
for (u = 0; u < D->V; u++)
if (D->adj[v][u] != 0)
{
D->tc[v][u] = 1;
if (pre[u] > pre[v]) continue;
if (pre[u] == -1) TCdfsR(D, EDGE(v, u));
for (i = 0; i < D->V; i++)
if (D->tc[u][i] == 1) D->tc[v][i] = 1;
}
}
int DAGreach(Dag D, int s, int t)
{ return D->tc[s][t]; }
merge. We access a row of size V for each tree edge and each cross edge.
There are no back edges, and we can ignore down edges because we
accounted for any vertices they reach when we processed any ancestors
of both nodes earlier in the search.
If our DAG has no down edges (see Exercise 19.43), the running
time of Program 19.9 is proportional to V E and represents no im-
provement over the transitive-closure algorithms that we examined for
general digraphs in Section 19.3 (such as, for example, Program 19.4)
196 §19.8 CHAPTER NINETEEN
Exercises
◦ 19.117 Show, in the style of Figure 19.27, the reachability vectors that result
when we use Program 19.9 to compute the transitive closure of the DAG
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4 4-3 2-3.
◦ 19.122 Does your solution to Exercise 19.121 require that you examine all
edges in the DAG, or are there edges that can be ignored, such as the down
edges in DFS? Give an example that requires examination of all edges, or
characterize the edges that can be skipped.
DIGRAPHS AND DAGS §19.8 197
0 1 9 Figure 19.28
0 2 0 6 8 12 Computing strong compo-
6 7 8 nents (Kosaraju’s algo-
3 4 10 11
1 2 rithm)
4 2 9 4 9
3 9 10
To compute the strong components
6 5
4 of the digraph at the lower left, we
7 0 0 3 first do a DFS of its reverse (top
5 11 12
8 left), computing a postorder ar-
7
ray that gives the vertex indices
in the order in which the recursive
0 1 2 3 4 5 6 7 8 9 10 11 12 DFS completed (top). This order
post 8 7 6 5 4 3 2 0 1 10 11 12 9
is equivalent to a postorder walk
of the DFS forest (top right). Then
1 7
we use the reverse of that order to
9 0
0 do a DFS of the original digraph
6 7 8 11 10 5 1 6 6 8 (bottom). First we check all nodes
1 2 12 12 4 9 4 7 9 reachable from 9, then we scan
from right to left through the ar-
3 9 10 9 3 11 2
ray to find that 1 is the rightmost
4 5 2 unvisited vertex, so we do the re-
5 11 12 cursive call for 1, and so forth. The
0 3
trees in the DFS forest that results
0 1 2 3 4 5 6 7 8 9 10 11 12 from this process define the strong
G->sc 2 1 2 2 2 2 2 3 3 0 0 0 0 components: all vertices in each
tree have the same value in the
vertex-indexed id array (bottom).
strong component (because all the vertices that can be reached from
that vertex have been processed). Second, the back links in the tree
provide a second path from one vertex to another and bind together
the strong components.
The recursive DFS function uses the same computation as Pro-
gram 18.7 to find the highest vertex reachable (via a back edge) from
any descendant of each vertex. It also uses a vertex-indexed array to
keep track of the strong components and a stack to keep track of the
current search path. It pushes the vertex names onto a stack on en-
try to the recursive function, then pops them and assigns component
numbers after visiting the final member of each strong component.
The algorithm is based on our ability to identify this moment with a
simple test (based on keeping track of the highest ancestor reachable
via one up link from all descendants of each node) at the end of the
recursive procedure that tells us that all vertices encountered since en-
try (except those already assigned to a component) belong to the same
strong component.
The implementation in Program 19.11 is a succinct and complete
description of the algorithm that fills in the details missing from the
brief sketch just given. Figure 19.29 illustrates the operation of the
algorithm for our sample digraph from Figure 19.1.
Figure 19.29
Computing strong compo- 0 7
nents (Tarjan and Gabow
algorithms) 5 1 6 6 8
4 9 4 7 9
Tarjan’s algorithm is based on a re-
cursive DFS, augmented to push 3 11 2
19.127 Show, in the style of Figure 19.28, the DFS forests and the contents
of the auxiliary vertex-indexed arrays that result when you use Kosaraju’s
algorithm to compute the strong components of the digraph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
◦ 19.131 Show, in the style of Figure 19.29, the DFS forest, stack contents
during the execution of the algorithm, and the final contents of the auxiliary
vertex-indexed arrays that result when you use Tarjan’s algorithm to compute
the strong components of the reverse of the digraph in Figure 19.5. (You
should have the same strong components.)
19.132 Show, in the style of Figure 19.29, the DFS forest, stack contents
during the execution of the algorithm, and the final contents of the auxiliary
vertex-indexed arrays that result when you use Tarjan’s algorithm to compute
the strong components of the digraph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
19.139 Develop a table in the spirit of Table 18.1 to study strong connectivity
in random digraphs (see Table 19.2). Let S be the set of vertices in the largest
strong component. Keep track of the size of S and study the percentages of
edges in the following four classes: those connecting two vertices in S, those
pointing out of S, those pointing in to S, those connecting two vertices not in
S.
19.140 Run empirical tests to compare the brute-force method for comput-
ing strong components described at the beginning of this section, Kosaraju’s
algorithm, Tarjan’s algorithm, and Gabow’s algorithm, for various types of
digraphs (see Exercises 19.11–18).
••• 19.141 Develop a linear-time algorithm for strong 2-connectivity: Determine
whether a strongly connected digraph has the property that it remains strongly
connected after deleting any vertex (and all its incident edges).
DAG (as described in the next paragraph); and DFS (Program 19.9) to
compute its transitive closure. After this preprocessing, we can imme-
diately access the information necessary to determine reachability.
Once we have a vertex-indexed array with the strong compo-
nents of a digraph, building an adjacency-array representation of its
kernel DAG is a simple matter. The vertices of the DAG are the com-
ponent numbers in the digraph. For each edge s-t in the original
digraph, we simply set D->adj[sc[s]][sc[t]] to 1. We would have
to cope with duplicate edges in the kernel DAG if we were using an
adjacency-lists representation—in an adjacency array, duplicate edges
simply correspond to setting an array entry to 1 that has already been
set to 1. This small point is significant because the number of duplicate
edges is potentially huge (relative to the size of the kernel DAG) in this
application.
Property 19.17 Given two vertices s and t in a digraph D, let sc(s)
and sc(t), respectively, be their corresponding vertices in D’s kernel
DAG K. Then, t is reachable from s in D if and only if sc(t) is
reachable from sc(s) in K.
This simple fact follows from the definitions. In particular, this prop-
erty assumes the convention that a vertex is reachable from itself (all
vertices have self-loops). If the vertices are in the same strong compo-
nent (sc(s) = sc(t)), then they are mutually reachable.
We determine whether a given vertex t is reachable from a given
vertex s in the same way as we built the kernel DAG: We use the
vertex-indexed array computed by the strong-components algorithm
to get component numbers sc(s) and sc(t) (in constant time), then
use those numbers to index into the transitive closure of the kernel
DAG (in constant time), which tells us the result. Program 19.13 is an
implementation of the abstract–transitive-closure ADT that embodies
these ideas.
We use an abstract–transitive-closure interface for the kernel
DAG as well. For purposes of analysis, we suppose that we use
an adjacency-matrix representation for the kernel DAG because we
expect the kernel DAG to be small, if not also dense.
Property 19.18 We can support constant query time for the abstract
transitive closure of a digraph with space proportional to V + v 2 and
time proportional to E + v 2 + vx for preprocessing (computing the
210 §19.9 CHAPTER NINETEEN
Property 19.19 We can support constant query time for the abstract
√
transitive closure of any digraph whose kernel DAG has less than 3 V
vertices with space proportional to V and time proportional to E + V
for preprocessing.
√
Proof : Take v < 3 V in Property 19.18. Then, xv < V since x < v 2 .
DIGRAPHS AND DAGS §19.9 211
This table shows the numbers of edges and vertices in the kernel DAGs
for random digraphs generated from two different models (the directed
versions of the models in Table 18.1). In both cases, the kernel DAG
becomes small (and is sparse) as the density increases.
E v e v e
1000 vertices
1000 983 981 916 755
2000 424 621 713 1039
5000 13 13 156 313
10000 1 1 8 17
20000 1 1 1 1
10000 vertices
50000 144 150 1324 150
100000 1 1 61 123
200000 1 1 1 1
Key:
v Number of vertices in kernel DAG
e Number of edges in kernel DAG
broadened the class of graphs for which we can avoid this worst-
case performance. Indeed, constructing a random-digraph model that
produces digraphs for which the algorithm is slow is a challenge (see
Exercise 19.146).
Table 19.2 displays the results of an empirical study; it shows
that random digraphs have small kernel DAGs even for moderate
densities and even in models with severe restrictions on edge placement.
Although there can be no guarantees in the worst case, we can expect
to see huge digraphs with small kernel DAGs in practice. When we
do have such a digraph, we can provide an efficient implementation of
the abstract–transitive-closure ADT.
Exercises
• 19.142 Develop a version of the implementation of the abstract transitive
closure for digraphs based on using an adjacency-lists representation of the
kernel DAG. Your challenge is to eliminate duplicates on the list without using
an excessive amount of time or space (see Exercise 19.68).
19.143 Show the kernel DAG computed by Program 19.13 and its transitive
closure for the digraph
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
19.145 Do empirical studies to estimate the expected size of the kernel DAG
for various types of digraphs (see Exercises 19.11–18).
19.10 Perspective
In this chapter, we have considered algorithms for solving the
topological-sorting, transitive-closure, and shortest-paths problems for
digraphs and for DAGs, including fundamental algorithms for finding
cycles and strong components in digraphs. These algorithms have nu-
merous important applications in their own right and also serve as the
basis for the more difficult problems involving weighted graphs that
we consider in the next two chapters. Worst-case running times of
these algorithms are summarized in Table 19.3.
In particular, a common theme through the chapter has been
the solution of the abstract–transitive-closure problem, where we wish
to support an ADT that can determine quickly, after preprocessing,
whether there is a directed path from one given vertex to another.
Despite a lower bound that implies that our worst-case preprocessing
costs are significantly higher than V 2 , the method discussed in Sec-
tion 19.7 melds the basic methods from throughout the chapter into
a simple solution that provides optimal performance for many types
of digraphs—the significant exception being dense DAGs. The lower
bound suggests that better guaranteed performance on all graphs will
be difficult to achieve, but we can use these methods to get good per-
formance on practical graphs.
The goal of developing an algorithm with performance charac-
teristics similar to the union-find algorithms of Chapter 1 for dense di-
graphs remains elusive. Ideally, we would like to define an ADT where
we can add directed edges or test whether one vertex is reachable from
another and to develop an implementation where we can support all
the operations in constant time (see Exercises 19.157 through 19.159).
As discussed in Chapter 1, we can come close to that goal for undi-
rected graphs, but comparable solutions for digraphs or DAGs are still
not known. (Note that deleting edges presents a challenge even for
undirected graphs.) Not only does this dynamic reachability problem
have both fundamental appeal and direct application in practice, but
also it plays a critical role in the development of algorithms at a higher
level of abstraction. For example, reachability lies at the heart of the
problem of implementing the network simplex algorithm for the min-
cost flow problem, a problem-solving model of wide applicability that
we consider in Chapter 22.
214 §19.10 CHAPTER NINETEEN
digraphs
cycle detect E DFS
transitive closure V(E+V) DFS from each vertex
single-source shortest paths E DFS
all shortest paths V(E+V) DFS from each vertex
strong components E Kosaraju, Tarjan, or Gabow
transitive closure E + v(v + x) kernel DAG
DAGs
acyclic verify E DFS or source queue
topological sort E DFS or source queue
transitive closure V(V+E) DFS
transitive closure V(V+X) DFS/dynamic programming
out this chapter, some of the facts that we have discovered about
digraphs are expressions of more general mathematical phenomena,
and many of our algorithms have applicability at levels of abstrac-
tion different from that at which we have been working. On the one
hand, the concept of intractability tells us that we might encounter
fundamental roadblocks in our quest for efficient algorithms that can
guarantee efficient solutions to some problems. On the other hand, the
classic algorithms described in this chapter are of fundamental impor-
tance and have broad applicability, as they provide efficient solutions
to problems that arise frequently in practice and would otherwise be
difficult to solve.
Exercises
19.148 Adapt Programs 17.13 and 17.14 to implement an ADT function
for printing an Euler path in a digraph, if one exists. Explain the purpose of
any additions or changes that you need to make in the code.
19.149 Draw the dominator tree of the digraph
3-7 1-4 7-8 0-5 5-2 3-0 2-9 0-6 4-9 2-6
6-4 1-5 8-2 9-0 8-3 4-5 2-3 1-6 3-5 7-6.
•• 19.150 Write an ADT function that uses DFS to create a parent-link repre-
sentation of the dominator tree of a given digraph (see reference section).
◦ 19.151 Find a transitive reduction of the digraph
3-7 1-4 7-8 0-5 5-2 3-0 2-9 0-6 4-9 2-6
6-4 1-5 8-2 9-0 8-3 4-5 2-3 1-6 3-5 7-6.
.32
5-3 .18 7
might represent time or the cost of performing tasks or of waiting for 7-4 .46 1
.2
tasks to be performed. 5-4 .40 1
.60
0-5 .60
.46
.51
Questions that entail cost minimization naturally arise for such
6-4 .51
situations. We examine algorithms for two such problems: (i) find the 7-0 .31 3 .3
4
8
.1
lowest-cost way to connect all of the points, and (ii) find the lowest- 7-6 .25
.40
7-1 .21 5 4
cost path between two given points. The first type of algorithm, which
is useful for undirected graphs that represent objects such as electric
Figure 20.1
circuits, finds a minimum spanning tree; it is the subject of this chap- A weighted undirected graph
ter. The second type of algorithm, which is useful for digraphs that and its MST
represent objects such as an airline route map, finds the shortest paths; A weighted undirected graph is a
it is the subject of Chapter 21. These algorithms have broad applica- set of weighted edges. The MST
bility beyond circuit and map applications, extending to a variety of is a set of edges of minimal total
weight that connects the vertices
problems that arise on weighted graphs.
(black in the edge list, thick edges
When we study algorithms that process weighted graphs, our in the graph drawing). In this par-
intuition is often supported by thinking of the weights as distances: ticular graph, the weights are pro-
we speak of “the vertex closest to x,” and so forth. Indeed, the term portional to the distances between
“shortest path” embraces this bias. Despite numerous applications the vertices, but the basic algo-
rithms that we consider are appro-
where we actually do work with distance and despite the benefits of priate for general graphs and make
geometric intuition in understanding the basic algorithms, it is impor- no assumptions about the weights
tant to remember that the weights do not need to be proportional to a (see Figure 20.2).
219
220 CHAPTER TWENTY
5-3 .37 7
7-4 .12 .2
8 but the efficiency of implementations varies widely, and researchers still
5-4 .78 1 seek better methods. In this section, we examine three classical algo-
.65
0-5 .65
.01
.12
6-4 .01
rithms that are easily understood at a conceptual level; in Sections 20.3
7-0 .49 3 .6 through 20.5, we examine implementations of each in detail; and in
5
7
.3
7-6 .65
7-1 .28 5
.78
4 Section 20.6, we consider comparisons of and improvements on these
basic approaches.
Figure 20.2 Definition 20.1 A minimum spanning tree (MST) of a weighted
Arbitrary weights
graph is a spanning tree whose weight (the sum of the weights of its
In this example, the edge weights edges) is no larger than the weight of any other spanning tree.
are arbitrary and do not relate to
the geometry of the drawn graph If the edge weights are all positive, it suffices to define the MST as
representation at all. This example
also illustrates that the MST is not the set of edges with minimal total weight that connects all the vertices,
necessarily unique if edge weights as such a set of edges must form a spanning tree. The spanning-tree
may be equal: we get one MST by condition in the definition is included so that it applies for graphs that
including 3-4 (shown) and a differ- may have negative edge weights (see Exercises 20.2 and 20.3).
ent MST by including 0-5 instead
(although 7-6, which has the same If edges can have equal weights, the minimum spanning tree may
weight as those two edges, is not not be unique. For example, Figure 20.2 shows a graph that has two
in any MST). different MSTs. The possibility of equal weights also complicates the
MINIMUM SPANNING TREES 221
Exercises
20.1 Assume that the weights in a graph are positive. Prove that you can
rescale them by adding a constant to all of them or by multiplying them all by
a constant without affecting the MSTs, provided only that the rescaled weights
are positive.
222 §20.1 CHAPTER TWENTY
20.2 Show that, if edge weights are positive, a set of edges that connects all
the vertices whose weights sum to a quantity no larger than the sum of the
weights of any other set of edges that connects all the vertices is an MST.
20.3 Show that the property stated in Exercise 20.2 holds for graphs with
negative weights, provided that there are no cycles whose edges all have non-
positive weights.
◦ 20.4 How would you find a maximum spanning tree of a weighted graph?
20.5 Show that if a graph’s edges all have distinct weights, the MST is unique.
20.6 Consider the assertion that a graph has a unique MST only if its edge
weights are distinct. Give a proof or a counterexample.
• 20.7 Assume that a graph has t < V edges with equal weights and that all
other weights are distinct. Give upper and lower bounds on the number of
different MSTs that the graph might have.
20.1 Representations
In this chapter, we concentrate on weighted undirected graphs—the
most natural setting for MST problems. Extending the basic graph rep-
resentations from Chapter 17 to represent weighted graphs is straight-
forward: In the adjacency-matrix representation, the matrix can con-
tain edge weights rather than Boolean values; in the adjacency-lists
representation, we add a field to the list elements that represent edges,
for the weights.
In our examples, we generally assume that edge weights are real
numbers between 0 and 1. This decision does not conflict with various
alternatives that we might need in applications, because we can explic-
itly or implicitly rescale the weights to fit this model (see Exercises 20.1
and 20.8). For example, if the weights are positive integers less than
a known maximum value, we can divide them all by the maximum
value to convert them to real numbers between 0 and 1.
We use the same basic graph ADT interface that we used in
Chapter 17 (see Program 17.1), except that we add a weight field to
the edge data type, as follows:
typedef struct { int v; int w; double wt; } Edge;
Edge EDGE(int, int, double);
To avoid proliferation of simple types, we use double for edge weights
throughout this chapter and Chapter 21. If we wanted to do so,
we could build a more general ADT interface and use any data type
MINIMUM SPANNING TREES §20.1 223
0 1 2 3 4 5 6 7
0 * .32 .29 * * .60 .51 .31 0 7 .31 5 .60 2 .29 1 .32 6 .51
2 .29 * * * * * * * 2 0 .29
Figure 20.3
that supports addition, subtraction, and comparisons, since we do Weighted-graph representa-
little more with the weights than to accumulate sums and to make tions (undirected)
decisions based on their values. In Chapter 22, our algorithms are The two standard representations
concerned with comparing linear combinations of edge weights, and of weighted undirected graphs
include weights with each edge
the running time of some algorithms depends on arithmetic properties representation, as illustrated in
of the weights, so we switch to integer weights to allow us to more the adjacency-matrix (left) and
easily analyze the algorithms. adjacency-lists (right) representa-
tion of the graph depicted in Fig-
We use sentinel weights to indicate the absence of an edge. An-
ure 20.1. The adjacency matrix is
other straightforward approach would be to use the standard adja- symmetric and the adjacency lists
cency matrix to indicate the existence of edges and a parallel matrix contain two nodes for each edge,
to hold weights. With sentinels, many of our algorithms do not need as in unweighted directed graphs.
Nonexistent edges are represented
to explicitly test whether or not an edge exists. The adjacency-matrix
by a sentinel value in the matrix
representation of our sample graph is shown in Figure 20.3; Pro- (indicated by asterisks in the figure)
gram 20.1 gives the implementation details of the weighted-graph ADT and are not present at all in the
for an adjacency-matrix representation. It uses an auxiliary function lists. Self-loops are absent in both
of the representations illustrated
that allocates a matrix of weights and fills it with the sentinel weight. here because MST algorithms are
Inserting an edge amounts to storing the weight in two places in the simpler without them; other al-
matrix—one for each orientation of the edge. The sentinel weight value gorithms that process weighted
that indicates the absence of an edge is larger than all other weights, graphs use them (see Chapter 21).
not 0, which represents an edge of length 0 (an alternative would be
to disallow edges of length 0). As is true of algorithms that use the
adjacency-matrix representation for unweighted graphs, the running
time of any algorithm that uses this representation is proportional to
V 2 (to initialize the matrix) or higher.
Similarly, Program 20.2 gives the implementation details of the
weighted-graph ADT for an adjacency-lists representation. A vertex-
indexed array associates each vertex with a linked list of that vertex’s
224 §20.1 CHAPTER TWENTY
#include <stdlib.h>
#include "GRAPH.h"
struct graph { int V; int E; double **adj; };
Graph GRAPHinit(int V)
{ int v;
Graph G = malloc(sizeof *G);
G->adj = MATRIXdouble(V, V, maxWT);
G->V = V; G->E = 0;
return G;
}
void GRAPHinsertE(Graph G, Edge e)
{
if (G->adj[e.v][e.w] == maxWT) G->E++;
G->adj[e.v][e.w] = e.wt;
G->adj[e.w][e.v] = e.wt;
}
#include "GRAPH.h"
typedef struct node *link;
struct node { int v; double wt; link next; };
struct graph { int V; int E; link *adj; };
link NEW(int v, double wt, link next)
{ link x = malloc(sizeof *x);
x->v = v; x->wt = wt; x->next = next;
return x;
}
Graph GRAPHinit(int V)
{ int i;
Graph G = malloc(sizeof *G);
G->adj = malloc(V*sizeof(link));
G->V = V; G->E = 0;
for (i = 0; i < V; i++) G->adj[i] = NULL;
return G;
}
void GRAPHinsertE(Graph G, Edge e)
{ link t;
int v = e.v, w = e.w;
if (v == w) return;
G->adj[v] = NEW(w, e.wt, G->adj[v]);
G->adj[w] = NEW(v, e.wt, G->adj[w]);
G->E++;
}
226 §20.1 CHAPTER TWENTY
0
0 7 .31 2 .29
0-2 .29 2 7
1 7 .21
4-3 .34 1 4 6
2 0 .29
5-3 .18 3
3 5 .18 4 .34
7-4 .46
5
4 7 .46 3 .34
7-0 .31
5 3 .18
7-6 .25 0 1 2 3 4 5 6 7
6 7 .25
7-1 .21 st 0 7 0 4 7 3 7 0
7 1 .21 6 .25 0 .31 4 .46
val 0 .21 .29 .34 .46 .18 .25 .31
Figure 20.4
MST representations How should we represent the MST itself? The MST of a graph
This figure depicts various rep-
G is a subgraph of G that is also a tree, so we have numerous options.
resentations of the MST in Fig- Chief among them are
ure 20.1. The most straightforward • A graph
is a list of its edges, in no particu-
• A linked list of edges
lar order (left). The MST is also a
sparse graph, and might be repre- • An array of edges
sented with adjacency lists (center). • A vertex-indexed array with parent links
The most compact is a parent-link Figure 20.4 illustrates these options for the example MST in Fig-
representation: we choose one of
the vertices as the root and keep ure 20.1. Another alternative is to define and use an ADT for trees.
two vertex-indexed arrays, one The same tree might have many different representations in any
with the parent of each vertex in of the schemes. In what order should the edges be presented in the
the tree, the other with the weight
list-of-edges representation? Which node should be chosen as the
of the edge from each vertex to
its parent (right). The orientation root in the parent-link representation (see Exercise 20.15)? Generally
of the tree (choice of root vertex) speaking, when we run an MST algorithm, the particular MST repre-
is arbitrary, not a property of the sentation that results is an artifact of the algorithm used, rather than
MST. We can convert from any
reflecting any important features of the MST.
one of these representations to any
other in linear time. From an algorithmic point of view, the choice of MST repre-
sentation is of little consequence, because we can convert easily from
each of these representations to any of the others. To convert from the
graph representation to an array of edges, we can use the GRAPHedges
function in the graph ADT. To convert from the parent-link represen-
tation in an array st to an array of edges in an array mst, we can use
the simple loop
for (k = 1; k < G->V; k++) mst[k] = EDGE(k, st[k]);
This code is for the typical case where the MST is rooted at 0, and it
does not put the dummy edge 0-0 onto the MST edge list.
These two conversions are trivial, but how do we convert from
the array-of-edges representation to the parent-link representation?
MINIMUM SPANNING TREES §20.1 227
Exercises
20.8 Build a graph ADT that uses integer weights, but keep track of the
minimum and maximum weights in the graph and include an ADT function
that always returns weights that are numbers between 0 and 1.
20.12 Write a program that generates a random complete graph that has
weights chosen from a Gaussian distribution.
• 20.13 Write a program that generates V random points in the plane, then
builds a weighted graph by connecting each pair of points within a given
distance d of one another with an edge whose weight is the distance. (see
Exercise 17.60). Determine how to set d so that the expected number of
edges is E.
few basic terms from graph theory that we define next make possible
2
a concise statement of this property, which follows. 0
6
Definition 20.2 A cut in a graph is a partition of the vertices into 7
two disjoint sets. A crossing edge is one that connects a vertex in one 1
set with a vertex in the other. 3
crossing edge that is not in any MST and let T be any MST; or suppose
that T is an MST that contains no minimal crossing edge and let e be 2
0
any minimal crossing edge. In either case, T is an MST that does not 6
contain the minimal crossing edge e. Now consider the graph formed 7
by adding e to T . This graph has a cycle that contains e, and that cycle 1
must contain at least one other crossing edge—say, f , which is equal 3
or higher weight than e (since e is minimal). We can get a spanning
5 4
tree of equal or lower weight by deleting f and adding e, contradicting
either the minimality of T or the assumption that e is not in T .
2
0
If a graph’s edge weights are distinct, it has a unique MST; and
6
the cut property says that the shortest crossing edge for every cut must 7
be in the MST. When equal weights are present, we may have multiple 1
minimal crossing edges. At least one of them will be in any given MST
3
and the others may be present or absent.
5 4
Figure 20.5 illustrates several examples of this cut property. Note
that there is no requirement that the minimal edge be the only MST
edge connecting the two sets; indeed, for typical cuts there are several Figure 20.5
MST edges that connect a vertex in one set with a vertex in the other. If Cut property
we could be sure that there were only one such edge, we might be able These four examples illustrate
to develop divide-and-conquer algorithms based on judicious selection Property 20.1. If we color one set
of vertices gray and another set
of the sets; but that is not the case. white, then the shortest edge con-
We use the cut property as the basis for algorithms to find MSTs, necting a gray vertex with a white
and it also can serve as an optimality condition that characterizes one belongs to an MST.
230 §20.2 CHAPTER TWENTY
2 MSTs. Specifically, the cut property implies that every edge in an MST
0
is a minimal crossing edge for the cut defined by the vertices in the two
6
7 subtrees connected by the edge.
1 The second property, which we refer to as the cycle property, has
to do with identifying edges that do not have to be in a graph’s MST.
3
That is, if we ignore these edges, we can still find an MST.
5 4
Property 20.2 (Cycle property) Given a graph G, consider the graph
G defined by adding an edge e to G. Adding e to an MST of G and
2
0 deleting a maximal edge on the resulting cycle gives an MST of G .
6
7
Proof : If e is longer than all the other edges on the cycle, it cannot
1 be on an MST of G , because of Property 20.1: Removing e from
3 any such MST would split the latter into two pieces, and e would not
be the shortest edge connecting vertices in each of those two pieces,
5 4
because some other edge on the cycle must do so. Otherwise, let t be
a maximal edge on the cycle created by adding e to the MST of G.
2
0 Removing t would split the original MST into two pieces, and edges of
6 G connecting those pieces are no shorter than t; so e is a minimal edge
7
in G connecting vertices in those two pieces. The subgraphs induced
1
by the two subsets of vertices are identical for G and G , so an MST
3 for G consists of e and the MSTs of those two subsets.
5 4 In particular, note that if e is maximal on the cycle, then we have
shown that there exists an MST of G that does not contain e (the MST
of G).
Figure 20.6
Cycle property Figure 20.6 illustrates this cycle property. Note that the process
Adding the edge 1-3 to the graph of taking any spanning tree, adding an edge that creates a cycle, and
in Figure 20.1 invalidates the MST
then deleting a maximal edge on that cycle gives a spanning tree of
(top). To find the MST of the new
graph, we add the new edge to weight less than or equal to the original. The new tree weight will be
the MST of the old graph, which less than the original if and only if the added edge is shorter than some
creates a cycle (center). Deleting edge on the cycle.
the longest edge on the cycle (4-7) The cycle property also serves as the basis for an optimality
yields the MST of the new graph
(bottom). One way to verify that condition that characterizes MSTs: It implies that every edge in a
a spanning tree is minimal is to graph that is not in a given MST is a maximal edge on the cycle that it
check that each edge not on the forms with MST edges.
MST has the largest weight on the The cut property and the cycle property are the basis for the
cycle that it forms with tree edges.
For example, in the bottom graph, classical algorithms that we consider for the MST problem. We con-
4-6 has the largest weight on the sider edges one at a time, using the cut property to accept them as
cycle 4-6-7-1-3-4. MST edges or the cycle property to reject them as not needed. The
MINIMUM SPANNING TREES §20.2 231
edge does not create a cycle, it is the only crossing edge, and since we
consider the edges in sorted order, it is a minimal edge and therefore
in an MST. The basis for the induction is the V individual vertices;
once we have chosen V − 1 edges, we have one tree (the MST). No
unexamined edge is shorter than an MST edge, and all would create
a cycle, so ignoring all of the rest of the edges leaves an MST, by the
0 1 cycle property.
choice as the closest, and v would have led to the choice only one of
its lower-numbered neighbors, not both.
20.22 Suppose that a graph has distinct edge weights. Does its shortest edge
have to belong to the MST? Prove that it does or give a counterexample.
20.23 Answer Exercise 20.22 for the graph’s longest edge.
20.24 Give a counterexample that shows why the following strategy does not
necessarily find the MST: “Start with any vertex as a single-vertex MST, then
add V − 1 edges to it, always taking next a minimal edge incident upon the
vertex most recently added to the MST.”
20.25 Suppose that a graph has distinct edge weights. Does a minimal edge
on every cycle have to belong to the MST? Prove that it does or give a coun-
terexample.
20.26 Given an MST for a graph G, suppose that an edge in G is deleted.
Describe how to find an MST of the new graph in time proportional to the
number of edges in G.
20.27 Show the MST that results when you repeatedly apply the cycle property
to the graph in Figure 20.1, taking the edges in the order given.
20.28 Prove that repeated application of the cycle property gives an MST.
20.29 Describe how each of the algorithms described in this section can be
adapted (if necessary) to the problem of finding a minimal spanning forest of
a weighted graph (the union of the MSTs of its connected components).
2 2
Figure 20.8 0
0
0 0
Prim’s MST algorithm 6 6
2 7
7 7
The first step in computing the 1 6
1 1
MST with Prim’s algorithm is to
add 0 to the tree. Then we find all 3 3
the edges that connect 0 to other
5 4 5 4
vertices (which are not yet on the
tree) and keep track of the shortest 0-2 0-7 0-1 0-6 0-5 7-4 0-5
(top left). The edges that connect
tree vertices with nontree vertices
(the fringe) are shadowed in gray
and listed below each graph draw- 2 2
0 0 0
ing. For simplicity in this figure, 0
6 6 2 7
we list the fringe edges in order 7 2 7
of their length, so that the short- 1 6 4
1 1
est is the first in the list. Different
implementations of Prim’s algo- 3 3
rithm use different data structures 5 4 5 4
to maintain this list and to find the
minimum. The second step is to 0-7 0-1 0-6 0-5 4-3 4-5
move the shortest edge 0-2 (along
with the vertex that it takes us to)
from the fringe to the tree (second
from top, left). Third, we move 2 2 0
0 0 0
0-7 from the fringe to the tree, re-
6 6 2 7
place 0-1 by 7-1 and 0-6 by 7-6 7 2 7 7
on the fringe (because adding 7 1 6 4
1 1
to the tree brings 1 and 6 closer 3
to the tree), and add 7-4 to the 3 3
fringe (because adding 7 to the tree 5 4 5 4
makes 7-4 an edge that connects
a tree vertex with a nontree ver- 7-1 7-6 7-4 0-5 3-5
tex) (third from top, left). Next, we
move edge 7-1 to the tree (bottom,
left). To complete the computation,
we take 7-6, 7-4, 4-3, and 3-5 2 2 0
0 0 0
off the queue, updating the fringe
6 6 2 7
after each insertion to reflect any 7 2 7 7
shorter or new paths discovered 1
1 6 4
1 1
(right, top to bottom). 3
An oriented drawing of the 3 3
5
growing MST is shown at the right 5 4 5 4
of each graph drawing. The ori-
entation is an artifact of the algo- 7-6 7-4 0-5
0 1 2 3 4 5 6 7
rithm: we generally view the MST st 0 7 0 4 7 3 7 0
itself as a set of edges, unordered wt 0 .21 .29 .34 .46 .18 .25 .31
and unoriented.
MINIMUM SPANNING TREES §20.3 237
change. The key is to note that our interest is in the shortest distance
from each nontree vertex to the tree. When we add a vertex v to the
tree, the only possible change for each nontree vertex w is that adding
v brings w closer than before to the tree. In short, we do not need to
check the distance from w to all tree vertices—we just need to keep
track of the minimum and check whether the addition of v to the tree
necessitates that we update that minimum.
To implement this idea, we need data structures that can give us
the following information:
• For each tree vertex, its parent in the MST
• For each nontree vertex, the closest tree vertex
• For each tree vertex, the length of its parent link
• For each nontree vertex, its distance to the tree
The simplest implementation for each of these data structures is a
vertex-indexed array, though we have various options to save space by
combining arrays or using structures.
Program 20.3 is an implementation of Prim’s algorithm for an
adjacency-matrix graph ADT implementation. It uses the arrays st,
fr, and wt for these four data structures, with st and fr for the first
two (respectively), and wt for both the third and fourth. For a tree
vertex v, the entry wt[v] is the length of the parent link (corresponding
to st[v]); for a nontree vertex w, the entry wt[w] is the distance to the
tree (corresponding to fr[w]). The implementation is packaged as a
function GRAPHmstV that takes st and wt as client-supplied arrays. If
desired, we could add a wrapper function GRAPHmst to build an edge
list (or some other) MST representation, as discussed in Section 20.1.
After adding a new edge (and vertex) to the tree, we have two
tasks to accomplish:
• Check to see whether adding the new edge brought any nontree
vertex closer to the tree.
• Find the next edge to add to the tree.
The implementation in Program 20.3 accomplishes both of these tasks
with a single scan through the nontree vertices, updating wt[w] and
fr[w] if v-w brings w closer to the tree, then updating the current
minimum if wt[w] (the distance from w to fr[w] indicates that w is
closer to the tree than any other vertex with a lower index.
Property 20.6 Using Prim’s algorithm, we can find the MST of a
dense graph in linear time.
238 §20.3 CHAPTER TWENTY
nontree edges to find the one that is closest to the tree does not repre-
sent excessive extra cost. But in a sparse graph, we can expect to use
substantially fewer than V steps to perform each of these operations.
The crux of the strategy that we will use to do so is to focus on the set
of potential edges to be added next to the MST—a set that we call the
fringe. The number of fringe edges is typically substantially smaller
than the number of nontree edges, and we can recast our description
of the algorithm as follows. Starting with a self loop to a start vertex
on the fringe and an empty tree, we perform the following operation
until the fringe is empty:
Move a minimal edge from the fringe to the tree. Visit the
vertex that it leads to, and put onto the fringe any edges
that lead from that vertex to an nontree vertex, replacing
the longer edge when two edges on the fringe point to the
same vertex.
From this formulation, it is clear that Prim’s algorithm is nothing
more than a generalized graph search (see Section 18.8), where the
fringe is a priority queue based on a delete the minimum operation
(see Chapter 9). We refer to generalized graph searching with priority
queues as priority-first search (PFS). With edge weights for priorities,
PFS implements Prim’s algorithm.
This formulation encompasses a key observation that we made
already in connection with implementing BFS in Section 18.7. An
even simpler general approach is to simply keep on the fringe all of
the edges that are incident upon tree vertices, letting the priority-
queue mechanism find the shortest one and ignore longer ones (see
Exercise 20.37). As we saw with BFS, this approach is unattractive
because the fringe data structure becomes unnecessarily cluttered with
edges that will never get to the MST. The size of the fringe could grow
to be proportional to E (with whatever attendant costs having a fringe
this size might involve), while the PFS approach just outlined ensures
that the fringe will never have more than V entries.
As with any implementation of a general algorithm, we have
a number of available approaches for interfacing with priority-queue
ADTs. One approach is to use a priority queue of edges, as in our gener-
alized graph-search implementation of Program 18.10. Program 20.4
is an implementation that is essentially equivalent to Program 18.10
but uses a vertex-based approach so that it can use the index priority-
240 §20.3 CHAPTER TWENTY
Figure 20.9
Prim’s MST algorithm
This sequence shows how the MST
grows as Prim’s algorithm discovers
1/4, 1/2, 3/4, and all of the edges
in the MST (top to bottom). An
oriented representation of the full
MST is shown at the right.
MINIMUM SPANNING TREES §20.3 241
◦ 20.31 Answer Exercise 20.30 for graphs in which all vertices have the same
fixed degree t.
◦ 20.32 Answer Exercise 20.30 for general sparse graphs that have V vertices
and E edges. Since the running time depends on the weights of the edges and
on the degrees of the vertices, do a worst-case analysis. Exhibit a family of
graphs for which your worst-case bound is confirmed.
20.33 Show, in the style of Figure 20.8, the result of computing the MST of
the network defined in Exercise 20.21 with Prim’s algorithm.
• 20.34 Describe a family of graphs with V vertices and E edges for which
the worst-case running time of the PFS implementation of Prim’s algorithm is
confirmed.
•• 20.35 Develop a reasonable generator for random graphs with V vertices
and E edges such that the running time of the PFS implementation of Prim’s
algorithm (Program 20.4) is nonlinear.
20.36 Convert Program 20.4 for use in an adjacency-matrix graph ADT
implementation.
◦ 20.37 Modify Program 20.4 so that it works like Program 18.8, in that it
keeps on the fringe all edges incident upon tree vertices. Run empirical studies
to compare your implementation with Program 20.4, for various weighted
graphs (see Exercises 20.9–14).
• 20.38 Provide an implementation of Prim’s algorithm that makes use of your
representation-independent graph ADT from Exercise 17.51.
20.39 Suppose that you use a priority-queue implementation that maintains
a sorted list. What would be the worst-case running time for graphs with V
vertices and E edges, to within a constant factor? When would this method
be appropriate, if ever? Defend your answer.
◦ 20.40 An MST edge whose deletion from the graph would cause the MST
weight to increase is called a critical edge. Show how to find all critical edges
in a graph in time proportional to E lg V .
20.41 Run empirical studies to compare the performance of Program 20.3
to that of Program 20.4, using an unordered array implementation for the
priority queue, for various weighted graphs (see Exercises 20.9–14).
• 20.42 Run empirical studies to determine the effect of using an index-heap–
tournament (see Exercise 9.53) priority-queue implementation instead of Pro-
gram 9.12 in Program 20.4, for various weighted graphs (see Exercises 20.9–
14).
20.43 Run empirical studies to analyze tree weights as a function of V , for
various weighted graphs (see Exercises 20.9–14).
20.44 Run empirical studies to analyze maximum fringe size as a function of
V , for various weighted graphs (see Exercises 20.9–14).
246 §20.4 CHAPTER TWENTY
20.46 Run empirical studies to study the dependence of the results of Exer-
cises 20.44 and 20.45 on the start vertex. Would it be worthwhile to use a
random starting point?
Figure 20.12
Kruskal’s MST algorithm
2 2
Given a list of a graph’s edges in
0 0 arbitrary order (left edge list), the
6 6 first step in Kruskal’s algorithm is
7 7
to sort them by weight (right edge
1 1 list). Then we go through the edges
on the list in order of their weight,
3 3
adding edges that do not create
5 4 5 4 cycles to the MST. We add 5-3
(the shortest edge), then 7-1, then
7-6 (left), then 0-2 (right, top) and
0-7 (right, second from top). The
edge with the next largest weight,
2 2
0-6 .51 5-3 .18 0 0 0-1, creates a cycle and is not in-
6 6 cluded. Edges that we do not add
0-1 .32 7-1 .21 7 7 to the MST are shown in gray on
0-2 .29 7-6 .25 the sorted list. Then we add 4-3
1 1
4-3 .34 0-2 .29 (right, third from top). Next, we re-
5-3 .18 7-0 .31 3 3 ject 5-4 because it causes a cycle,
then we add 7-4 (right, bottom).
7-4 .46 0-1 .32 5 4 5 4
Once the MST is complete, any
5-4 .40 4-3 .34 edges with larger weights would
0-5 .60 5-4 .40 cause cycles and be rejected (we
6-4 .51 7-4 .46 stop when we have added V − 1
2 2 edges to the MST). These edges are
7-0 .31 * 0-6 .51 0 0
marked with asterisks on the sorted
7-6 .25 * 6-4 .51 6 6
7 7 list.
7-1 .21 * 0-5 .60
1 1
3 3
5 4 5 4
2 2
0 0
6 6
7 7
1 1
3 3
5 4 5 4
248 §20.4 CHAPTER TWENTY
on the weights (see Chapter 2). Recall that the function lg∗ E is the
number of iterations of the binary logarithm function before the result
is less than 1, which is less than 5 if E is less than 265536 . In other
words, these adjustments make Kruskal’s algorithm effectively linear
in most practical circumstances.
Typically, the cost of finding the MST with Kruskal’s algorithm
is even lower than the cost of processing all edges, because the MST
is complete well before a substantial fraction of the (long) graph edges
is ever considered. We can take this fact into account to reduce the
running time significantly in many practical situations, by keeping
edges that are longer than the longest MST edge entirely out of the
sort. One easy way to accomplish this objective is to use a priority
queue, with an implementation that does the construct operation in
linear time and the delete the minimum operation in logarithmic time.
For example, we can achieve these performance characteristics
with a standard heap implementation, using bottom-up construction
250 §20.4 CHAPTER TWENTY
have to examine all the edges. For example, the graph might consist of
tight clusters of vertices all connected together by short edges, with one
outlier connected to one of the vertices by a long edge. Despite such
anomalies, the partial-sort approach is probably worthwhile because
it offers significant gain when it applies and incurs little if any extra
cost.
Historical perspective is relevant and instructive here as well.
Kruskal presented this algorithm in 1956, but, again, the relevant ADT
implementations were not carefully studied for many years, so the per-
formance characteristics of implementations such as the priority-queue
version of Program 20.5 were not well understood until the 1970s.
Other interesting historical notes are that Kruskal’s paper mentioned
a version of Prim’s algorithm (see Exercise 20.54) and that Boruvka
mentioned both approaches. Efficient implementations of Kruskal’s
method for sparse graphs preceded implementations of Prim’s method
for sparse graphs because union-find (and sort) ADTs came into use
before priority-queue ADTs. Generally, as was true of implementa-
tions of Prim’s algorithm, advances in the state of the art for Kruskal’s
algorithm are attributed primarily to advances in ADT performance.
On the other hand, the applicability of the union-find abstraction to
Kruskal’s algorithm and the applicability of the priority-queue ab-
straction to Prim’s algorithm have been prime motivations for many
researchers to seek better implementations of those ADTs.
Exercises
20.48 Show, in the style of Figure 20.12, the result of computing the MST of
the network defined in Exercise 20.21 with Kruskal’s algorithm.
◦ 20.49 Run empirical studies to analyze the length of the longest edge in the
MST and the number of graph edges that are not longer than that one, for
various weighted graphs (see Exercises 20.9–14).
• 20.50 Develop an implementation of the union-find ADT that implements
find in constant time and union in time proportional to lg V .
20.51 Run empirical tests to compare your ADT implementation from Exer-
cise 20.50 to weighted union-find with halving (Program 1.4) when Kruskal’s
algorithm is the client, for various weighted graphs (see Exercises 20.9–14).
Separate out the cost of sorting the edges so that you can study the effects of
the change both on the total cost and on the part of the cost associated with
the union-find ADT.
20.52 Develop an implementation based on the idea described in the text
where we integrate Kruskal’s algorithm with quicksort so as to check MST
252 §20.5 CHAPTER TWENTY
2 membership of each edge as soon as we know that all smaller edges have been
0
6
checked.
7
◦ 20.53 Adapt Kruskal’s algorithm to implement two ADT functions that fill
1
a client-supplied vertex-indexed array classifying vertices into k clusters with
3 the property that no edge of length greater than d connects two vertices in
different clusters. For the first function, take k as an argument and return d;
5 4
for the second, take d as an argument and return k. Test your program on
random Euclidean neighbor graphs and on grid graphs (see Exercises 20.11
0
2 and 20.13) of various sizes for various values of k and d.
6
7 20.54 Develop an implementation of Prim’s algorithm that is based on pre-
sorting the edges.
1
3 The next MST algorithm that we consider is also the oldest. Like
5 4 Kruskal’s algorithm, we build the MST by adding edges to a spreading
forest of MST subtrees; but we do so in stages, adding several MST
Figure 20.14 edges at each stage. At each stage, we find the shortest edge that
Boruvka’s MST algorithm connects each MST subtree with a different one, then add all such
The diagram at the top shows a di- edges to the MST.
rected edge from each vertex to
Again, our union-find ADT from Chapter 1 leads to an efficient
its closest neighbor. These edges
show that 0-2, 1-7, and 3-5 are implementation. For this problem, it is convenient to extend the in-
each the shortest edge incident on terface to make the find operation available to clients. We use this
both their vertices, 6-7 is 6’s short- function to associate an index with each subtree, so that we can tell
est edge, and 4-3 is 4’s shortest quickly to which subtree a given vertex belongs. With this capabil-
edge. These edges all belong to
the MST and comprise a forest of ity, we can implement efficiently each of the necessary operations for
MST subtrees (center), as computed Boruvka’s algorithm.
by the first phase of Boruvka’s al- First, we maintain a vertex-indexed array that identifies, for each
gorithm. In the second phase, the MST subtree, the nearest neighbor. Then, we perform the following
algorithm completes the MST com-
putation (bottom) by adding the operations on each edge in the graph:
edge 0-7, which is the shortest • If it connects two vertices in the same tree, discard it.
edge incident on any of the ver- • Otherwise, check the nearest-neighbor distances between the two
tices in the subtrees it connects, trees the edge connects and update them if appropriate.
and the edge 4-7, which is the
shortest edge incident on any of After this scan of all the graph edges, the nearest-neighbor array has
the vertices in the bottom subtree. the information that we need to connect the subtrees. For each vertex
MINIMUM SPANNING TREES §20.5 253
takes less than logd V steps, and delete the minimum takes time propor-
tional to d logd V . By Property 20.8, this behavior leads to a running
time proportional to V d logd V + E logd V for Prim’s algorithm, which
is linear for graphs that are not sparse.
E V C H J P K K* e/E B e/E
density 2
20000 10000 2 22 27 9 11 1.00 14 3.3
50000 25000 8 69 84 24 31 1.00 38 3.3
100000 50000 15 169 203 49 66 1.00 89 3.8
200000 100000 30 389 478 108 142 1.00 189 3.6
density 20
20000 1000 2 5 4 20 6 5 .20 9 4.2
50000 2500 12 12 13 130 16 15 .28 25 4.6
100000 5000 14 27 28 34 31 .30 55 4.6
200000 10000 29 61 61 73 68 .35 123 5.0
density 100
100000 1000 14 17 17 24 30 19 .06 51 4.6
250000 2500 36 44 44 130 81 53 .05 143 5.2
500000 5000 73 93 93 181 113 .06 312 5.5
1000000 10000 151 204 198 377 218 .06 658 5.6
density V /2.5
400000 1000 61 60 59 20 137 78 .02 188 4.5
2500000 2500 597 409 400 128 1056 687 .01 1472 5.5
Key:
C extract edges only
H Prim’s algorithm (adjacency lists/indexed heap)
J Johnson’s version of Prim’s algorithm (d-heap priority queue)
P Prim’s algorithm (adjacency-matrix representation)
K Kruskal’s algorithm
K* Partial-sort version of Kruskal’s algorithm
B Boruvka’s algorithm
e edges examined (union operations)
MINIMUM SPANNING TREES §20.6 259
10
Program 20.7 Multiway heap PQ implementation
18 29
These fixUp and fixDown functions for the heap PQ implementation 34 32 46 40
(see Program 9.5), maintain a d-way heap; so delete the minimum takes
60 51 31
time proportional to d, but decrease key requires less than logd V steps.
For d = 2, these functions are equivalent to Programs 9.3 and 9.4, 1 2 3 4 5 6 7 8 9 10
respectively. 10 18 29 34 32 46 40 60 51 31
graphs (choose the largest value that does not slow down the algo-
rithm), but a small fixed value (such as 4, 5, or 6) will be fine except
possibly for some particular huge classes of graphs that have atypical
characteristics.
Using d-heaps is not effective for sparse graphs because d has to
be an integer greater than or equal to 2, a condition that implies that
we cannot bring the asymptotic running time lower than V lg V . If the
density is a small constant, then a linear-time MST algorithm would
have to run in time proportional to V .
The goal of developing practical algorithms for computing the
MST of sparse graphs in linear time remains elusive. A great deal
of research has been done on variations of Boruvka’s algorithm as
the basis for nearly linear-time MST algorithms for extremely sparse
graphs (see reference section). Such research still holds the potential
to lead us eventually to a practical linear-time MST algorithm and has
even shown the existence of a randomized linear-time algorithm. While
these algorithms are generally quite complicated, simplified versions
of some of them may yet be shown to be useful in practice. In the
meantime, we can use the basic algorithms that we have considered
here to compute the MST in linear time in most practical situations,
perhaps paying an extra factor of lg V for some sparse graphs.
Exercises
◦ 20.66 [V. Vyssotsky] Develop an implementation of the algorithm discussed
in Section 20.2 that builds the MST by adding edges one at a time and deleting
the longest edges on the cycle formed (see Exercise 20.28). Use a parent-link
representation of a forest of MST subtrees. Hint: Reverse pointers when
traversing paths in trees.
20.67 Run empirical tests to compare the running time of your implementa-
tion in Exercise 20.66 with that of Kruskal’s algorithm, for various weighted
graphs (see Exercises 20.9–14). Check whether randomizing the order in
which the edges are considered affects your results.
• 20.68 Describe how you would find the MST of a graph so large that only V
edges can fit into main memory at once.
• 20.71 Develop a generator for random connected cubic graphs (each vertex
of degree 3) that have random weights on the edges. Fine-tune for this case the
MST algorithms that we have discussed, then determine which is the fastest.
◦ 20.72 For V = 106 , plot the ratio of the upper bound on the cost for Prim’s
algorithm with d-heaps to E as a function of the density d, for d in the range
from 1 to 100.
◦ 20.73 Table 20.2 suggests that the standard implementation of Kruskal’s al-
gorithm is significantly faster than the partial-sort implementation for low-
density graphs. Explain this phenomenon.
• 20.74 Run an empirical study, in the style of Table 20.2, for random complete
graphs that have Gaussian weights (see Exercise 20.12).
This fact is a direct consequence of two basic facts about points in the
plane that we discuss in detail in Part 7. First, a graph known as the
Delauney triangulation contains the MST, by definition. Second, the
Delauney triangulation is a planar graph whose number of edges is
proportional to N .
◦ 20.76 Develop a fast version of Prim’s algorithm for computing the Euclidean
MST of a uniformly distributed set of points in the plane based on ignoring
distant points until the tree approaches them.
•• 20.77 Develop an algorithm that, given a set of N points in the plane, finds
a set of edges of cardinality proportional to N that is certain to contain the
MST and is sufficiently easy to compute that you can develop a concise and
efficient implementation of your algorithm.
264 §20.7 CHAPTER TWENTY
◦ 20.78 Given a random set of N points in the unit square (uniformly dis-
tributed), empirically determine a value of d, to within two decimal places,
such that the set of edges defined by all pairs of points within distance d of
one another is 99 percent certain to contain the MST.
◦ 20.79 Work Exercise 20.78 for points where each coordinate is drawn from
a Gaussian distribution with mean 0.5 and standard deviation 0.1.
• 20.80 Describe how you would improve the performance of Kruskal’s and
Boruvka’s algorithm for sparse Euclidean graphs.
CHAPTER TWENTY-ONE
Shortest Paths
265
266 CHAPTER TWENTY-ONE
0-1 .41 0 1 2 3 4 5
1-2 .51 0 0 0 0 5 .29 1 .41
2-3 .50 0 0 .41 .29
1 1 1 0 4 .32 2 .51
4-3 .36 1 0 .51 .32
3-5 .38 5 2 2 0 3 .50
3-0 .45 2 0 .50
0-5 .29 3 3 0 0 .45 5 .38
4 3 .45 0 .38
5-4 .21 4 4 0 2 .32 3 .36
1-4 .32 4 .32 .36 0
4-2 .32 3 2 5 .29 .21 0 5 5 0 1 .29 4 .21
5-1 .29
Figure 21.1
Sample network and represen- for the unweighted undirected graphs of Chapters 17 and 18, the un-
tations weighted digraphs of Chapter 19, or the weighted undirected graphs
This network (weighted digraph) of Chapter 20 can be derived (see Exercise 21.9).
is shown in four representations: When we work with networks, it is generally convenient to keep
list of edges, drawing, adjacency self-loops in all the representations. This convention allows algorithms
matrix, and adjacency lists (left to
the flexibility to use a sentinel maximum-value weight to indicate that a
right). As we did for MST algo-
rithms, we often use edge weights vertex cannot be reached from itself. In our examples, we use self-loops
that are proportional to their of weight 0, although positive-weight self-loops certainly make sense
lengths in the drawing, but we in many applications. Many applications also call for parallel edges,
do not insist on this rule because
perhaps with differing weights. As we mentioned in Section 20.1,
most shortest-paths algorithms han-
dle arbitrary nonnegative weights various options for ignoring or combining such edges are appropriate
(negative weights do present spe- in various different applications. In this chapter, for simplicity, none of
cial challenges). The adjacency- our examples use parallel edges, and we do not allow parallel edges in
matrix and adjacency-lists repre- the adjacency-matrix representation; we also do not check for parallel
sentations include weights with
each edge representation, as in edges or remove them in adjacency lists.
weighted undirected graphs. The All the connectivity properties of digraphs that we considered in
adjacency matrix is not symmet- Chapter 19 are relevant in networks. In that chapter, we wished to
ric, and the adjacency lists contain know whether it is possible to get from one vertex to another; in this
one node for each edge (as in un-
weighted digraphs). Nonexistent chapter, we take weights into consideration—we wish to find the best
edges are represented by a sentinel way to get from one vertex to another.
value in the matrix (blank in the
figure) and are not present at all Definition 21.1 A shortest path between two vertices s and t in a
in the lists. Self-loops of length 0 network is a directed simple path from s to t with the property that
are present because they simplify no other such path has a lower weight.
our implementations of shortest-
paths algorithms. They are omit- This definition is succinct, but its brevity masks points worth
ted from the list of edges at left for examining. First, if t is not reachable from s, there is no path at all,
economy and to indicate the typ-
ical scenario where we add them and therefore there is no shortest path. For convenience, the algorithms
by convention when we create an that we consider often treat this case as equivalent to one in which there
adjacency-matrix or adjacency-lists exists an infinite-weight path from s to t. Second, as we did for MST
representation. algorithms, we use networks where edge weights are proportional to
SHORTEST PATHS 267
0-1 .99 0 0 0
1-2 .51 0
2-3 .50 3
1 3 5 3 5
4-3 .36 5
3-5 .38 5 1 1
0-3 .45
0-5 .83 1 4 4 2
4
5-4 .21
4
1-4 .10 2
4-2 .41 3 2 2
5-1 .10
Figure 21.2
edge lengths in examples, but the definition has no such requirement Shortest-path trees
and our algorithms (other than the one in Section 21.5) do not make
A shortest-path tree (SPT) defines
this assumption. Indeed, shortest-paths algorithms are at their best shortest paths from the root to
when they discover counterintuitive shortcuts, such as a path between other vertices (see Definition 21.2).
two vertices that passes through several other vertices but has total In general, different paths may
have the same length, so there
weight smaller than that of a direct edge connecting those vertices.
may be multiple SPTs defining the
Third, there may be multiple paths of the same weight from one vertex shortest paths from a given vertex.
to another; we typically are content to find one of them. Figure 21.2 In the example network shown at
shows an example with general weights that illustrates these points. left, all shortest paths from 0 are
subgraphs of the DAG shown to
The restriction in the definition to simple paths is unnecessary
the right of the network. A tree
in networks that contain edges that have nonnegative weight, because rooted at 0 spans this DAG if and
any cycle in a path in such a network can be removed to give a path only if it is an SPT for 0. The two
that is no longer (and is shorter unless the cycle comprises zero-weight trees at right are such trees.
edges). But when we consider networks with edges that could have
negative weight, the need for the restriction to simple paths is readily
apparent: Otherwise, the concept of a shortest path is meaningless if
there is a cycle in the network that has negative weight. For example,
suppose that the edge 3-5 in the network in Figure 21.1 were to have
weight -.38, and edge 5-1 were to have weight -.31. Then, the weight
of the cycle 1-4-3-5-1 would be .32 + .36 - .38 - .31 = -.01,
and we could spin around that cycle to generate arbitrarily short paths.
Note carefully that, as is true in this example, it is not necessary for
all the edges on a negative-weight cycle to be of negative weight; what
counts is the sum of the edge weights. For brevity, we use the term
negative cycle to refer to directed cycles whose total weight is negative.
In the definition, suppose that some vertex on a path from s to t
is also on a negative cycle. In this case, the existence of a (nonsimple)
shortest path from s to t would be a contradiction, because we could
use the cycle to construct a path that had a weight lower than any
given value. To avoid this contradiction, we include in the definition
268 CHAPTER TWENTY-ONE
0 .41 0-1 .82 0-5-4-2 .86 0-5-4-3 .50 0-5-4 .29 0-5
1.13 1-4-3-0 1 .51 1-2 .68 1-4-3 .32 1-4 1.06 1-4-3-5
.95 2-3-0 1.17 2-3-0-1 2 .50 2-3 1.09 2-3-5-4 .88 2-3-5
.45 3-0 .67 3-5-1 .91 3-5-4-2 0 3 .59 3-5-4 .38 3-5
.81 4-3-0 1.03 4-3-5-1 .32 4-2 .36 4-3 0 4 .74 4-3-5
1.02 5-4-3-0 .29 5-1 .53 5-4-2 .57 5-4-3 .21 5-4 0 5
Figure 21.3
Source–sink shortest path Given a start vertex s and a finish
All shortest paths
vertex t, find a shortest path in the graph from s to t. We refer to the
This table gives all the shortest
start vertex as the source and to the finish vertex as the sink, except paths in the network of Figure 21.1
in contexts where this usage conflicts with the definition of sources and their lengths. This network is
(vertices with no incoming edges) and sinks (vertices with no outgoing strongly connected, so there ex-
edges) in digraphs. ist paths connecting each pair of
vertices.
Single-source shortest paths Given a start vertex s, find shortest The goal of a source-sink
paths from s to each other vertex in the graph. shortest-path algorithm is to com-
All-pairs shortest paths Find shortest paths connecting each pute one of the entries in this ta-
pair of vertices in the graph. For brevity, we sometimes use the term ble; the goal of a single-source
shortest-paths algorithm is to com-
all shortest paths to refer to this set of V 2 paths.
pute one of the rows in this ta-
If there are multiple shortest paths connecting any given pair ble; and the goal of an all-pairs
shortest-paths algorithm is to com-
of vertices, we are content to find any one of them. Since paths pute the whole table. Generally,
have varying number of edges, our implementations provide ADT we use more compact representa-
functions that allow clients to trace paths in time proportional to the tions, which contain essentially the
paths’ lengths. Any shortest path also implicitly gives us the shortest- same information and allow clients
to trace any path in time propor-
path length, but our implementations explicitly provide lengths. In
tional to its number of edges (see
summary, to be precise, when we say “find a shortest path” in the Figure 21.8).
problem statements just given, we mean “compute the shortest-path
length and a way to trace a specific path in time proportional to that
path’s length.”
Figure 21.3 illustrates shortest paths for the example network in
Figure 21.1. In networks with V vertices, we need to specify V paths
to solve the single-source problem, and to specify V 2 paths to solve
the all-pairs problem. In our implementations, we use a representation
more compact than these lists of paths; we first noted it in Section 18.7,
and we consider it in detail in Section 21.1.
In modern implementations, we build our algorithmic solutions
to these problems into ADT implementations that allow us to build
efficient client programs that can solve a variety of practical graph-
processing problems. For example, as we see in Section 21.3, an
attractive way to package a solution to the all-pairs shortest-paths
problem is as a preprocessing function in an ADT interface that pro-
270 CHAPTER TWENTY-ONE
cities, or to minimize the cost of the trip. Costs in such networks might
involve functions of time, of money, or of other complicated resources.
For example, flights between two cities typically take more time in one
direction than the other, because of prevailing winds. Air travelers also
know that the fare is not necessarily a simple function of the distance
between the cities—situations where it is cheaper to use a circuitous
route (or endure a stopover) than to take a direct flight are all too
common. Such complications can be handled by the basic shortest-
paths algorithms that we consider in this chapter; these algorithms are
designed to handle any positive costs.
The fundamental shortest-paths computations suggested by these
applications only scratch the surface of the applicability of shortest-
paths algorithms. In Section 21.6, we consider problems from applica-
tions areas that appear unrelated to these natural ones, in the context
of a discussion of reduction, a formal mechanism for proving relation-
ships among problems. We solve problems for these applications by
transforming them into abstract shortest-paths problems that do not
have the intuitive geometric feel of the problems just described. In-
deed, some applications lead us to consider shortest-paths problems in
networks with negative weights. Such problems can be far more diffi-
cult to solve than are problems where negative weights cannot occur.
Shortest-paths problems for such applications not only bridge a gap
between elementary algorithms and unsolved algorithmic challenges,
but also lead us to powerful and general problem-solving mechanisms.
As we did with MST algorithms in Chapter 20, we often mix
the weight, cost, and distance metaphors. Again, we normally ex-
ploit the natural appeal of geometric intuition even when working in
more general settings with arbitrary edge weights; thus we refer to
the “length” of paths and edges when we should say “weight” and to
one path as “shorter” than another when we should say that it “has
lower weight.” We also might say that v is “closer” to s than w when
we should say that “the lowest-weight directed path from s to v has
weight lower than that of the lowest-weight directed path s to w,”
and so forth. This usage is inherent in the standard use of the term
“shortest paths” and is natural even when weights are not related to
distances (see Figure 21.2); however, when we expand our algorithms
to handle negative weights in Section 21.6, we must abandon such
usage.
272 CHAPTER TWENTY-ONE
◦ 21.6 Implement a network ADT for dense graphs with weights between 0
and 1, based on Program 17.3. Include a random-network generator based on
Program 17.8 and edge-weight generators as described in Exercise 21.5. Write
client programs to generate random networks for both weight distributions
SHORTEST PATHS §21.1 273
with a well-chosen set of values of V and E, so that you can use them to run
empirical tests on graphs drawn from these models.
21.7 Implement a representation-independent network ADT function that
builds a network by taking edges with weights (pairs of integers between 0
and V − 1 with weights between 0 and 1) from standard input.
• 21.8 Write a program that generates V random points in the plane, then
builds a network with edges (in both directions) connecting all pairs of points
within a given distance d of one another (see Exercise 17.72), setting each
edge’s weight to the distance between the two points that that edge connects.
Determine how to set d so that the expected number of edges is E.
◦ 21.9 Write functions that use a network ADT to implement ADTs for undi-
rected graphs (weighted and unweighted) and for unweighted digraphs.
21.10 The following table from a published road map purports to give the
length of the shortest routes connecting the cities. It contains an error. Correct
the table. Also, add a table that shows how to execute the shortest routes, in
the style of Figure 21.4.
Providence Westerly New London Norwich
Providence – 53 54 48
Westerly 53 – 18 101
New London 54 18 – 12
Norwich 48 101 12 –
0-1 .41 0 0
0 3
1-2 .51 1 1
2-3 .50 5 1 4 2
4-3 .36 5 5
4 5 1
3-5 .38
4 4
3-0 .45 2 3 0
0-5 .29
5-4 .21 3 2 3 2
1-4 .32
4-2 .32 0 1 2 3 4 5 0 1 2 3 4 5
5-1 .29 st 0 0 4 4 5 0 st 5 4 3 3 3 4
wt 0 .41 .32 .36 .21 .29 wt .29 .32 .50 0 .36 .21
Figure 21.6
There may be multiple paths of the same length connecting a Shortest paths trees
given pair of nodes, so SPTs are not necessarily unique. In general, as
The shortest paths from 0 to the
illustrated in Figure 21.2, if we take shortest paths from a vertex s to other nodes in this network are
every vertex reachable from s in a network and from the subnetwork 0-1, 0-5-4-2, 0-5-4-3, 0-5-4,
induced by the edges in the paths, we may get a DAG. Different shortest and 0-5, respectively. These paths
define a spanning tree, which is
paths connecting pairs of nodes, may each appear as a subpath in
depicted in three representations
some longer path containing both nodes. Because of such effects, we (gray edges in the network draw-
generally are content to compute any SPT for a given digraph and start ing, oriented tree, and parent links
vertex. with weights) in the center. Links
in the parent-link representation
Our algorithms generally initialize the entries in the wt array
(the one that we typically com-
with the sentinel value maxWT. That value needs to be sufficiently small pute) run in the opposite direction
that the addition in the relaxation test does not cause overflow and than links in the digraph, so we
sufficiently large that no simple path has a larger weight. For example, sometimes work with the reverse
digraph. The spanning tree defined
if edge weights are between 0 and 1, we can use the value V. Note
by shortest paths from 3 to each
that we have to take extra care to check our assumptions when using of the other nodes in the reverse is
sentinels in networks that could have negative weights. For example, depicted on the right. The parent-
if both vertices have the sentinel value, the relaxation code just given link representation of this tree gives
the shortest paths from each of the
takes no action if e.wt is nonnegative (which is probably what we
other nodes to 2 in the original
intend in most implementations), but it will change wt[w] and st[w] graph. For example, we can find
if the weight is negative. the shortest path 0-5-4-3 from 0
The st array is a parent-link representation of the shortest-paths to 3 by following the links st[0] =
5, st[5] = 4, and st[4] = 3.
tree, with the links pointing in the direction opposite from that of the
links in the network, as illustrated in Figure 21.6. We can compute
the shortest path from s to t by traveling up the tree from t to s,
visiting the vertices on the path in reverse order (t, st[t], st[st[t]],
and so forth). In some situations, reverse order is precisely what we
want. For example, if we are to return a linked-list representation of
the path, we can (adhering to our usual conventions for linked lists,
where NEW is a function that allocates memory for a node, fills in the
276 §21.1 CHAPTER TWENTY-ONE
node’s fields from its arguments, and returns a link to the node) use
code like the following:
p = NEW(t, null);
while (t != s)
{ t = st[t]; p = NEW(t, p); }
return p;
Another option is to use similar code to push the vertices on the path
i onto a stack—then the client program can visit the vertices on the path
in order by popping them from the stack.
On the other hand, if we simply want to print or otherwise
s t
process the vertices on the path, reverse order is inconvenient because
we have to go all the way through the path in reverse order to get to
the first vertex, then go back through the path to process the vertices.
i One approach to get around this difficulty is to work with the reverse
graph, as illustrated in Figure 21.6.
s t
Next, we consider path relaxation, which is the basis of some of
our all-pairs algorithms: Does going through a given vertex lead us to
a shorter path that connects two other given vertices? For example,
suppose that, for three vertices s, x, and t, we wish to know whether
Figure 21.7 it is better to go from s to x and then from x to t or to go from s to t
Path relaxation without going through x. For straight-line connections in a Euclidean
These diagrams illustrate the relax- space, the triangle inequality tells us that the route through x cannot
ation operation that underlies our be shorter than the direct route from s to t, but for paths in a network,
all-pairs shortest-paths algorithms.
We keep track of the best known
it could be (see Figure 21.7). To determine which, we need to know
path between all pairs of vertices the lengths of paths from s to x, x to t, and of those from s to t (that do
and ask whether a vertex i is evi- not include x). Then, we simply test whether or not the sum of the first
dence that the shortest known path two is less than the third; if it is, we update our records accordingly.
from s to t could be improved. In
the top example, it is not; in the Path relaxation is appropriate for all-pairs solutions where we
bottom example, it is. Whenever maintain the lengths of the shortest paths that we have encountered
we encounter a vertex i such that between all pairs of vertices. Specifically, in all-pairs–shortest-paths
the length of the shortest known
code of this kind, we maintain an array d such that d[s][t] is the
path from s to i plus the length
of the shortest known path from i shortest-path length from s to t, and we also maintain an array p such
to t is smaller than the length of that p[s][t] is the next vertex on a shortest path from s to t. We
the shortest known path from s to refer to the former as the distances matrix and the latter as the paths
t, then we update our data struc-
matrix. Figure 21.8 shows the two matrices for our example network.
tures to indicate that we now know
a shorter path from s to t (head The distances matrix is a prime objective of the computation, and
towards i first). we use the paths matrix because it is clearly more compact than, but
SHORTEST PATHS §21.1 277
0-1 .41
1-2 .51 0 0 1 2 3 4 5 0 1 2 3 4 5
2-3 .50 1 0 0 .41 .82 .86 .50 .29 0 0 1 5 5 5 5
4-3 .36
3-5 .38 5 1 1.13 0 .51 .68 .32 1.06 1 4 1 2 4 4 4
3-0 .45 2 .95 1.17 0 .50 1.09 .88 2 3 3 2 3 3 3
0-5 .29 4 3 .45 .67 .91 0 .59 .38 3 0 5 5 3 5 5
5-4 .21
1-4 .32 4 .81 1.03 .32 .36 0 .74 4 3 3 2 3 4 3
4-2 .32 3 2 5 1.02 .29 .53 .57 .21 0 5 4 1 4 4 4 5
5-1 .29
Figure 21.8
carries the same information as, the full list of paths that is illustrated All shortest paths
in Figure 21.3.
The two matrices on the right are
In terms of these data structures, path relaxation amounts to the compact representations of all the
following code: shortest paths in the sample net-
work on the left, containing the
if (d[s][t] > d[s][x] + d[x][t])
same information in the exhaus-
{ d[s][t] = d[s][x] + d[x][t]; p[s][t] = p[s][x]; } tive list in Figure 21.3. The dis-
Like edge relaxation, this code reads as a restatement of the informal tances matrix on the left contains
the shortest-path length: the en-
description that we have given, so we use it directly in our implemen-
try in row s and column t is the
tations. More formally, path relaxation reflects the following: length of the shortest path from s
to t. The paths matrix on the right
Property 21.1 If a vertex x is on a shortest path from s to t, then contains the information needed to
that path consists of a shortest path from s to x followed by a shortest execute the path: the entry in row
path from x to t. s and column t is the next vertex
on the path from s to t.
Proof : By contradiction. We could use any shorter path from s to x
or from x to t to build a shorter path from s to t.
1-0 .41
0
Figure 21.9 2-1 .51 0
1
All shortest paths in a net- 3-2 .50
3
3-4 .36
work 5-3 .38
5
4 2
These diagrams depict the SPTs 0-3 .45
4
5-0 .29 5 1 0 1 2 3 4 5
for each vertex in the reverse of 4-5 .21 st 0 4 3 0 3 4
the network in Figure 21.8 (0 to 4-1 .32 3 2
wt 0 1.13 .95 .45 .81 1.02
5, top to bottom), as network sub- 2-4 .32
1-5 .29
trees (left), oriented trees (center), 0 1
and parent-link representation in- 1
cluding a vertex-indexed array for 5 0
5
path length (right). Putting the ar- 3
rays together to form path and dis- 4
4 2 0 1 2 3 4 5
tance matrices (where each array
st 1 1 3 5 3 1
becomes a column) gives the solu- 3 2
wt .41 0 1.17 .67 1.03 .29
tion to the all-pairs shortest-paths
problem illustrated in Figure 21.8. 0 2
1
4 1
5
5
4
0 3 0 1 2 3 4 5
st 5 2 2 5 2 4
3 2
wt .82 .51 0 .91 .32 .53
0 3
1
4 2
5
5 1
4 0
0 1 2 3 4 5
st 5 4 3 3 3 4
3 2
wt .86 .68 .50 0 .36 .57
0 4
1
5 1
5 0 3
4 2
0 1 2 3 4 5
st 5 4 3 5 4 4
3 2
wt .50 .32 1.09 .59 0 .21
0 5
1 0 3
5 4 2
4 1
0 1 2 3 4 5
st 5 4 3 5 3 5
3 2
wt .29 1.06 .88 .38 .74 0
SHORTEST PATHS §21.1 279
Exercises
21.11 Draw the SPT from 0 for the network defined in Exercise 21.1 and for
its reverse. Give the parent-link representation of both trees.
21.12 Consider the edges in the network defined in Exercise 21.1 to be undi-
rected edges, such that each edge corresponds to equal-weight edges in both
directions in the network. Answer Exercise 21.11 for this corresponding
network.
280 §21.2 CHAPTER TWENTY-ONE
21.13 Change the direction of edge 0-2 in Figure 21.2. Draw two different
SPTs that are rooted at 2 for this modified network.
21.14 Write a code fragment that, using a parent-link representation of an
SPT, prints out each of the paths to the root.
21.15 Write a code fragment that, using a paths-matrix representation of
all shortest paths in a network, prints out all of those paths, in the style of
Figure 21.3.
21.16 Give an example that shows how we could know that a path from s
to t is shortest without knowing the length of a shorter path from s to x for
some x.
adds a shortest path to that vertex. But all other paths to x must begin
with a tree path followed by an edge to a vertex not on the tree. By
construction, all such paths are longer than the one from s to x that is
under consideration.
The same argument shows that Dijkstra’s algorithm solves the
source–sink shortest-paths problem, if we start at the source and stop
when the sink comes off the priority queue.
The proof breaks down if the edge weights could be negative,
because it assumes that a path’s length does not decrease when we add
more edges to the path. In a network with negative edge weights, this
assumption is not valid because any edge that we encounter might lead
to some tree vertex and might have a sufficiently large negative weight
to give a path to that vertex shorter than the tree path. We consider
this defect in Section 21.7 (see Figure 21.28).
Figure 21.10 shows the evolution of an SPT for a sample graph
when computed with Dijkstra’s algorithm; Figure 21.11 shows an
oriented drawing of a larger SPT tree. Although Dijkstra’s algorithm
differs from Prim’s MST algorithm in only the choice of priority, SPT
trees are different in character from MSTs. They are rooted at the
start vertex and all edges are directed away from the root, whereas
MSTs are unrooted and undirected. We represent MSTs as directed,
rooted trees when we use Prim’s algorithm, but such structures are
still different in character from SPTs (compare the oriented drawing in
Figure 20.9 with the drawing in Figure 21.11). Indeed, the nature of
the SPT somewhat depends on the choice of start vertex, as well, as
depicted in Figure 21.12.
Dijkstra’s original implementation, which is suitable for dense
graphs, is precisely like Prim’s MST algorithm. Specifically, we simply
change the definition of P in Program 20.3 from
#define P G->adj[v][w]
(the edge weight) to
#define P wt[v] + G->adj[v][w]
(the distance from the source to the edge’s destination). This change
gives the classical implementation of Dijkstra’s algorithm: we grow an
SPT one edge at a time, each time checking all the nontree vertices to
find an edge to move to the tree whose destination vertex is a nontree
vertex of minimal distance from the source.
282 §21.2 CHAPTER TWENTY-ONE
3 2
SHORTEST PATHS §21.2 283
Figure 21.11
Property 21.3 With Dijkstra’s algorithm, we can find any SPT in a Shortest-paths spanning tree
dense network in linear time.
This figure illustrates the progress
Proof : As for Prim’s MST algorithm, it is immediately clear, from of Dijkstra’s algorithm in solving
the single-source shortest-paths
inspection of the code of Program 20.3, that the running time is pro- problem in a random Euclidean
portional to V 2 , which is linear for dense graphs. near-neighbor digraph (with di-
rected edges in both directions cor-
For sparse graphs, we can do better, by viewing Dijkstra’s al- responding to each line drawn), in
gorithm as a generalized graph-searching method that differs from the same style as Figures 18.13,
depth-first search (DFS), from breadth-first search (BFS), and from 18.24, and 20.9. The search tree is
similar in character to BFS because
Prim’s MST algorithm in only the rule used to add edges to the tree. vertices tend to be connected to
As in Chapter 20, we keep edges that connect tree vertices to nontree one another by short paths, but it
vertices on a generalized queue called the fringe, use a priority queue to is slightly deeper and less broad
implement the generalized queue, and provide for updating priorities because distances lead to slightly
longer paths than path lengths.
so as to encompass DFS, BFS, and Prim’s algorithm in a single imple-
mentation (see Section 20.3). This priority-first search (PFS) scheme
also encompasses Dijkstra’s algorithm. That is, changing the definition
of P in Program 20.4 to
#define P wt[v] + t->wt
(the distance from the source to the edge’s destination) gives an imple-
mentation of Dijkstra’s algorithm that is suitable for sparse graphs.
Program 21.1 is an alternative PFS implementation for sparse
graphs that is slightly simpler than Program 20.4 and that directly
284 §21.2 CHAPTER TWENTY-ONE
The general results that we considered concerning the perfor- Figure 21.12
SPT examples
mance of priority-first search (PFS) in Chapter 20 give us specific
These three examples show grow-
information about the performance of these implementations of Di-
ing SPTs for three different source
jkstra’s algorithm for sparse graphs (Program 21.1 and Program 20.4, locations: left edge (top), upper left
suitably modified). For reference, we restate those results in the present corner (center), and center (bot-
context. Since the proofs do not depend on the priority function, they tom).
apply without modification. They are worst-case results that apply
to both programs, although Program 20.4 may be more efficient for
many classes of graphs because it maintains a smaller fringe.
Property 21.4 For all networks and all priority functions, we can
compute a spanning tree with PFS in time proportional to the time
286 §21.2 CHAPTER TWENTY-ONE
This table summarizes the cost (worst-case running time) of various im-
plementations of Dijkstra’s algorithm. With appropriate priority-queue
implementations, the algorithm runs in linear time (time proportional to
V 2 for dense networks, E for sparse networks) except for networks that
are extremely sparse.
◦ 21.18 How would you find a second shortest path from s to t in a network?
21.19 Add a function to a standard network ADT that uses GRAPHspt to
compute the length of a shortest path connecting two given vertices s and t.
21.20 Add a function to a standard network ADT that uses GRAPHspt to find
the most distant vertex from a given vertex s (the vertex whose shortest path
from s is the longest).
21.21 Add a function to a standard network ADT that uses GRAPHspt to
compute the average of the lengths of the shortest paths from a given vertex
to each of the vertices reachable from it.
◦ 21.22 Add a function to a standard adjacency-lists network ADT that solves
the source–sink shortest-paths problem by using GRAPHspt to compute a
linked-list representation of a shortest path connecting two given vertices s
and t.
◦ 21.23 Add a function to a standard network ADT that solves the source–sink
shortest-paths problem by using GRAPHspt to fill the initial entries in a given
argument array with the successive vertex indices on a shortest path connecting
two given vertices s and t.
21.24 Develop an interface and implementation based on Program 21.1 to
push a shortest path connecting two given vertices s and t onto a user-supplied
stack.
21.25 Add a function to a standard adjacency-lists network ADT that finds
all vertices within a given distance d of a given vertex in a given network.
The running time of your function should be proportional to the size of the
subgraph induced by those vertices and the vertices incident on them.
21.26 Develop an algorithm for finding an edge whose removal causes maxi-
mal increase in the shortest-path length from one given vertex to another given
vertex in a given network.
• 21.27 Add a function to a standard adjacency-matrix network ADT that
performs a sensitivity analysis on the network’s edges with respect to a given
pair of vertices s and t: Compute a V-by-V array such that, for every u and v,
the entry in row u and column v is 1 if u-v is an edge in the network whose
weight can be increased without the shortest-path length from s to t being
increased and is 0 otherwise.
◦ 21.28 Add a function to a standard adjacency-lists network ADT that finds
a shortest path connecting one given set of vertices with another given set of
vertices in a given network.
21.29 Use your solution from Exercise 21.28 to implement a function that
finds a shortest path from the left edge to the right edge in a random grid
network (see Exercise 20.7).
21.30 Show that an MST of an undirected graph is equivalent to a bottleneck
SPT of the graph: For every pair of vertices v and w, it gives the path connecting
them whose longest edge is as short as possible.
290 §21.3 CHAPTER TWENTY-ONE
21.31 Run empirical studies to compare the performance of the two versions
of Dijkstra’s algorithm for the sparse graphs that are described in this sec-
tion (Program 21.1 and Program 20.4, with suitable priority definition), for
various networks (see Exercises 21.4–8). Use a standard-heap priority-queue
implementation.
21.32 Run empirical studies to learn the best value of d when using a d-heap
priority-queue implementation (see Program 20.7) for each of the three PFS
implementations that we have discussed (Program 18.10, Program 20.4 and
Program 21.1), for various networks (see Exercises 21.4–8).
• 21.33 Run empirical studies to determine the effect of using an index-
heap-tournament priority-queue implementation (see Exercise 9.53) in Pro-
gram 21.1, for various networks (see Exercises 21.4–8).
◦ 21.34 Run empirical studies to analyze height and average path length in
SPTs, for various networks (see Exercises 21.4–8).
21.35 Develop an implementation for the source–sink shortest-paths problem
that is based on initializing the priority queue with both the source and the
sink. Doing so leads to the growth of an SPT from each vertex; your main
task is to decide precisely what to do when the two SPTs collide.
• 21.36 Describe a family of graphs with V vertices and E edges for which the
worst-case running time of Dijkstra’s algorithm is achieved.
•• 21.37 Develop a reasonable generator for random graphs with V vertices and
E edges for which the running time of the heap-based PFS implementation of
Dijkstra’s algorithm is superlinear.
• 21.38 Write a client program that does dynamic graphical animations of Dijk-
stra’s algorithm. Your program should produce images like Figure 21.11 (see
Exercises 17.55 through 17.59). Test your program on random Euclidean
networks (see Exercise 21.8).
void GRAPHdiameter(Graph G)
{ int v, w, vMAX = 0, wMAX = 0;
double MAX = 0.0;
GRAPHspALL(G);
for (v = 0; v < G->V; v++)
for (w = 0; w < G->V; w++)
if (GRAPHspPATH(G, v, w) != G->V)
if (MAX < GRAPHspDIST(G, v, w))
{ vMAX = v; wMAX = w;
MAX = GRAPHspDIST(G, v, w); }
printf("Diameter is %f\n", MAX);
for (v = vMAX; v != wMAX; v = w)
{ printf("%d-", v);
w = GRAPHspPATH(G, v, wMAX); }
printf("%d\n", w);
}
of the SPT (the st array) for t in the reverse network. Since the lengths
of shortest paths are the same in both directions, we can also fill column
t in the distances matrix with the wt array to give the solution to the
single-source shortest-paths problem for t.
Program 21.4 is an ADT implementation based on these ideas.
It can be used with either the adjacency-matrix or the adjacency-lists
representation, but it is intended for use with sparse graphs, because
it takes advantage of the efficiency of Dijkstra’s algorithm for such
graphs.
void GRAPHspALL(Graph G)
{ int i, s, t;
double **d = MATRIXdouble(G->V, G->V, maxWT);
int **p = MATRIXint(G->V, G->V, G->V);
for (s = 0; s < G->V; s++)
for (t = 0; t < G->V; t++)
if ((d[s][t] = G->adj[s][t]) < maxWT)
p[s][t] = t;
for (i = 0; i < G->V; i++)
for (s = 0; s < G->V; s++)
if (d[s][i] < maxWT)
for (t = 0; t < G->V; t++)
if (d[s][t] > d[s][i]+d[i][t])
{ p[s][t] = p[s][i];
d[s][t] = d[s][i]+d[i][t]; }
G->dist = d; G->path = p;
}
Property 21.8 With Floyd’s algorithm, we can find all shortest paths
in a network in time proportional to V 3 .
0
0 1 2 3 4 5 0 1 2 3 4 5 Figure 21.14
1 0 0 .41 .29 0 0 1 5 Floyd’s algorithm
1 0 .51 .32 1 1 2 4 This sequence shows the construc-
5
2 0 .50 2 2 3 tion of the all-pairs shortest-paths
4 3 .45 .86 0 .38 3 0 0 3 5 matrices with Floyd’s algorithm.
4 .32 .36 0 4 2 3 4 For i from 0 to 5 (top to bottom),
3 2 5 .29 .21 0 5 1 4 5 we consider, for all s and t, all
of the paths from s to t having
0 1 2 3 4 5 0 1 2 3 4 5
0 no intermediate vertices greater
0 0 .41 .92 .73 .29 0 0 1 1 1 5
1 than i (the shaded vertices). Ini-
1 0 .51 .32 1 1 2 4
5
tially, the only such paths are the
2 0 .50 2 2 3
network’s edges, so the distances
4 3 .45 .86 1.37 0 1.18 .38 3 0 0 0 3 0 5 matrix (center) is the graph’s adja-
4 .32 .36 0 4 2 3 4 cency matrix and the paths matrix
3 2 5 .29 .80 .21 0 5 1 1 4 5 (right) is set with p[s][t] = t for
each edge s-t. For vertex 0 (top),
0 1 2 3 4 5 0 1 2 3 4 5
0 the algorithm finds that 3-0-1 is
0 0 .41 .92 1.42 .73 .29 0 0 1 1 1 1 5
1 shorter than the sentinel value that
1 0 .51 1.01 .32 1 1 2 2 4
5
is present because there is no edge
2 0 .50 2 2 3
3-1 and updates the matrices ac-
4 3 .45 .86 1.37 0 1.18 .38 3 0 0 0 3 0 5
cordingly. It does not do so for
4 .32 .36 0 4 2 3 4
paths such as 3-0-5, which is not
3 2 5 .29 .80 1.3 .21 0 5 1 1 1 4 5 shorter than the known path 3-5.
Next the algorithm considers paths
0 1 2 3 4 5 0 1 2 3 4 5
0 through 0 and 1 (second from top),
0 0 .41 .92 1.42 .73 .29 0 0 1 1 1 1 5
1 and finds the new shorter paths
1 1.46 0 .51 1.01 .32 1.39 1 2 1 2 2 4 2
5
0-1-2, 0-1-4, 3-0-1-2, 3-0-1-4,
2 .95 1.36 0 .50 1.68 .88 2 3 3 2 3 3 3
and 5-1-2. The third row from the
4 3 .45 .86 1.37 0 1.18 .38 3 0 0 0 3 0 5
top shows the updates correspond-
4 .81 1.22 .32 .36 0 .74 4 3 3 2 3 4 3
ing to shorter paths through 0, 1,
3 2 5 1.75 .29 .80 1.3 .21 0 5 1 1 1 1 4 5
and 2; and so forth.
Black numbers overstrik-
0 1 2 3 4 5 0 1 2 3 4 5
0 ing gray ones in the matrices in-
0 0 .41 1.09 .73
.92 1.42 .29 0 0 1 1 1 1 5
1 dicate situations where the algo-
1 1.13
1.46 0 .68 .32 1.39
.51 1.01 1.06 1 2
4 1 2 2
4 4 2
4
5
rithm finds a shorter path than one
2 .95 1.36 0 .50 1.68 .88 2 3 3 2 3 3 3
it found earlier. For example, .91
4 3 .45 .86 1.37 0 1.18 .38 3 0 0 0 3 0 5
overstrikes 1.37 in row 3 and col-
4 .81 1.22 .32 .36 0 .74 4 3 3 2 3 4 3
umn 2 in the bottom diagram be-
3 2 5 1.02
1.75 .29 .53
.80 .57
1.3 .21 0 5 1
4 1 1
4 1
4 4 5
cause the algorithm discovered that
0 1 2 3 4 5
3-5-4-2 is shorter than 3-0-1-2.
0 1 2 3 4 5
0
0 0 .41 .82 1.09
.92 .86 .73
.50 .29 0 0 1 1
5 1
5 1
5 5
1
1 1.13 0 .51 .68 .32 1.06 1 4 1 2 4 4 4
5
2 1.17
.95 1.36 0 1.09 .88
.50 1.68 2 3 3 2 3 3 3
4 3 .45 .67 1.37
.86 .91 0 .59 .38
1.18 3 0 0
5 0
5 3 0
5 5
4 1.03 .32
.81 1.22 .36 0 .74 4 3 3 2 3 4 3
3 2 5 1.02 .29 .53 .57 .21 0 5 4 1 4 4 4 5
298 §21.3 CHAPTER TWENTY-ONE
21.42 Show, in the style of Figure 21.14, the result of using Floyd’s algorithm
to compute all shortest paths of the network defined in Exercise 21.1.
◦ 21.43 Combine Program 20.3 and Program 21.4 to make an implementation
of the all-pairs shortest-paths ADT interface (based on Dijkstra’s algorithm)
for dense networks that does not require explicit computation of the reverse
network. Do not define a separate function for GRAPHpfs—put the code from
Program 20.3 directly in the inner loop, eliminate the argument arrays wt and
st, and put results directly in G->dist and G->path (or use local arrays d and
p as in Program 21.5).
21.44 Run empirical tests, in the style of Table 20.2, to compare Dijkstra’s
algorithm (Program 21.4 and Exercise 21.43) and Floyd’s algorithm (Pro-
gram 21.5) for various networks (see Exercises 21.4–8).
21.45 Run empirical tests to determine the number of times that Floyd’s and
Dijkstra’s algorithms update the values in the distances matrix, for various
networks (see Exercises 21.4–8).
21.46 Give a matrix in which the entry in row s and column t is equal to the
number of different simple directed paths connecting s and t in Figure 21.1.
21.47 Implement a network ADT function that can compute the path-count
matrix that is described in Exercise 21.46.
21.48 Develop an implementation of the abstract shortest-paths ADT for
sparse graphs that cuts the space cost to be proportional to V , by increasing
the query time to be proportional to V .
• 21.49 Develop an implementation of the abstract shortest-paths ADT for
sparse graphs that uses substantially less than O(V 2 ) space but supports queries
in substantially less than O(V ) time. Hint: Compute all shortest paths for a
subset of the vertices.
• 21.50 Develop an implementation of the abstract shortest-paths ADT for
sparse graphs that uses substantially less than O(V 2 ) space and (using ran-
domization) supports queries in constant expected time.
◦ 21.51 Develop an implementation of the shortest-paths ADT that takes the
lazy approach of using Dijkstra’s algorithm to build the SPT (and associated
distance vector) for each vertex s the first time that the client issues a shortest-
path query from s, then references the information on subsequent queries.
21.52 Modify the shortest-paths ADT and Dijkstra’s algorithm to handle
shortest-paths computations in networks that have weights on both vertices
and edges. Do not rebuild the graph representation (the method described in
Exercise 21.3); modify the code instead.
• 21.53 Build a small model of airline routes and connection times, perhaps
based upon some flights that you have taken. Use your solution to Exer-
cise 21.52 to compute the fastest way to get from one of the served destinations
to another. Then test your program on real data (see Exercise 21.4).
300 §21.4 CHAPTER TWENTY-ONE
0 .41
0 5 0 5
Figure 21.15
1 .51
2 .50
Computing longest paths in
3 .36 1 7 1 7 an acyclic network
4 .38
5 .45
2 8 3 4 2 8 3 4 In this network, each edge has
6 .21
7 .32
the weight associated with the
8 .32 6 9 6 9 vertex that it leads from, listed at
9 .29
0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10 the top left. Sinks have edges to
st 0 0 0 st 6 6 a dummy vertex 10, which is not
wt .41 .41 .41 wt .91 .91
shown in the drawings. The wt
array contains the length of the
0 5 0 5
longest known path to each ver-
tex from some source, and the
1 7 1 7
st array contains the previous
4 4
2 8 3 2 8 3 vertex on the longest path. This
figure illustrates the operation of
6 9 6 9
Program 21.6, which picks from
0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10 among the sources (the shaded
st 5 st 4
wt .45 wt 1.08 nodes in each diagram) using the
FIFO discipline, though any of the
0 5 0 5 sources could be chosen at each
step. We begin by removing 0
1 7 1 7 and checking each of its incident
4 4 edges, discovering one-edge paths
2 8 3 2 8 3
of length .41 to 1, 7, and 9. Next,
6 9 6 9 we remove 5 and record the one-
0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10 edge path from 5 to 10 (left, sec-
st 9 9 st 3 ond from top). Next, we remove
wt .70 .70 wt 1.27
9 and record the paths 0-9-4 and
0-9-6, of length .70 (left, third
0 5 0 5 from top). We continue in this
way, changing the arrays when-
1 7 1 7
ever we find longer paths. For ex-
4 4 ample, when we remove 7 (left,
2 8 3 2 8 3
second from bottom) we record
6 9 6 9
paths of length .73 to 8 and 3;
0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10 then, later, when we remove 6, we
st 7 7 st 8
wt .73 .73 wt 1.23 record longer paths (of length .91)
to 8 and 3 (right, top). The point
0 5 0 5 of the computation is to find the
longest path to the dummy node
1 7 1 7 10. In this case, the result is the
4 4
path 0-9-6-8-2, of length 1.73.
2 8 3 2 8 3
6 9 6 9
0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10
st 1 st 2
wt .92 wt 1.73
304 §21.4 CHAPTER TWENTY-ONE
0 1 2 3 4 5
0-1 -.41 Figure 21.17
1-2 -.51 0
2-3 -.50 1
3 0 All longest paths in an acyclic
4-3 -.36
2 0 -.50 network
5-3 -.38 5 4 -.32 -.82 0
0-3 -.45
Our method for computing all
5 -.53 -1.03 -.21 0
5-4 -.21 4 shortest paths in acyclic networks
1-4 -.32 1 0 -.82 -1.32 -.50 -.29 works even if the weights are neg-
4-2 -.32
1-5 -.29
3 2 0 0 -.41 -1.23 -1.73 -.91 -.70 ative. Therefore, we can use it to
compute longest paths, simply
by first negating all the weights,
as illustrated here for the net-
that we have examined in this chapter. Developing implementations work in Figure 21.16. The longest
simple path in this network is
of them is a worthwhile way to cement your understanding of both
0-1-5-4-2-3, of weight 1.73.
DAGs and shortest paths (see Exercises 21.62 through 21.65). All the
methods run in time proportional to V E in the worst case, with actual
costs dependent on the structure of the DAG. In principle, we might do
even better for certain sparse weighted DAGs (see Exercise 19.119).
Exercises
21.54 Give the solutions to the multisource shortest- and longest-paths prob-
lems for the network defined in Exercise 21.1, with the directions of edges
2-3 and 1-0 reversed.
21.55 Modify Program 21.6 such that it solves the multisource shortest-paths
problem for acyclic networks.
21.56 Give an implementation of GRAPHlpt that is derived from the source-
queue–based topological-sorting code of Program 19.8, performing the re-
laxation operations for each vertex just after that vertex is removed from the
source queue.
◦ 21.57 Define an ADT for the relaxation operation, provide implementations,
and modify Program 21.6 to use your ADT, such that you can use Pro-
gram 21.6 to solve the multisource shortest-paths problem, the multisource
longest-paths problem, and other problems, just by changing the relaxation
implementation.
21.58 Use your generic implementation from Exercise 21.57 to implement
ADT operations that return the length of the longest paths from any source
to any other vertex in a DAG, the length of the shortest such path, and the
number of vertices reachable via paths whose lengths fall within a given range.
• 21.59 Define properties of relaxation such that you can modify the proof of
Property 21.9 to apply an abstract version of Program 21.6 (such as the one
described in Exercise 21.57).
21.60 Show, in the style of Figure 21.16, the computation of the all-pairs
shortest-paths matrices for the network defined in Exercise 21.54 by Pro-
gram 21.7.
308 §21.5 CHAPTER TWENTY-ONE
◦ 21.61 Give an upper bound on the number of edge weights accessed by Pro-
gram 21.7, as a function of basic structural properties of the network. Write
a program to compute this function, and use it to estimate the accuracy of the
V E bound, for various acyclic networks (add weights as appropriate to the
models in Chapter 19).
◦ 21.62 Write a DFS-based solution to the multisource shortest-paths problem
for acyclic networks. Does your solution work correctly in the presence of
negative edge weights? Explain your answer.
21.63 Extend your solution to Exercise 21.62 to provide an implementation
of the all-pairs shortest-paths ADT interface for acyclic networks that builds
the all-paths and all-distances arrays in time proportional to V E.
21.64 Show, in the style of Figure 21.9, the computation of all shortest paths
of the network defined in Exercise 21.54 using the DFS-based method of
Exercise 21.63.
21.65 Modify Program 21.6 such that it solves the single-source shortest-
paths problem in acyclic networks, then use it to develop an implementation
of the all-pairs shortest-paths ADT interface for acyclic networks that builds
the all-paths and all-distances arrays in time proportional to V E.
◦ 21.66 Work Exercise 21.61 for the DFS-based (Exercise 21.63) and for
the topological-sort–based (Exercise 21.65) implementations of the all-pairs
shortest-paths ADT. What inferences can you draw about the comparative
costs of the three methods?
21.67 Run empirical tests, in the style of Table 20.2, to compare the three
programs for the all-pairs shortest-paths problem described in this section
(see Program 21.7, Exercise 21.63, and Exercise 21.65), for various acyclic
networks (add weights as appropriate to the models in Chapter 19).
will be shorter than the distance from s to d. The algorithm for the
source–sink shortest-paths problem that we examine in this section
takes advantage of these two properties to improve performance.
Often, Euclidean networks are also symmetric: Edges run in
both directions. As mentioned at the beginning of the chapter, such
networks arise immediately if, for example, we interpret the adjacency-
matrix or adjacency-lists representation of an undirected weighted Eu-
clidean graph (see Section 20.7) as a weighted digraph (network).
When we draw an undirected Euclidean network, we assume this in-
terpretation, to avoid proliferation of arrowheads in the drawings.
The basic idea is straightforward: Priority-first search provides
us with a general mechanism to search for paths in graphs. With
w
Dijkstra’s algorithm, we examine paths in order of their distance from
the start vertex. This ordering ensures that, when we reach the sink, we
have examined all paths in the graph that are shorter, none of which v
Proof : The proof of Property 21.2 applies: At the time that we add
a vertex x to the tree, the addition of the distance from x to d to the
priority does not affect the reasoning that the tree path from s to x
is a shortest path in the graph from s to x, since the same quantity
is added to the length of all paths to x. When d is added to the tree,
we know that no other path from s to d is shorter than the tree path,
because any such path must consist of a tree path followed by an edge
to some vertex w that is not on the tree, followed by a path from w to
d (whose length cannot be shorter than the distance from w to d); and,
by construction, we know that the length of the path from s to w plus
the distance from w to d is no smaller than the length of the tree path
from s to d.
The Euclidean heuristic affects the performance, but not the cor-
rectness, of Dijkstra’s algorithm for the source–sink shortest-paths
computation. As discussed in the proof of Property 21.2, using the
standard algorithm to solve the source–sink problem amounts to build-
ing an SPT that has all vertices closer to the start than the sink d. With
the Euclidean heuristic, the SPT contains just the vertices whose path
from s plus distance to d is smaller than the length of the shortest path
from s to d. We expect this tree to be substantially smaller for many
applications because the heuristic prunes a substantial number of long
paths. The precise savings is dependent on the structure of the graph
and the geometry of the vertices. Figure 21.19 shows the operation
of the Euclidean heuristic on our sample graph, where the savings are
substantial. We refer to the method as a heuristic because there is no
guarantee that there will be any savings at all: It could always be the
case that the only path from source to sink is a long one that wanders
arbitrarily far from the source before heading back to the sink (see
Exercise 21.80).
Figure 21.20 illustrates the basic underlying geometry that de-
scribes the intuition behind the Euclidean heuristic: If the shortest-path
length from s to d is z, then vertices examined by the algorithm fall
roughly within the ellipse defined as the locus of points x for which
the distance from s to x plus the distance from x to d is equal to z.
For typical Euclidean graphs, we expect the number of vertices in this
ellipse to be far smaller than the number of vertices in the circle of
radius z that is centered at the source (those that would be examined
by Dijkstra’s algorithm).
Precise analysis of the savings is a difficult analytic problem and
depends on models of both random point sets and random graphs
(see reference section). For typical situations, we expect that, if the
Figure 21.19
standard algorithm examines X vertices in computing a source–sink
Shortest path in a Euclidean
shortest path,
√ the Euclidean heuristic will cut the cost to be propor- graph
tional to X, which leads to an expected√ running time proportional When we direct the shortest-path
to V for dense graphs and proportional to V for sparse graphs. This search towards the destination ver-
example illustrates that the difficulty of developing an appropriate tex, we can restrict the search to
model or analyzing associated algorithms should not dissuade us from vertices within a relatively small
ellipse around the path, as illus-
taking advantage of the substantial savings that are available in many trated in these three examples,
applications, particularly when the implementation (add a term to the which show SPT subtrees from the
priority) is trivial. examples in Figure 21.12.
312 §21.5 CHAPTER TWENTY-ONE
The proof of Property 21.11 applies for any function that gives
a lower bound on the distance from each vertex to d. Might there be
other functions that will cause the algorithm to examine even fewer
vertices than the Euclidean heuristic? This question has been studied in
a general setting that applies to a broad class of combinatorial search
algorithms. Indeed, the Euclidean heuristic is a specific instance of
an algorithm called A* (pronounced “ay-star”). This theory tells us
that using the best available lower-bound function is optimal; stated
another way, the better the bound function, the more efficient the
search. In this case, the optimality of A* tells us that the Euclidean
heuristic will certainly examine fewer vertices than Dijkstra’s algorithm
(which is A* with a lower bound of 0). The analytic results just
described give more precise information for specific random network
models.
We can also use properties of Euclidean networks to help build
efficient implementations of the abstract shortest-paths ADT, trading
time for space more effectively than we can for general networks (see
Exercises 21.48 through 21.50). Such algorithms are important in
applications such as map processing, where networks are huge and
sparse. For example, suppose that we want to develop a navigation
system based on shortest paths for a map with millions of roads.
We perhaps can store the map itself in a small onboard computer,
but the distances and paths matrices are much too large to be stored
(see Exercises 21.39 and 21.40); therefore, the all-paths algorithms
of Section 21.3 are not effective. Dijkstra’s algorithm also may not
give sufficiently short response times for huge maps. Exercises 21.77
through 21.78 explore strategies whereby we can invest a reasonable
amount of preprocessing and space to provide fast responses to source–
Figure 21.20 sink shortest-paths queries.
Euclidean heuristic cost
bounds Exercises
When we direct the shortest-path • 21.68 Find a large Euclidean graph online—perhaps a map with an underlying
search towards the destination ver- table of locations and distances between them, telephone connections with
tex, we can restrict the search to costs, or airline routes and rates.
vertices within an ellipse around
the path, as compared to the circle 21.69 . Using the strategies described in Exercises 17.13 through 17.15,
centered at s that is required by write programs
√that generate
√ random Euclidean graphs by connecting vertices
Dijkstra’s algorithm. The radius of arranged in a V -by- V grid.
the circle and the shape of the el- 21.70 Show that the partial SPT computed by the Euclidean heuristic is inde-
lipse are determined by the length pendent of the value that we use to initialize wt[s]. Explain how to compute
of the shortest path. the shortest-path lengths from the initial value.
SHORTEST PATHS §21.5 313
21.71 Show, in the style of Figure 21.10, what is the result when you use
the Euclidean heuristic to compute a shortest path from 0 to 6 in the network
defined in Exercise 21.1.
◦ 21.72 Describe what happens if the function dist(s, t), used for the Eu-
clidean heuristic, returns the actual shortest-path length from s to t for all
pairs of vertices.
21.73 Develop an ADT implementation for shortest paths in dense Euclidean
graphs that is based on an adjacency-matrix network ADT and an implemen-
tation of Dijkstra’s algorithm (Program 20.3, with an appropriate priority
function).
21.74 Run empirical studies to test the effectiveness of the Euclidean shortest-
path heuristic, for various Euclidean networks (see Exercises 21.8, 21.68,
21.69, and 21.80). For each graph, generate V /10 random pairs of vertices,
and print a table that shows the average distance between the vertices, the
average length of the shortest path between the vertices, the average ratio of
the number of vertices examined with the Euclidean heuristic to the number
of vertices examined with Dijkstra’s algorithm, and the average ratio of the
area of the ellipse associated with the Euclidean heuristic with the area of the
circle associated with Dijkstra’s algorithm.
21.75 Develop an implementation for the source–sink shortest-paths problem
in Euclidean graphs that is based on the bidirectional search described in
Exercise 21.35.
◦ 21.76 Use a geometric interpretation to provide an estimate of the ratio of
the number of vertices in the SPT produced by Dijkstra’s algorithm for the
source–sink problem to the number of vertices in the SPTs produced in the
two-way version described in Exercise 21.75.
21.77 Develop an ADT implementation for shortest paths in Euclidean graphs
that performs the following preprocessing step: Divide the map region into
a W -by-W grid, and then use Floyd’s all-pairs shortest-paths algorithm to
compute a W 2 -by-W 2 array, where row i and column j contain the length of
a shortest path connecting any vertex in grid square i to any vertex in grid
square j. Then, use these shortest-path lengths as lower bounds to improve
the Euclidean heuristic. Experiment with a few different values of W such
that you expect a small constant number of vertices per grid square.
21.78 Develop an implementation of the all-pairs shortest-paths ADT for
Euclidean graphs that combines the ideas in Exercises 21.75 and 21.77.
21.79 Run empirical studies to compare the effectiveness of the heuristics
described in Exercises 21.75 through 21.78, for various Euclidean networks
(see Exercises 21.8, 21.68, 21.69, and 21.80).
21.80 Expand your empirical studies to include Euclidean graphs that are
derived by removal of all vertices and edges from a circle of radius r in the
center, for r = 0.1, 0.2, 0.3, and 0.4. (These graphs provide a severe test of the
Euclidean heuristic.)
314 §21.6 CHAPTER TWENTY-ONE
21.6 Reduction
It turns out that shortest-paths problems—particularly the general
case, where negative weights are allowed (the topic of Section 21.7)—
represent a general mathematical model that we can use to solve a
variety of other problems that seem unrelated to graph processing.
This model is the first among several such general models that we
encounter. As we move to more difficult problems and increasingly
general models, one of the challenges that we face is to characterize
precisely relationships among various problems. Given a new problem,
we ask whether we can solve it easily by transforming it to a problem
that we know how to solve. If we place restrictions on the problem,
will we be able to solve it more easily? To help answer such questions,
we digress briefly in this section to discuss the technical language that
we use to describe these types of relationships among problems.
Definition 21.3 We say that a problem A reduces to another problem
B if we can use an algorithm that solves B to develop an algorithm
that solves A, in a total amount of time that is, in the worst case, no
more than a constant times the worst-case running time of B. We say
that two problems are equivalent if they reduce to each other.
We postpone until Part 8 a rigorous definition of what it means
to “use” one algorithm to “develop” another. For most applications,
we are content with the following simple approach. We show that A
SHORTEST PATHS §21.6 315
0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5
0
0 .10 .10 .10 0 .10 .10 .20 .20 .10 0 1 1 1 0 1 1
1
1 .10 .10 .10 1 .10 .10 .10 1 0 1 1 0 1 0
5 2 .10 2 .10 2 0 0 1 0 0 0
3 .10 .10 .10 .10 .10 3 .10 .20 .10 .10 .10 .10 3 1 1 1 1 1 1
4
4 .10 .10 4 .10 .10 4 0 0 1 0 1 0
5 .10 .10 .10 5 .10 .20 .10 .10 5 0 1 1 0 1 1
3 2
Figure 21.21
reduces to B by demonstrating that we can solve any instance of A in Transitive-closure reduction
three steps:
Given a digraph (left), we can
• Transform it to an instance of B. transform its adjacency matrix
• Solve that instance of B. (with self-loops) into an adjacency
• Transform the solution of B to be a solution of A. matrix representing a network by
assigning an arbitrary weight to
As long as we can perform the transformations (and solve B) effi-
each edge (left matrix). As usual,
ciently, we can solve A efficiently. To illustrate this proof technique, blank entries in the matrix repre-
we consider two examples. sent a sentinel value that indicates
the absence of an edge. Given the
Property 21.12 The transitive-closure problem reduces to the all- all-pairs shortest-paths-lengths ma-
pairs shortest-paths problem with nonnegative weights. trix of that network (center matrix),
the transitive closure of the digraph
Proof : We have already pointed out the direct relationship between (right matrix) is simply the matrix
Warshall’s algorithm and Floyd’s algorithm. Another way to consider formed by subsituting 0 for each
sentinel and 1 for all other entries.
that relationship, in the present context, is to imagine that we need to
compute the transitive closure of digraphs using a library function that
computes all shortest paths in networks. To do so, we add self-loops if
they are not present in the digraph; then, we build a network directly
from the adjacency matrix of the digraph, with an arbitrary weight (say
0.1) corresponding to each 1 and the sentinel weight corresponding to
each 0. Then, we call the all-pairs shortest-paths function. Next, we
can easily compute the transitive closure from the all-pairs shortest-
paths matrix that the function computes: Given any two vertices u and
v, there is a path from u to v in the digraph if and only if the length of
the path from u to v in the network is nonzero (see Figure 21.21).
This property is a formal statement that the transitive-closure
problem is no more difficult than the all-pairs shortest-paths problem.
Since we happen to know algorithms for transitive closure that are
even faster than the algorithms that we know for all-pairs shortest-
paths problems, this information is no surprise. Reduction is more
316 §21.6 CHAPTER TWENTY-ONE
find the kth smallest element in a file by sorting the file and then
indexing (or scanning) to the kth position, but this fact certainly does
not imply that sorting reduces to selection. In the present context,
the shortest-paths problem for weighted DAGs and the shortest-paths
problem for networks with positive weights both reduce to the general
shortest-paths problem. This use of reduction corresponds to the
intuitive notion of one problem being more general than another. Any
sorting algorithm solves any selection problem, and, if we can solve
the shortest-paths problem in general networks, we certainly can use
that solution for networks with various restrictions; but the converse
is not necessarily true.
This use of reduction is helpful, but the concept becomes more
useful when we use it to gain information about the relationships
between problems in different domains. For example, consider the
following problems, which seem at first blush to be far removed from
graph processing. Through reduction, we can develop specific rela-
tionships between these problems and the shortest-paths problem.
Job scheduling A large set of jobs, of varying durations, needs
to be performed. We can be working on any number of jobs at a given
0 .41 time, but a set of precedence relationships specify, for a set of pairs of
0 5
1 .51 jobs, that the first must be completed before the second can be started.
2 .50
3 .36
What is the minimum amount of time required to complete all the jobs
4 .38 1 7 while satisfying all the precedence constraints? Specifically, given a set
5 .45
4
of jobs (with durations) and a set of precedence constraints, schedule
6 .21 2 8 3
7 .32 the jobs (find a start time for each) so as to achieve this minimum.
8 .32
6 9 Figure 21.22 depicts an example instance of the job-scheduling
9 .29
problem. It uses a natural network representation, which we use in a
Figure 21.22 moment as the basis for a reduction. This version of the problem is
Job scheduling perhaps the simplest of literally hundreds of versions that have been
In this network, vertices represent studied—versions that involve other job characteristics and other con-
jobs to be completed (with weights straints, such as the assignment of personnel or other resources to the
indicating the amount of time re-
quired) and edges represent prece-
jobs, other costs associated with specific jobs, deadlines, and so forth.
dence relationships between them. In this context, the version that we have described is commonly called
For example, the edges from 7 to precedence-constrained scheduling with unlimited parallelism; we use
8 and 3 mean that job 7 must be the term job scheduling as shorthand.
finished before job 8 or job 3 can
be started. What is the minimum To help us to develop an algorithm that solves the job-scheduling
amount of time required to com- problem, we consider the following problem, which is widely applica-
plete all the jobs? ble in its own right.
SHORTEST PATHS §21.6 319
#include <stdio.h>
#include "GRAPH.h"
#define Nmax 1000
main(int argc, char *argv[])
{ int i, s, t, N = atoi(argv[1]);
double length[Nmax], start[Nmax];
int st[Nmax];
Graph G = GRAPHinit(N);
for (i = 0; i < N; i++)
scanf("%lf", &length[i]);
while (scanf("%d %d", &s, &t) != EOF)
GRAPHinsertE(G, EDGE(s, t, length[s]));
GRAPHlpt(G, 0, st, start);
for (i = 0; i < N; i++)
printf("%3d %6.2f\n", i, start[i]);
}
them require exponential time in the worst case. We show here that
the general shortest-paths problem is NP-hard.
As mentioned briefly in Section 17.8 and discussed in detail in
Part 8, we generally take the fact that a problem is NP-hard to mean
not just that no efficient algorithm is known that is guaranteed to
solve the problem, but also that we have little hope of finding one.
In this context, we use the term efficient to refer to algorithms whose
running time is bounded by some polynomial function of the size of the
input, in the worst case. We assume that the discovery of an efficient
algorithm to solve any NP-hard problem would be a stunning research
breakthrough. The concept of NP-hardness is important in identifying
problems that are difficult to solve, because it is often easy to prove
that a problem is NP-hard, using the following technique:
Property 21.17 A problem is NP-hard if there is an efficient reduc-
tion to it from any NP-hard problem.
This property depends on the precise meaning of an efficient reduction
from one problem A to another problem B. We defer such definitions
to Part 8 (two different definitions are commonly used). For the mo-
ment, we simply use the term to cover the case where we have efficient
algorithms both to transform an instance of A to an instance of B and
to transform a solution of B to a solution of A.
Now, suppose that we have an efficient reduction from an NP-
hard problem A to a given problem B. The proof is by contradiction:
If we have an efficient algorithm for B, then we could use it to solve any
instance of A in polynomial time, by reduction (transform the given
instance of A to an instance of B, solve that problem, then transform
the solution). But no known algorithm can make such a guarantee
for A (because A is NP-hard), so the assumption that there exists a
polynomial-time algorithm for B is incorrect: B is also NP-hard.
This technique is extremely important because people have used
it to show a huge number of problems to be NP-hard, giving us a broad
variety of problems from which to choose when we want to develop a
proof that a new problem is NP-hard. For example, we encountered
one of the classic NP-hard problems in Section 17.7. The Hamilton-
path problem, which asks whether there is a simple path containing
all the vertices in a given graph, was one of the first problems shown
to be NP-hard (see reference section). It is easy to formulate as a
326 §21.6 CHAPTER TWENTY-ONE
A B A ⇒ B implication example
Key:
EMST Euclidean minimum spanning tree
TC transitive closure
APSP all-pairs shortest paths
SSSP single-source shortest paths
SSLP single-source longest paths
(+) (in networks with nonnegative weights)
(±) (in networks with weights that could be negative)
(DAG) (in acyclic networks)
DC difference constraints
HP Hamilton paths
JS(WD) job scheduling (with deadlines)
328 §21.6 CHAPTER TWENTY-ONE
to the first ones. When we find an upper bound, we can analyze the
associated algorithm, run empirical studies, and so forth to determine
whether it represents a better solution to the problem. When we de-
velop a good general-purpose algorithm, we can invest in developing
and testing a good implementation and then develop associated ADTs
that expand its applicability.
We use reduction as a basic tool in this and the next chapter. We
emphasize the general relevance of the problems that we consider, and
the general applicability of the algorithms that solve them, by reducing
other problems to them. It is also important to be aware of a hier-
archy among increasingly general problem-formulation models. For
example, linear programming is a general formulation that is impor-
tant not just because many problems reduce to it but also because it
is not known to be NP-hard. In other words, there is no known way
to reduce the general shortest-paths problem (or any other NP-hard
problem) to linear programming. We discuss such issues in Part 8.
Not all problems can be solved, but good general models have
been devised that are suitable for broad classes of problems that we
do know how to solve. Shortest paths in networks is our first example
of such a model. As we move to ever-more-general problem domains,
we enter the field of operations research (OR), the study of mathemat-
ical methods of decision making, where developing and studying such
models is central. One key challenge in OR is find the model that is
most appropriate for solving a problem and to fit the problem to the
model. This activity is sometimes known as mathematical program-
ming (a name given to it before the advent of computers and the new
use of the word “programming”). Reduction is a modern concept that
is in the same spirit as mathematical programming and is the basis for
our understanding of the cost of computation in a broad variety of
applications.
Exercises
21.85 Use the reduction of Property 21.12 to develop a transitive-closure
implementation (the ADT function GRAPHtc of Section 19.3) using the all-
pairs shortest-paths ADT of Section 21.3.
21.86 Show that the problem of computing the number of strong com-
ponents in a digraph reduces to the all-pairs shortest-paths problem with
nonnegative weights.
330 §21.6 CHAPTER TWENTY-ONE
0-1 .41
1-2 .51 0 0 1 2 3 4 5 0 1 2 3 4 5
2-3 .50 1 0 0 0 .51 .68 .32 .29 0 0 5 5 5 5 5
4-3 .36
3-5 -.38 5 1 1.13 0 .51 .68 .32 .30 1 4 1 2 4 4 4
3-0 .45 2 .95 -.17 0 .50 .15 .12 2 3 3 2 3 3 3
0-5 .29 4 3 .45 -.67 -.16 0 -.35 -.38 3 0 5 5 3 5 5
5-4 .21
1-4 .32 4 .81 -.31 .20 .36 0 -.02 4 3 3 3 3 4 3
4-2 .32 3 2 5 .84 -.29 .22 .39 .03 0 5 1 1 1 1 1 5
5-1 -.29
Figure 21.26
A sample network with nega- are not merely a mathematical curiosity; on the contrary, they sig-
tive edges nificantly extend the applicability of the shortest-paths problems as a
This sample network is the same model for solving other problems. This potential utility is our motiva-
as the network depicted in Fig- tion to search for efficient algorithms to solve network problems that
ure 21.1, except that the edges 3-5
involve negative weights.
and 5-1 are negative. Naturally,
this change dramatically affects Figure 21.26 is a small example that illustrates the effects of
the shortest paths structure, as we introducing negative weights on a network’s shortest paths. Perhaps
can easily see by comparing the the most important effect is that, when negative weights are present,
distance and path matrices at the
right with their counterparts in Fig-
low-weight shortest paths tend to have more edges than higher-weight
ure 21.9. For example, the shortest paths. For positive weights, our emphasis was on looking for shortcuts;
path from 0 to 1 in this network is but when negative weights are present, we seek detours that use as
0-5-1, which is of length 0; and many edges with negative weights as we can find. This effect turns our
the shortest path from 2 to 1 is
2-3-5-1, which is of length -.17.
intuition in seeking “short” paths into a liability in understanding the
algorithms, so we need to suppress that line of intuition and consider
the problem on a basic abstract level.
The relationship shown in the proof of Property 21.18 between
shortest paths in networks and Hamilton paths in graphs ties in with
our observation that finding paths of low weight (which we have been
calling “short”) is tantamount to finding paths with a high number of
edges (which we might consider to be “long”). With negative weights,
we are looking for long paths rather than short paths.
The first idea that suggests itself to remedy the situation is to
find the smallest (most negative) edge weight, then to add the absolute
value of that number to all the edge weights to transform the network
into one with no negative weights. This naive approach does not work
at all, because shortest paths in the new network bear little relation to
shortest paths in the old one. For example, in the network illustrated
in Figure 21.26, the shortest path from 4 to 2 is 4-3-5-1-2. If we add
.38 to all the edge weights in the graph to make them all positive, the
SHORTEST PATHS §21.7 333
weight of this path grows from .20 to 1.74. But the weight of 4-2
grows from .32 to just .70, so that edge becomes the shortest path
from 4 to 2. The more edges a path has, the more it is penalized by
this transformation; that result, from the observation in the previous
paragraph, is precisely the opposite of what we need. Even though
this naive idea does not work, the goal of transforming the network
into an equivalent one with no negative weights but the same shortest
paths is worthy; at the end of the section, we consider an algorithm
that achieves this goal.
Our shortest-paths algorithms to this point have all placed one
of two restrictions on the shortest-paths problem so that they can offer
an efficient solution: They either disallow cycles or disallow negative
weights. Is there a less stringent restriction that we could impose on
networks that contain both cycles and negative weights that would still
lead to tractable shortest-paths problems? We touched on an answer
to this question at the beginning of the chapter, when we had to add
the restriction that paths be simple so that the problem would make
sense if there were negative cycles. Perhaps we should restrict attention
to networks that have no such cycles?
Shortest paths in networks with no negative cycles Given a
network that may have negative edge weights but does not have any
negative-weight cycles, solve one of the following problems: find a
shortest path connecting two given vertices (shortest-path problem),
find shortest paths from a given vertex to all the other vertices (single-
source problem), or find shortest paths connecting all pairs of vertices
(all-pairs problem).
The proof of Property 21.18 leaves the door open for the possi-
bility of efficient algorithms for solving this problem because it breaks
down if we disallow negative cycles. To solve the Hamilton-path
problem, we would need to be able to solve shortest-paths problems
in networks that have huge numbers of negative cycles.
Moreover, many practical problems reduce precisely to the prob-
lem of finding shortest paths in networks that contain no negative
cycles. We have already seen one such example.
Proof : The argument that we used in the proof of Property 21.15 shows
that the construction in the proof of Property 21.16 leads to networks
that contain no negative cycles. From the job-scheduling problem, we
construct a difference-constraints problem with variables that corre-
spond to job start times; from the difference-constraints problem, we
construct a network. We negate all the weights to convert from a
longest-paths problem to a shortest-paths problem—a transformation
that corresponds to reversing the sense of all the inequalities. Any
simple path from i to j in the network corresponds to a sequence of
inequalities involving the variables. The existence of the path implies,
by collapsing these inequalities, that xi − xj ≤ wij , where wij is the
sum of the weights on the path from i to j. A negative cycle corre-
sponds to 0 on the left side of this inequality and a negative value on
the right, so the existence of such a cycle is a contradiction.
As we noted when we first discussed the job-scheduling prob-
lem in Section 21.6, this statement implicitly assumes that our job-
scheduling problems are feasible (have a solution). In practice, we
would not make such an assumption, and part of our computational
burden would be determining whether or not a job-scheduling-with-
deadlines problem is feasible. In the construction in the proof of
Property 21.19, a negative cycle in the network implies an infeasible
problem, so this task corresponds to the following problem:
Negative cycle detection Does a given network have a negative
cycle? If it does, find one such cycle.
On the one hand, this problem is not necessarily easy (a simple
cycle-checking algorithm for digraphs does not apply); on the other
hand, it is not necessarily difficult (the reduction of Property 21.16
from the Hamilton-path problem does not apply). Our first challenge
will be to develop an algorithm for this task.
In the job-scheduling-with-deadlines application, negative cycles
correspond to error conditions that are presumably rare, but for which
we need to check. We might even develop algorithms that remove edges
to break the negative cycle and iterate until there are none. In other
applications, detecting negative cycles is the prime objective, as in the
following example.
Arbitrage Many newspapers print tables showing conversion
rates among the world’s currencies (see, for example, Figure 21.27).
We can view such tables as adjacency-matrix representations of com-
SHORTEST PATHS §21.7 335
plete networks. An edge s-t with weight x means that we can convert $ P Y C S
1 unit of currency s into x units of currency t. Paths in the network $ 1.0 1.631 0.669 0.008 0.686
specify multistep conversions. For example, if there is also an edge t-w P 0.613 1.0 0.411 0.005 0.421
Y 1.495 2.436 1.0 0.012 1.027
with weight y, then the path s-t-w represents a way to convert 1 unit
C 120.5 197.4 80.82 1.0 82.91
of currency s into xy units of currency w. We might expect xy to be
S 1.459 2.376 0.973 0.012 1.0
equal to the weight of s-w in all cases, but such tables represent a com-
plex dynamic system where such consistency cannot be guaranteed. If $ P Y C S
we find a case where xy is smaller than the weight of s-w, then we may $ 0.0 0.489 -0.402 -4.791 -0.378
be able to outsmart the system. Suppose that the weight of w-s is z P -0.489 0.0 -0.891 -5.278 -0.865
and xyz > 1, then the cycle s-t-w-s gives a way to convert 1 unit of Y 0.402 0.89 0.0 -4.391 0.027
currency s into more than 1 units (xyz) of currency s. That is, we can C 4.791 5.285 4.392 0.0 4.418
make a 100(xyz − 1) percent profit by converting from s to t to w back S 0.378 0.865 -0.027 -4.415 0.0
0 Proof : The proof of Property 21.8 does not depend on whether or not
1 edge weights are negative; however, we need to interpret the results
5 differently when negative edge weights are present. Each entry in the
4 matrix is evidence of the algorithm having discovered a path of that
3-5 -.38 length; in particular, any negative entry on the diagonal of the distances
3 2 3-0 .45 matrix is evidence of the presence of at least one negative cycle. In
0 the presence of negative cycles, we cannot directly infer any further
1 information, because the paths that the algorithm implicitly tests are
5 not necessarily simple: Some may involve one or more trips around
4 one or more negative cycles. However, if there are no negative cycles,
5-1 -.29 then the paths that are computed by the algorithm are simple, because
3 2 3-0 .45
any path with a cycle would imply the existence of a path that has
0 fewer edges and is not of higher weight that connects the same two
1
points (the same path with the cycle removed).
5
4
The proof of Property 21.20 does not give specific information
about how to find a specific negative cycle from the distances and
3 2 3-0 .45 paths matrices computed by Floyd’s algorithm. We leave that task for
an exercise (see Exercise 21.122).
Figure 21.28 Floyd’s algorithm solves the all-pairs shortest-paths problem for
Failure of Dijkstra’s algorithm graphs that contain no negative cycles. Given the failure of Dijkstra’s
(negative weights)
algorithm in networks that contain weights that could be negative, we
In this example, Dijkstra’s algo- could also use Floyd’s algorithm to solve the the all-pairs problem for
rithm decides that 4-2 is the short-
est path from 4 to 2 (of length sparse networks that contain no negative cycles, in time proportional to
.32), and misses the shorter path V 3 . If we have a single-source problem in such networks, then we can
4-3-5-1-2 (of length .20). use this V 3 solution to the all-pairs problem that, although it amounts
SHORTEST PATHS §21.7 337
0 1 2 3 4 5 0 1 2 3 4 5
0
0 0 .41 .29 0 0 1 5 Figure 21.29
1
1 0 .51 .32 1 1 2 4 Floyd’s algorithm (negative
5
2 0 .50 2 2 3
weights)
4 3 .45 .86 0 -.38 3 0 0 3 5 This sequence shows the construc-
4 .32 .36 0 4 2 3 4 tion of the all-shortest paths ma-
3 2 5 -.29 .21 0 5 1 4 5 trices for a digraph with negative
weights, using Floyd’s algorithm.
0 1 2 3 4 5 0 1 2 3 4 5 The first step are the same as de-
0
1 0 0 .41 .92 .73 .29 0 0 1 1 1 5 picted in Figure 21.14. Then the
1 0 .51 .32 1 1 2 4 negative edge 5-1 comes into play
5
2 0 .50 2 2 3 in the second step, where the paths
4 3 .45 .86 1.37 0 1.18 -.38 3 0 0 0 3 0 5 5-1-2 and 5-1-4 are discovered.
4 .32 .36 0 4 2 3 4 The algorithm involves precisely
3 2 5 -.29 .22 .21
.03 0 5 1 1 4
1 5 the same sequence of relaxation
steps for any edge weights, but the
0 1 2 3 4 5 0 1 2 3 4 5
0 outcome differs.
0 0 .41 .92 1.42 .73 .29 0 0 1 1 1 1 5
1
1 0 .51 1.01 .32 1 1 2 2 4
5
2 0 .50 2 2 3
4 3 .45 .86 1.37 0 1.18 -.38 3 0 0 0 3 0 5
4 .32 .36 0 4 2 3 4
3 2 5 -.29 .22 .72 .03 0 5 1 1 1 1 5
0 1 2 3 4 5 0 1 2 3 4 5
0
0 0 .41 .92 1.42 .73 .29 0 0 1 1 1 1 5
1
1 1.46 0 .51 1.01 .32 .63 1 2 1 2 2 4 2
5
2 .95 1.36 0 .50 1.68 .12 2 3 3 2 3 3 3
4 3 .45 .86 1.37 0 1.18 -.38 3 0 0 0 3 0 5
4 .81 1.22 .32 .36 0 -.02 4 3 3 2 3 4 3
3 2 5 1.17 -.29 .22 .72 .03 0 5 1 1 1 1 1 5
0 1 2 3 4 5 0 1 2 3 4 5
0
0 0 .41 1.09 .73
.92 1.42 .29 0 0 1 1 1 1 5
1
1 1.13
1.46 0 .68 .32
.51 1.01 .30
.63 1 2
4 1 2 2
4 4 2
4
5
2 .95 1.36 0 .50 1.68 .12 2 3 3 2 3 3 3
4 3 .45 .86 1.37 0 1.18 -.38 3 0 0 0 3 0 5
4 .81 1.22 .32 .36 0 -.02 4 3 3 2 3 4 3
3 2 5 .84 -.29 .22
1.17 .39
.72 .03 0 5 1 1 1 1 1 5
0 1 2 3 4 5 0 1 2 3 4 5
0
0 0 0
.41 .51 1.09
.92 .68 .73
.32 .29 0 0 1
5 1
5 1
5 1
5 5
1
1 1.13 0 .51 .68 .32 .30 1 4 1 2 4 4 4
5
2 -.17
.95 1.36 0 .15 .12
.50 1.68 2 3 3 2 3 3 3
4 3 .45 -.67 -.16
.86 1.37 0 -.35 -.38
1.18 3 0 0
5 0
5 3 0
5 5
4 -.31 .32
.81 1.22 .20 .36 0 -.02 4 3 3 2
3 3 4 3
3 2 5 .84 -.29 .22 .39 .03 0 5 1 1 1 1 1 5
338 §21.7 CHAPTER TWENTY-ONE
to overkill, is the best that we have yet seen for the single-source prob-
lem. Can we develop faster algorithms for these problems—ones that
achieve the running times that we achieve with Dijkstra’s algorithm
when edge weights are positive (E lg V for single-source shortest paths
and V E lg V for all-pairs shortest paths)? We can answer this question
in the affirmative for the all-pairs problem, and can bring down the
worst-case cost to V E for the single-source problem, but breaking the
V E barrier for the general single-source shortest-paths problem is a
longstanding open problem.
The following approach, known as the Bellman–Ford algo-
rithm, provides a simple and effective basis for attacking single-source
shortest-paths problems in networks that contain no negative cycles.
To compute shortest paths from a vertex s, we maintain (as usual)
a vertex-indexed array wt such that wt[t] contains the shortest-path
length from s to t. We initialize wt[s] to 0 and all other wt entries to
a large sentinel value, then compute shortest paths as follows:
Considering the network’s edges in any order, relax along
each edge. Make V such passes.
We use the term Bellman–Ford algorithm to refer to the generic method
of making V passes through the edges, considering the edges in any
order. Certain authors use the term to describe a more general method
(see Exercise 21.130).
Proof : We make V passes through all E edges, so the total time is pro-
portional to V E. To show that the computation achieves the desired
result, we show by induction on i that, after the ith pass, wt[v] is no
greater than the length of the shortest path from s to v that contains
i or fewer edges (or maxWT if there is no such path), for all vertices v.
The claim is certainly true if i is 0. Assuming the claim to be true for
i, there are two cases for each given vertex v: Among the paths from
s to v with i+1 or fewer edges, there may or may not be a shortest
path with i+1 edges. If the shortest of the paths with i+1 or fewer
edges from s to v is of length i or less, then wt[v] will not change
and will remain valid. Otherwise, there is a path from s to v with
i+1 edges that is shorter than any path from s to v with i or fewer
SHORTEST PATHS §21.7 339
edges. That path must consist of a path with i edges from s to some
vertex w plus the edge w-v. By the induction hypothesis, wt[w] is an
upper bound on the shortest distance from s to w, and the (i+1)st pass
checks whether each edge constitutes the final edge in a new shortest
path to that edge’s destination. In particular, it checks the edge w-v.
After V-1 iterations, then, wt[v] is a lower bound on the length
of any shortest path with V-1 or fewer edges from s to v, for all vertices
v. We can stop after V-1 iterations because any path with V or more
edges must have a (positive- or zero-cost) cycle and we could find a
path with V-1 or fewer edges that is the same length or shorter by
removing the cycle. Since wt[v] is the length of some path from s to
v, it is also an upper bound on the shortest-path length, and therefore
must be equal to the shortest-path length.
Although we did not consider it explicitly, the same proof shows
that the st array is a parent-link representation of the shortest-paths
tree rooted at s.
For example, in a graph represented with adjacency lists, we
could implement the Bellman–Ford algorithm to find the shortest paths
from a start vertex s as follows:
for (v = 0; v < G->V; v++)
{ st[v] = -1; wt[v] = maxWT; }
wt[s] = 0; st[s] = 0;
for (i = 0; i < G->V; i++)
for (v = 0; v < G->V; v++)
if (wt[v] < maxWT)
for (t = G->adj[v]; t != NULL; t = t->next)
if (wt[t->v] > wt[v] + t->wt)
{ wt[t->v] = wt[v] + t->wt; st[t->v] = v; }
This code exhibits the simplicity of the basic method. It is not used in
practice, however, because simple modifications yield implementations
that are more efficient for most graphs, as we soon see.
For typical graphs, examining every edge on every pass is waste-
ful. Indeed, we can easily determine a priori that numerous edges are
not going to lead to a successful relaxation in any given pass. In fact,
the only edges that could lead to a change are those emanating from a
vertex whose value changed on the previous pass.
Program 21.9 is a straightforward implementation where we use
a FIFO queue to hold these edges, so that they are the only ones exam-
340 §21.7 CHAPTER TWENTY-ONE
1 2
SHORTEST PATHS §21.7 341
Proof : Given any two vertices s and t, reweighting changes the weight
of any path from s to t, precisely by adding the difference between
the weights of s and t. This fact is easy to prove by induction on the
length of the path. The weight of every path from s to t is changed
by the same amount when we reweight the network, long paths and
short paths alike. In particular, this fact implies immediately that
the shortest-path length between any two vertices in the transformed
network is the same as the shortest-path length between them in the
original network.
1-0 1.51
Figure 21.32
0
2-1 0 0 All shortest paths in a
1
3-2 .34
3
reweighted network
3-4 0
5 These diagrams depict the SPTs
5-3 0 4 2
0-3 0
4 for each vertex in the reverse of
5-0 1.12 1 0 1 2 3 4 5
4-5 .23
the reweighted network from Fig-
st 0 4 3 0 3 1
4-1 .01 3 2 5
wt 0 .01 .34 0 0 .01
ure 21.31, as could be computed
2-4 .12 with Dijkstra’s algorithm to give us
1-5 0
0
shortest paths in the original net-
1
1 work in Figure 21.26. The paths
5 are the same as for the network
5
3 0 before reweighting, so, as in Fig-
4 ure 21.9, the st vectors in these
4 2 0 1 2 3 4 5 diagrams are the columns of the
st 5 1 3 5 3 1
3 2
wt .41 0 1.17 .67 1.03 .29
paths matrix in Figure 21.26. The
wt vectors in this diagram cor-
0 respond to the columns in the
2
1 distances matrix, but we have to
1 undo the reweighting for each en-
5
5 try by subtracting the weight of
4 the source vertex and adding the
3 0 0 1 2 3 4 5 weight of the final vertex in the
4 st 5 2 2 5 3 1
3 2
wt 1.12 0 .34 0 0 0 path (see Figure 21.31). For ex-
ample, from the third row from
0 3 the bottom here we can see that
1 the shortest path from 0 to 3 is
4 2
0-5-1-4-3 in both networks, and
5
1 its length is 1.13 in the reweighted
4 5 network shown here. Consulting
0 1 2 3 4 5
Figure 21.31, we can calculate its
0 st 5 4 3 3 3 1
3 2
wt 1.13 .01 .34 0 0 .01 length in the original network by
subtracting the weight of 0 and
0 4 adding the weight of 3 to get the
1 result 1.13 - .81 + .36 = .68, the
1
entry in row 0 and column 3 of the
5 5 distances matrix in Figure 21.26.
4 3 0 All shortest paths to 4 in this net-
0 1 2 3 4 5
work are of length 0 because we
2 st 5 4 3 5 4 1
3 2
wt 0 0 0 0 0 0 used those paths for reweighting.
0 5
1 3 0
5 4 2
4 1
0 1 2 3 4 5
st 5 4 3 5 3 5
3 2
wt 1.12 .01 .34 0 0 0
346 §21.7 CHAPTER TWENTY-ONE
For example, the weight values chosen in Figure 21.31 are pre-
cisely the lengths of shortest paths from 4, so the edges in the shortest-
paths tree rooted at 4 have weight 0 in the reweighted network.
In summary, we can solve the all-pairs shortest-paths problem in
networks that contain negative edge weights but no negative cycles by
proceeding as follows:
• Apply the Bellman–Ford algorithm to find a shortest-paths forest
in the original network.
• If the algorithm detects a negative cycle, report that fact and
terminate.
• Reweight the network from the forest.
• Apply the all-pairs version of Dijkstra’s algorithm to the reweighted
network.
After this computation, the paths matrix gives shortest paths in both
networks, and the distances matrix give the path lengths in the
reweighted network. This series of steps is sometimes known as John-
son’s algorithm (see reference section).
Proof : See Properties 21.22 through 21.24 and the summary in the
previous paragraph. The worst-case bound on the running time is
immediate from Properties 21.7 and 21.22.
◦ 21.111 Develop client programs that use your generators from Exer-
cises 21.109 and 21.110 to produce networks that have a large percentage
of negative weights but have at most a few negative cycles, for as large a range
of values of V and E as possible.
21.112 Find a currency-conversion table online or in a newspaper. Use it to
build an arbitrage table. Note: Avoid tables that are derived (calculated) from
a few values and that therefore do not give sufficiently accurate conversion
information to be interesting. Extra credit: Make a killing in the money-
exchange market!
• 21.113 Build a sequence of arbitrage tables using the source for conversion
that you found for Exercise 21.112 (any source publishes different tables
periodically). Find all the arbitrage opportunities that you can in the tables,
and try to find patterns among them. For example, do opportunities persist
day after day, or are they fixed quickly after they arise?
21.114 Develop a model for generating random arbitrage problems. Your
goal is to generate tables that are as similar as possible to the tables that you
used in Exercise 21.113.
348 §21.7 CHAPTER TWENTY-ONE
21.129 Show the schedule that is computed by Program 21.9 for the job-
scheduling-with-deadlines problem in Exercise 21.89.
◦ 21.130 Prove that the following generic algorithm solves the single-source
shortest-paths problem: “Relax any edge; continue until there are no edges
that can be relaxed.”
21.131 Modify the implementation of the Bellman–Ford algorithm in Pro-
gram 21.9 to use a randomized queue rather than a FIFO queue. (The result
of Exercise 21.130 proves that this method is correct.)
◦ 21.132 Modify the implementation of the Bellman–Ford algorithm in Pro-
gram 21.9 to use a deque rather than a FIFO queue, such that edges are put
onto the deque according to the following rule: If the edge has previously been
on the deque, put it at the beginning (as in a stack); if it is being encountered
for the first time, put it at the end (as in a queue).
21.133 Run empirical studies to compare the performance of the implemen-
tations in Exercises 21.131 and 21.132 with Program 21.9, for various
general networks (see Exercises 21.109 through 21.111).
• 21.139 Implement network ADT functions like the one described in Exer-
cise 21.138 that allow clients to insert and delete edges.
• 21.140 Develop an algorithm that breaks the V E barrier for the single-
source shortest-paths problem in general networks, for the special case where
the weights are known to be bounded in absolute value by a constant.
21.8 Perspective
Table 21.4 summarizes the algorithms that we have discussed in this
chapter and gives their worst-case performance characteristics. These
algorithms are broadly applicable because, as discussed in Section 21.6,
shortest-paths problems are related to a large number of other prob-
lems in a specific technical sense that directly leads to efficient algo-
rithms for solving the entire class, or at least indicates such algorithms
exist.
The general problem of finding shortest paths in networks where
edge weights could be negative is intractable. Shortest-paths problems
are a good illustration of the fine line that often separates intractable
problems from easy ones, since we have numerous algorithms to solve
the various versions of the problem when we restrict the edge weights
to be positive or acyclic, or even when we restrict to subproblems where
there are negative edge weights but no negative cycles. Several of the
algorithms are optimal or nearly so, although there are significant gaps
between the best known lower bound and the best known algorithm for
the single-source problem in networks that contain no negative cycles
and for the all-pairs problem in networks that contain nonnegative
weights.
The algorithms are all based on a small number of abstract op-
erations and can be cast in a general setting. Specifically, the only
operations that we perform on edge weights are addition and compar-
ison: any setting in which these operations make sense can serve as the
platform for shortest-paths algorithms. As we have noted, this point
of view unifies our algorithms for computing the transitive closure of
digraphs with our algorithms for finding shortest paths in networks.
The difficulty presented by negative edge weights corresponds to a
monotonicity property on these abstract operations: If we can ensure
that the sum of two weights is never less than either of the weights,
then we can use the algorithms in Sections 21.2 through 21.4; if we
SHORTEST PATHS §21.8 351
single-source
nonnegative Dijkstra V2 optimal (dense networks)
nonnegative Dijkstra (PFS) E lg V conservative bound
acyclic source queue E optimal
no negative cycles Bellman–Ford VE room for improvement?
none open ? NP-hard
all-pairs
nonnegative Floyd V3 same for all networks
nonnegative Dijkstra (PFS) V E lg V conservative bound
acyclic DFS VE same for all networks
no negative cycles Floyd V3 same for all networks
no negative cycles Johnson V E lg V conservative bound
none open ? NP-hard
Network Flow
353
354 CHAPTER TWENTY-TWO
are costs associated with the channels, what is the cheapest way to
send the information at a given rate that is less than the maximum?
Traffic flow A city government needs to formulate a plan for supply channels cost
evacuating people from the city in an emergency. What is the minimum 0: 3 0-6: 2
1: 4 0-7: 1
amount of time that it would take to evacuate the city, if we suppose 2: 6 0-8: 5
that we can control traffic flow so as to realize the minimum? Traffic 3: 3 1-6: 3
planners also might formulate questions like this when deciding which 4: 2 1-5: 1
demand 2-8: 3
new roads, bridges, or tunnels might alleviate rush-hour or vacation- 5: 6 2-9: 4
weekend traffic problems. 6: 6 3-6: 2
7: 7 3-7: 1
8: 3 4-9: 6
In matching problems, the network represents the possible ways 9: 4 4-5: 3
to connect pairs of vertices, and our goal is to choose among the
connections (according to a specified criterion) without including any
vertex twice. In other words, the chosen set of edges defines a way 0 1 2 3 4
1-4 1 1
To describe network-flow algorithms, we begin with an idealized phys- 2-3 1 1
ical model in which several of the basic concepts are intuitive. Specif- 2-4 1 1 3 4
3-5 2 2
ically, we imagine a collection of interconnected oil pipes of varying 4-5 3 2 5
sizes, with switches controlling the direction of flow at junctions, as
in the example illustrated in Figure 22.5. We suppose further that the
Figure 22.5
network has a single source (say, an oil field) and a single sink (say,
Network flow
a large refinery) to which all the pipes ultimately connect. At each
A flow network is a weighted net-
vertex, the flowing oil reaches an equilibrium where the amount of oil work where we interpret edge
flowing in is equal to the amount flowing out. We measure both flow weights as capacities (top). Our
and pipe capacity in the same units (say, gallons per second). objective is to compute a second
If every switch has the property that the total capacity of the set of edge weights, bounded by
the capacities, which we call the
ingoing pipes is equal to the total capacity of the outgoing pipes, flow. The bottom drawing illus-
then there is no problem to solve: We simply fill all pipes to full trates our conventions for drawing
capacity. Otherwise, not all pipes are full, but oil flows through the flow networks. Each edge’s width
network, controlled by switch settings at the junctions, such that the is proportional to its capacity; the
amount of flow in each edge is
amount of oil flowing into each junction is equal to the amount of shaded in gray; the flow is always
oil flowing out. But this local equilibrium at the junctions implies an directed down the page from a sin-
equilibrium in the network as a whole: We prove in Property 22.1 that gle source at the top to a single
the amount of oil flowing into the sink is equal to the amount flowing sink at the bottom; and intersec-
tions (such as 1-4 and 2-3 in this
out of the source. Moreover, as illustrated in Figure 22.6, the switch example) do not represent vertices
settings at the junctions of this amount of flow from source to sink unless labelled as such. Except for
have nontrivial effects on the flow through the network. Given these the source and the sink, flow in is
facts, we are interested in the following question: What switch settings equal to flow out at every vertex:
for example, vertex 2 has 2 units
will maximize the amount of oil flowing from source to sink?
of flow coming in (from 0) and 2
We can model this situation directly with a network (a weighted units of flow going out (1 unit to 3
digraph, as defined in Chapter 21) that has a single source and a single and 1 unit to 4).
360 §22.1 CHAPTER TWENTY-TWO
cap flow 0
0-1 2 2* sink. The edges in the network correspond to the oil pipes, the vertices
0-2 3 0 1 2 correspond to the junctions with switches that control how much oil
1-3 3 2
1-4 1 0 goes into each outgoing edge, and the weights on the edges correspond
2-3 1 0 to the capacity of the pipes. We assume that the edges are directed,
2-4 1 0 3 4
3-5 2 2*
specifying that oil can flow in only one direction in each pipe. Each
4-5 3 0 5 pipe has a certain amount of flow, which is less than or equal to its
capacity, and every vertex satisfies the equilibrium condition that the
cap flow 0
flow in is equal to the flow out.
0-1 2 2*
0-2 3 1
This flow-network abstraction is a useful problem-solving model
1 2
1-3 3 2 that applies directly to a variety of applications and indirectly to still
1-4 1 0
2-3 1 0
more. We sometimes appeal to the idea of oil flowing through pipes
2-4 1 1* 3 4 for intuitive support of basic ideas, but our discussion applies equally
3-5 2 2*
5
well to goods moving through distribution channels and to numerous
4-5 3 1
other situations.
The flow model directly applies to a distribution scenario: we in-
cap flow 0
0-1 2 2 terpret the flow values as rates of flow, so that a flow network describes
0-2 3 2 1 2 the flow of goods in a manner precisely analogous to the flow of oil.
1-3 3 1
1-4 1 1* For example, we can interpret the flow in Figure 22.5 as specifying
2-3 1 1* that we should be sending two items per time unit from 0 to 1 and
2-4 1 1* 3 4
3-5 2 2* from 0 to 2, one item per time unit from 1 to 3 and from 1 to 4, and
4-5 3 2 5 so forth.
Another way to interpret the flow model for a distribution sce-
Figure 22.6
Controlling flow in a network nario is to interpret flow values as amounts of goods, so that a flow
network describes a one-time transfer of goods. For example, we can
We might initialize the flow in this
network by opening the switches interpret the flow in Figure 22.5 as describing the transfer of four items
along the path 0-1-3-5, which from 0 to 5 in the following three-step process: First, send two items
can handle 2 units of flow (top), from 0 to 1 and two items from 0 to 2, leaving two items at each of
and by opening switches along the
those vertices. Second, send one item each from 1 to 3, 1 to 4, 2 to
path 0-2-4-5 to get another 1 unit
of flow in the network (center). 3, and 2 to 4, leaving two items each at 3 and 4. Third, complete the
Asterisks indicate full edges. transfer by sending two items from 3 to 5 and two items from 4 to 5.
Since 0-1, 2-4, and 3-5 are
As with our use of distance in shortest-paths algorithms, we are
full, there is no direct way to get
more flow from 0 to 5, but if we free to abandon any physical intuition when convenient because all
change the switch at 1 to redirect the definitions, properties, and algorithms that we consider are based
enough flow to fill 1-4, we open entirely on an abstract model that does not necessarily obey physical
up enough capacity in 3-5 to al-
laws. Indeed, a prime reason for our interest in the network-flow
low us to add flow on 0-2-3-5,
giving a maxflow for this network model is that it allows us to solve numerous other problems through
(bottom). reduction, as we see in Sections 22.4 and 22.6. Because of this broad
NETWORK FLOW §22.1 361
0-1 1 0 0 0 0
1-3 3 0-1 1
2-0 1 1 2 1-3 2 1 2 1 2 1 2
2-1 1 2-0 1 1-3 1
3-5 4 2-1 1 2-1 1
4-1 1 3-5 3 3-5 2
4-2 2 3 4 4-2 2 3 4 4-2 1 3 4 3-5 1 3 4
4-3 1 4-3 1 4-3 1 4-3 1
5-4 4 5 5-4 3 5 5-4 2 5 5-4 1 5
Figure 22.9
edge that has flow, follow any edge leaving that edge’s destination
Cycle flow decomposition
process vertex that has flow and continue until encountering a vertex that has
To decompose any circulation into already been visited (a cycle has been detected). Go back around the
a set of cycles, we iterate the fol- cycle to find an edge with minimal flow; then reduce the flow on every
lowing process: follow any path edge in the cycle by that amount. Each iteration of this process reduces
until encountering a node for the the flow on at least one edge to 0, so there are at most E cycles.
second time, then find the min-
imum weight on the indicated Figure 22.9 illustrates the process described in the proof. For
cycle, then subtract that weight
st-flows, applying this property to the circulation created by the ad-
from each edge on the cycle and
remove any edge whose weight dition of an edge from t to s gives the result that any st-flow can be
becomes 0. For example, the represented as flow along a set of at most E directed paths, each of
first iteration is to follow the path which is either a path from s to t or a cycle.
0-1-3-5-4-1 to find the cycle
1-3-5-4-1, then subtract 1 from Corollary Any st-network has a maxflow such that the subgraph
the weights of each of the edges induced by nonzero flow values is acyclic.
on the cycle, which causes us to
remove 4-1 because its weight be- Proof : Cycles that do not contain t-s do not contribute to the value of
comes 0. In the second iteration, the flow, so we can change the flow to 0 along any such cycle without
we remove 0-1 and 2-0; in the
third iteration, we remove 1-3, changing the value of the flow.
4-2, and 2-1; and in the fourth it-
Corollary Any st-network has a maxflow that can be represented as
eration, we remove 3-5, 5-4, and
4-3. flow along a set of at most E directed paths from s to t.
Proof : Immediate.
This representation provides a useful insight into the nature of flows
that is helpful in the design and analysis of maxflow algorithms.
On the one hand, we might consider a more general formula-
tion of the maxflow problem where we allow for multiple sources and
sinks. Doing so would allow our algorithms to be used for a broader
range of applications. On the other hand, we might consider special
cases, such as restricting attention to acyclic networks. Doing so might
make the problem easier to solve. In fact, as we see in Section 22.4,
NETWORK FLOW §22.1 365
Figure 22.10
cap flow
0-1 2 2 0 0 1 2 3 4 5 0 1 2 3 4 5 Flow-network representations
0-2 3 1 0 0 2 3 * * * 0 0 2 1 * * * In the adjacency-array represen-
1-3 3 2 1 2 1 -2 0 * 3 1 * 1 -2 0 * 2 0 * tation of a flow network, we use
1-4 1 0 2 -3 * 0 1 1 * 2 -1 * 0 0 1 *
parallel arrays (top): one for ca-
2-3 1 0 3 * -3 -1 0 * 2 3 * -2 0 0 * 2
2-4 1 1
pacities (left) and one for flow val-
4 * -1 -1 * 0 3 4 * 0 -1 * 0 1
3 4 ues (right). We represent an edge
3-5 2 2 5 * * * -2 -3 0 5 * * * -2 -1 0
4-5 3 1 s-t of capacity c by putting c in
5
row s and column t and -c in
0 0 0 0 1 2 2 2 3 1 row t and column s of the array
on the left. We use an identical
1 0 -2 -2 1 0 0 3 3 2 4 1 0
scheme for flow values in the array
2 0 -3 -1 2 0 0 3 1 0 4 1 1 on the right. Including both repre-
3 1 -3 -2 2 -1 0 3 0 0 5 2 2 sentations in this way simplifies our
4 1 -1 0 2 -1 -1 4 0 0 5 3 1
code because our algorithms need
to traverse edges in both directions.
5 3 -2 -2 4 -3 -1 5 0 0
We use a similar scheme
in the adjacency-lists represen-
tation (right). In order to make it
possible to change flow values in
these variants are equivalent in difficulty to the version that we are constant time, links connecting the
considering. Therefore, in the first case, we can adapt our algorithms two representations of each edge
and implementations to the broader range of applications; in the sec- are needed. These links are omit-
ted in this diagram.
ond case, we cannot expect an easier solution. In our figures, we
use acyclic networks because the examples are easier to understand
when they have an implicit flow direction (down the page), but our
implementations allow networks with cycles.
To implement maxflow algorithms, we use standard natural rep-
resentations of flow networks that we derive by extending either the
adjacency-matrix or the adjacency-list representation that we used in
previous chapters. Instead of the single weight that we used in Chap-
ters 20 and 21, we associate two weights with each edge, cap (capacity)
and flow. Even though networks are directed graphs, the algorithms
that we examine need to traverse edges in the both directions, so we
use a representation like the one that we use for undirected graphs: If
there is an edge from x to y with capacity c and flow f, we also keep
an edge from y to x with capacity -c and flow -f. Figure 22.10 shows
an adjacency-matrix representation of the example in Figure 22.5.
In the network representations of Chapters 20 and 21, we used
the convention that weights are real numbers between 0 and 1. In
this chapter, we assume that the weights (capacities and flows) are all
m-bit integers (between 0 and 2m − 1). We do so for two primary
366 §22.1 CHAPTER TWENTY-TWO
#include <stdlib.h>
#include "GRAPH.h"
typedef struct node *link;
struct node
{ int v; int cap; int flow; link dup; link next;};
struct graph
{ int V; int E; link *adj; };
link NEW(int v, int cap, int flow, link next)
{ link x = malloc(sizeof *x);
x->v = v; x->cap = cap; x->flow = flow;
x->next = next;
return x;
}
Graph GRAPHinit(int V)
{ int i;
Graph G = malloc(sizeof *G);
G->adj = malloc(V*sizeof(link));
G->V = V; G->E = 0;
for (i = 0; i < V; i++) G->adj[i] = NULL;
return G;
}
void GRAPHinsertE(Graph G, Edge e)
{ int v = e.v, w = e.w;
G->adj[v] = NEW(w, e.cap, e.flow, G->adj[v]);
G->adj[w] = NEW(v, -e.cap, -e.flow, G->adj[w]);
G->adj[v]->dup = G->adj[w];
G->adj[w]->dup = G->adj[v];
G->E++;
}
NETWORK FLOW §22.1 367
22.16 Write an ADT function for the adjacency-lists representation that reads
values and cycles (one per line, in the format illustrated in Figure 22.8) and
computes the corresponding flow.
22.17 Write an ADT function for the adjacency-lists representation that finds
the cycle representation of a network’s flow using the method described in the
proof of Property 22.2 and prints values and cycles (one per line, in the format
illustrated in Figure 22.8).
◦ 22.18 Write an ADT function for the adjacency-lists representation that re-
moves cycles from an st-flow.
◦ 22.19 Write a program that assigns integer flows to each edge in any given
digraph that contains no sinks and no sources such that the digraph is a flow
network that is a circulation.
0 0 0 0 0 0 0
1 2 1 2 1 2 1 2 1 2 1 2 1 2
3 4 3 4 3 4 3 4 3 4 3 4 3 4
5 5 5 5 5 5 5
Figure 22.13
To improve the algorithm such that it always finds a maxflow, we Augmenting flow along a path
consider a more general way to increase the flow, along any path from This sequence shows the process of
source to sink through the network’s underlying undirected graph. increasing flow in a network along
The edges on any such path are either forward edges, which go with a path of forward and backward
edges. Starting with the flow de-
the flow (when we traverse the path from source to sink, we traverse picted at the left and reading from
the edge from its source vertex to its destination vertex) or backward left to right, we increase the flow
edges, which go against the flow (when we traverse the path from in 0-2 and then 2-3 (additional
source to sink, we traverse the edge from its destination vertex to its flow is shown in black). Then we
decrease the flow in 1-3 (shown
source vertex). Now, for any path with no full forward edges and in white) and divert it to 1-4 and
no empty backward edges, we can increase the amount of flow in the then 4-5, resulting in the flow at
network by increasing flow in forward edges and decreasing flow in the right.
backward edges. The amount by which the flow can be increased
is limited by the minimum of the unused capacities in the forward
edges and the flows in the backward edges. Figure 22.13 depicts an
example. In the new flow, at least one of the forward edges along the
path becomes full or at least one of the backward edges along the path
becomes empty.
The process just sketched is the basis for the classical Ford–
Fulkerson maxflow algorithm (augmenting-path method). We sum-
marize it as follows:
Start with zero flow everywhere. Increase the flow along
any path from source to sink with no full forward edges or
empty backward edges, continuing until there are no such
paths in the network.
Remarkably, this method always finds a maxflow, no matter how we
choose the paths. Like the MST method discussed in Section 20.1 and
the Bellman–Ford shortest-paths method discussed in Section 21.7, it is
a generic algorithm that is useful because it establishes the correctness
of a whole family of more specific algorithms. We are free to use any
method whatever to choose the path.
372 §22.2 CHAPTER TWENTY-TWO
Figure 22.14 0 0 0
Augmenting-path sequences
1 2 1 2 1 2
In these three examples, we aug-
ment a flow along different se-
quences of augmenting paths until
3 4 3 4 3 4
no augmenting path can be found.
The flow that results in each case 5 5 5
is a maximum flow. The key classi-
cal theorem in the theory of net- 0-1-3-5 0-2-4-5 0-2-3-1-4-5
work flows states that we get a
maximum flow in any network, 0 0 0 0
no matter what sequence of paths
we use (see Property 22.5). 1 2 1 2 1 2 1 2
3 4 3 4 3 4 3 4
5 5 5 5
0 0 0 0
1 2 1 2 1 2 1 2
3 4 3 4 3 4 3 4
5 5 5 5
assignment of weights to all the edges). On the contrary, the key fact
of this chapter is that the maxflow and mincut problems are intimately
related. The augmenting-path method itself, in conjunction with two
facts about flows and cuts, provides a proof.
Property 22.3 For any st-flow, the flow across each st-cut is equal
to the value of the flow.
Proof : This property is an immediate consequence of the generaliza-
tion of Property 22.1 that we discussed in the associated proof (see
Figure 22.7). Add an edge t-s with flow equal to the value of the
flow, such that inflow is equal to outflow for any set of vertices. Then,
for any st-cut where Cs is the vertex set containing s and Ct is the
vertex set containing t, the inflow to Cs is the inflow to s (the value of
the flow) plus the sum of the flows in the backward edges across the
cut; and the outflow from Cs is the sum of the flows in the forward
edges across the cut. Setting these two quantities equal establishes the
desired result.
Property 22.4 No st-flow’s value can exceed the capacity of any
st-cut.
Proof : The flow across a cut certainly cannot exceed that cut’s capacity,
so this result is immediate from Property 22.3.
In other words, cuts represent bottlenecks in networks. In our
military application, an enemy that is not able to cut off army troops
completely from their supplies could still be sure that supply flow is
restricted to at most the capacity of any given cut. We certainly might
imagine that the cost of making a cut is proportional to its capacity in
this application, thus motivating the invading army to find a solution
to the mincut problem. More important, these facts also imply, in
particular, that no flow can have value higher than the capacity of any
minimum cut.
Property 22.5 (Maxflow–mincut theorem) The maximum value
among all st-flows in a network is equal to the minimum capacity
among all st-cuts.
Proof : It suffices to exhibit a flow and a cut such that the value of the
flow is equal to the capacity of the cut. The flow has to be a maxflow
because no other flow value can exceed the capacity of the cut and the
NETWORK FLOW §22.2 375
0 1 2 3 4 5 0-1 0-2 5
0 1 2 3 4 5 0-2 1-3 1-4 7
cap flow 0 2 1 3 4 5 0-1 2-3 2-4 4
0-1 2 2 0
0 3 1 2 4 5 0-1 0-2 3-5 1-3 2-3 6
0-2 3 2 0 4 1 2 3 5 0-1 0-2 4-5 1-4 2-4 8
1-3 3 1 1 2
0 1 2 3 4 5 1-3 1-4 2-3 2-4 6
1-4 1 1 0 1 3 2 4 5 0-2 1-4 3-5 2-3 6
2-3 1 1 0 1 4 2 3 5 0-2 1-3 4-5 2-4 9
2-4 1 1 0 2 3 1 4 5 0-1 2-4 3-5 1-3 5
3 4
3-5 2 2 0 2 4 1 3 5 0-1 2-3 4-5 1-4 6
4-5 3 2 5 0 3 4 1 2 5 0-1 0-2 1-3 1-4 2-3 2-4 5
0 1 2 3 4 5 1-4 2-4 3-5 4
0 1 2 4 3 5 1-3 4-5 6
0 2 3 4 1 5 0-1 3-5 4-5 1-3 1-4 7
0 1 2 3 4 5 3-5 4-5 5
Figure 22.16
cut has to be a minimum cut because no other cut capacity can be lower
All st-cuts
than the value of the flow (by Property 22.4). The Ford–Fulkerson
This list gives, for all the st-cuts of
algorithm gives precisely such a flow and cut: When the algorithm the network at left, the vertices in
terminates, identify the first full forward or empty backward edge on the set containing s, the vertices
every path from s to t in the graph. Let Cs be the set of all vertices that in the set containing t, forward
can be reached from s with an undirected path that does not contain edges, backward edges, and ca-
pacity (sum of capacities of the
a full forward or empty backward edge, and let Ct be the remaining forward edges). For any flow, the
vertices. Then, t must be in Ct , so (Cs , Ct ) is an st-cut, whose cut set flow across all the cuts (flow in
consists entirely of full forward or empty backward edges. The flow forward edges minus flow in back-
across this cut is equal to the cut’s capacity (since forward edges are ward edges) is the same. For ex-
ample, for the flow in the network
full and the backward edges are empty) and also to the value of the
at left, the flow across the cut sep-
network flow (by Property 22.3). arating 0 1 3 and 2 4 5 is 2 + 1 +
2 (the flow in 0-2, 1-4, and 3-5,
This proof also establishes explicitly that the Ford–Fulkerson respectively) minus 1 (the flow in
algorithm finds a maxflow. No matter what method we choose to find 2-3), or 4. This calculation also
an augmenting path, and no matter what paths we find, we always end results in the value 4 for every
up with a cut whose flow is equal to its capacity, and therefore also other cut in the network, and the
flow is a maximum flow because
is equal to the value of the network’s flow, which therefore must be a
its value is equal to the capacity
maxflow. of the minimum cut (see Prop-
Another implication of the correctness of the Ford–Fulkerson al- erty 22.5). There are two minimum
gorithm is that, for any flow network with integer capacities, there cuts in this network.
exists a maxflow solution where the flows are all integers. Each aug-
menting path increases the flow by a positive integer (the minimum
of the unused capacities in the forward edges and the flows in the
backward edges, all of which are always positive integers). This fact
justifies our decision to restrict our attention to integer capacities and
flows. It is possible to design a maxflow with noninteger flows, even
when capacities are all integers (see Exercise 22.25), but we do not
376 §22.2 CHAPTER TWENTY-TWO
Figure 22.17
Residual networks (augment-
ing paths)
cap flow 0 0
0-1 2 0 0-1 2 Finding augmenting paths in a flow
0-2 3 0 1 2 0-2 3 1 2 network is equivalent to finding
1-3 3 0 1-3 3 directed paths in the residual net-
1-4 1 0 1-4 1 work that is defined by the flow.
2-3 1 0 2-3 1
2-4 1 0 2-4 1
For each edge in the flow network,
3 4 3 4
3-5 2 0 3-5 2 we create an edge in each direc-
4-5 3 0 5 4-5 3 5 tion in the residual network: one
in the direction of the flow with
weight equal to the unused ca-
pacity and one in the opposite di-
rection with weight equal to the
cap flow 0 0 flow. We do not include edges
0-1 2 2 1-0 2
of weight 0 in either case. Ini-
0-2 3 0 1 2 0-2 3 1 2
1-3 3 2 1-3 1 3-1 2 tially (top), the residual network
1-4 1 0 1-4 1 is the same as the flow network
2-3 1 0 2-3 1 with weights equal to capacities.
2-4 1 0 3 4 2-4 1 3 4 When we augment along the path
3-5 2 2 5-3 2 0-1-3-5 (second from top), we
4-5 3 0 5 4-5 3 5
fill edges 0-1 and 3-5 to capac-
ity, so they switch direction in the
residual network, we reduce the
weight of 1-3 to correspond to
cap flow the remaining flow, and we add
0 0
0-1 2 2 1-0 2 the edge 3-1 of weight 2. Simi-
0-2 3 1 1 2 0-2 2 2-0 1 1 2 lary, when we augment along the
1-3 3 2 1-3 1 3-1 2 path 0-2-4-5, we fill 2-4 to ca-
1-4 1 0 1-4 1
pacity, so it switches direction, and
2-3 1 0 2-3 1
2-4 1 1 3 4 4-2 1 3 4
we have edges in either direction
3-5 2 2 5-3 2 between 0 and 2 and between 4
4-5 3 1 5 4-5 2 5-4 1 5 and 5 to represent flow and un-
used capacity. After we augment
along 0-2-3-1-4-5 (bottom), no
directed paths from source to sink
remain in the residual network, so
cap flow 0 0 there are no augmenting paths.
0-1 2 2 1-0 2
0-2 3 2 1 2 0-2 1 2-0 2 1 2
1-3 3 1 1-3 2 3-1 1
1-4 1 1 4-1 1
2-3 1 1 3-2 1
2-4 1 1 3 4 4-2 1 3 4
3-5 2 2 5-3 2
4-5 3 2 5 4-5 1 5-4 2 5
378 §22.2 CHAPTER TWENTY-TWO
Figure 22.18
Shortest augmenting paths These quantities can vary widely, depending on the network being
This sequence illustrates how the processed and on the graph-search strategy (fringe data structure).
shortest-augmenting-path imple-
mentation of the Ford–Fulkerson Perhaps the simplest Ford–Fulkerson implementation uses the
method finds a maximum flow in shortest augmenting path (as measured by the number of edges on the
a sample network. Path lengths in- path, not flow or capacity). This method was suggested by Edmonds
crease as the algorithm progresses:
The first four paths in the top row
and Karp in 1972. To implement it, we use a queue for the fringe, either
are of length 3; the last path in the by using the value of an increasing counter for P or by using a queue
top row and all of the paths in the ADT instead of a priority-queue ADT in Program 22.3. In this case, the
second row are of length 4; the search for an augmenting path amounts to breadth-first search (BFS) in
first two paths in the bottom row
are of length 5; and the process the residual network, precisely as described in Sections 18.8 and 21.2.
finishes with two paths of length 7 Figure 22.18 shows this implementation of the Ford–Fulkerson method
that each have a backward edge. in operation on a sample network. For brevity, we refer to this method
as the shortest-augmenting-path maxflow algorithm. As is evident
from the figure, the lengths of the augmenting paths form a non-
decreasing sequence. Our analysis of this method, in Property 22.7,
proves that this property is characteristic.
NETWORK FLOW §22.2 381
Figure 22.19
Another Ford–Fulkerson implementation suggested by Edmonds Maximum-capacity augment-
and Karp is the following: Augment along the path that increases ing paths
the flow by the largest amount. To implement this method with our This sequence illustrates how the
generic PFS implementation, we use the priority maximum-capacity–augmenting-
path implementation of the Ford-
#define P ( Q > wt[v] ? wt[v] : Q ) Fulkerson method finds a maxflow
in a sample network. Path capaci-
in Program 22.3. This priority makes the algorithm choose edges
ties decrease as the algorithm pro-
from the fringe to give the maximum amount of flow that can be gresses, but their lengths may in-
pushed through a forward edge or diverted from a backward edge. For crease or decrease. The method
brevity, we refer to this method as the maximum-capacity augmenting- needs only nine augmenting paths
to compute the same maxflow as
path maxflow algorithm. Figure 22.19 illustrates the algorithm on the
the one depicted in Figure 22.18.
same flow network as that in Figure 22.18.
These are but two examples (ones we can analyze!) of Ford–
Fulkerson implementations. At the end of this section, we consider
others. Before doing so, we consider the task of analyzing augmenting-
path methods, so as to learn their properties and, ultimately, to decide
which one will have the best performance.
In trying to choose among the family of algorithms represented
by Program 22.3, we are in a familiar situation. Should we focus on
worst-case performance guarantees, or do those represent a mathe-
matical fiction that may not relate to networks that we encounter in
practice? This question is particularly relevant in this context, because
the classical worst-case performance bounds that we can establish are
much higher than the actual performance results that we see for typical
382 §22.2 CHAPTER TWENTY-TWO
Proof : Any cut has at most V edges, of capacity M , for a total capacity
of V M . Every augmenting path increases the flow through every cut
by at least 1, so the algorithm must terminate after V M passes, since
all cuts must be filled to capacity after that many augmentations.
Figure 22.20
also gives an upper bound on the running time of any Ford–Fulkerson Two scenarios for the Ford–
implementation that is useful in many practical situations: Fulkerson algorithm
This network illustrates that the
Corollary The time required to find a maxflow is O(V EM ), which
number of iterations used by the
is O(V 2 M ) for sparse networks. Ford-Fulkerson algorithm depends
on the capacities of the edges in
Proof : Immediate from the basic result that generalized graph search the network and the sequence of
is linear in the size of the graph representation (Property 18.12). As paths chosen by the implementa-
mentioned, we need an extra lg V factor if we are using a priority-queue tion. It consists of four edges of
fringe implementation. capacity X and one of capacity 1.
The scenario depicted at the top
For sparse networks and networks with small integer capacities, shows that an implementation that
alternates between using 0-1-2-3
this bound is reasonably low. The proof actually establishes that the
and 0-2-1-3 as augmenting paths
factor of M can be replaced by the ratio between the largest and small- (for example, one that prefers long
est nonzero capacities in the network (see Exercise 22.27). When this paths) would require X pairs of it-
ratio is low, the bound tells us that any Ford–Fulkerson implementa- erations like the two pairs shown,
each pair incrementing the total
tion will find a maxflow in time proportional to the time required to
flow by 2. The scenario depicted
(for example) solve the all-shortest-paths problem, in the worst case. at the bottom shows that an imple-
There are many situations where the capacities are indeed low and the mentation that chooses 0-1-3 and
factor of M is of no concern. We will see an example in Section 22.4. then 0-2-3 as augmenting paths
(for example, one that prefers short
When M is large, the V EM worst-case bound is high; but it
paths) finds the maximum flow in
is pessimistic, as we obtained it by multiplying together worst-case just two iterations.
bounds that derive from contrived examples. Actual costs on practical If edge capacities are, say,
networks are typically much lower. 32-bit integers, the scenario de-
picted at the top would be billions
From a theoretical standpoint, our first goal is to discover, using
of times slower than the scenario
the rough subjective categorizations of Section 17.8, whether or not depicted the bottom.
the maximum-flow problem for networks with large integer weights is
tractable (solvable by a polynomial-time algorithm). The bounds just
derived do not resolve this question, because the maximum weight
384 §22.2 CHAPTER TWENTY-TWO
augmenting path can decrease the length of the shortest path from the
source s to any vertex in the residual network. Suppose that some
augmenting path does so, and that v is the first such vertex on the
path. There are two cases to consider: Either no vertex on the new
shorter path from s to v appears anywhere on the augmenting path or
some vertex w on the new shorter path from s to v appears somewhere
between v and t on the augmenting path. Both situations contradict
the minimality of the augmenting path.
Now, by construction, every augmenting path has at least one
critical edge: an edge that is deleted from the residual network be-
cause it corresponds either to a forward edge that becomes filled to
capacity or a backward edge that is emptied. Suppose that an edge
u-v is a critical edge for an augmenting path P of length d. The next
augmenting path for which it is a critical edge has to be of length at
least d+2, because that path has to go from s to v, then along v-u, then
from u to t. The first segment is of length at least 1 greater than the
distance from s to u in P , and the final segment is of length at least 1
greater than the distance from v to t in P , so the path is of length at
least 2 greater than P .
Since augmenting paths are of length at most V , these facts imply
that each edge can be the critical edge on at most V /2 augmenting
paths, so the total number of augmenting paths is at most EV /2.
Figure 22.21
Stack-based augmenting-path smaller than V . Given this range between best- and worst-case perfor-
search
mance, comparing augmenting-path algorithms solely on the basis of
This illustrates the result of using worst-case bounds is not wise.
a stack for the generalized queue
in our implementation of the Ford– Still, other implementations that are nearly as simple as the
Fulkerson method, so that the path shortest-augmenting-path method might admit better bounds or
search operates like DFS. In this be preferred in practice (or both). For example, the maximum-
case, the method does about as
well as BFS, but its somewhat er- augmenting-path algorithm used far fewer paths to find a maxflow
ratic behavior is rather sensitive to than did the shortest-augmenting-path algorithm in the example illus-
the network representation and has trated in Figures 22.18 and 22.19. We now turn to the worst-case
not been analyzed.
analysis of that algorithm.
First, just as for Prim’s algorithm and for Dijkstra’s algorithm
(see Sections 20.6 and 21.2), we can implement the priority queue such
that the algorithm takes time proportional to V 2 (for dense graphs)
or (E + V ) log V (for sparse graphs) per iteration in the worst case,
although these estimates are pessimistic because the algorithm stops
when it reaches the sink. We also have seen that we can do slightly
NETWORK FLOW §22.2 387
Figure 22.22
better with advanced data structures. The more important and more Randomized augmenting-path
challenging question is how many augmenting paths are needed. search
Property 22.8 The number of augmenting paths needed in the This sequence the result of using
a randomized queue for the fringe
maximal-augmenting-path implementation of the Ford–Fulkerson al- data structure in the augmenting-
gorithm is at most 2E lg M . path search in the Ford-Fulkerson
method. In this example, we hap-
Proof : Given a network, let F be its maxflow value. Let v be the pen upon the short high-capacity
value of the flow at some point during the algorithm as we begin to path and therefore need relatively
look for an augmenting path. Applying Property 22.2 to the residual few augmenting paths. While pre-
dicting the performance character-
network, we can decompose the flow into at most E directed paths
istics of this method is a challeng-
that sum to F − v, so the flow in at least one of the paths is at least ing task, it performs well in many
(F − v)/E. Now, either we find the maxflow sometime before doing situations.
another 2E augmenting paths or the value of the augmenting path
after that sequence of 2E paths is less than (F − v)/2E, which is less
than one-half of the value of the maximum before that sequence of 2E
paths. That is, in the worst case, we need a sequence of 2E paths to
decrease the path value by a factor of 2. The first path value is at most
M , which we need to decrease by a factor of 2 at most lg M times, so
we have a total of at most lg M sequences of 2E paths.
Corollary The time required to find a maxflow in a sparse network
is O(V 2 lg M lg V ).
Proof : Immediate from the use of a heap-based priority-queue imple-
mentation, as for Properties 20.7 and 21.5.
388 §22.2 CHAPTER TWENTY-TWO
Figure 22.23
Random flow networks
This figure depicts maxflow com-
putations on our random Euclidean
graph, with two different capac-
ity models. On the left, all edges
are assigned unit capacities; on the
right edges are assigned random
capacities. The source is near the
middle at the top and the sink near
the middle at the bottom. Illus-
trated top to bottom are the flows
computed by the shortest-path,
maximum-capacity, stack-based,
and randomized algorithms, re-
spectively. Since the vertices are
not of high degree and the capac-
ities are small integers, there are
many different flows that achieve
the maximum for these examples.
The indegree of the sink is 6,
so all the algorithms find the flow
in the unit-capacity model on the
left with 6 augmenting paths.
The methods find augment-
ing paths that differ dramatically
in character for the random-weight
model on the right. In particular,
the stack-based method finds long
paths on low weight, and even
produces a flow with a discon-
nected cycle.
390 §22.2 CHAPTER TWENTY-TWO
Figure 22.24
Maximum-capacity augment- V E/2 in Property 22.7 for this network is 177,000, but the shortest-
ing paths (larger example) path algorithm needs only 37 paths.
This figure depicts the augmenting As we have already indicated, the relatively low node degree
paths computed by the maximum- and the locality of the connections partially explain these differences
capacity algorithm for the Eu- between theoretical and actual performance in this case. We can prove
clidean network with random
more accurate performance bounds that account for such details; but
weights that is shown in Fig-
ure 22.23, along with the edges in such disparities are the rule, not the exception, in flow-network models
the graph-search spanning tree (in and in practical networks. On the one hand, we might take these
gray). The resulting flow is shown results to indicate that these networks are not sufficiently general to
at the bottom right. represent the networks that we encounter in practice; on the other
hand, perhaps the worst-case analysis is more removed from practice
than these kinds of networks.
Large gaps like this certainly provide strong motivation for re-
searchers seeking to lower the worst-case bounds. There are many
other possible implementations of augmenting-path algorithms to con-
sider that might lead to better worst-case performance or better prac-
tical performance than the methods that we have considered (see Exer-
cises 22.57 through 22.61). Numerous methods that are more sophis-
ticated and have been shown to have improved worst-case performance
can be found in the research literature (see reference section).
NETWORK FLOW §22.2 393
Figure 22.25
Shortest augmenting paths
(larger example)
This figure depicts the augmenting
paths computed by the shortest-
paths algorithm for the Euclidean
network with random weights that
is shown in Figure 22.23, along
with the edges in the graph-search
spanning tree (in gray). This al-
gorithm is much slower than the
maximum-capacity algorithm de-
picted in Figure 22.24 in this case
both because it requires a large
number of augmenting paths (the
paths shown are just the first 12
out of a total of 37) and because
the spanning trees are larger (usu-
ally containing nearly all of the
vertices).
394 §22.2 CHAPTER TWENTY-TWO
◦ 22.42 Give an example showing that not all maxflows can be the result of
starting with an empty network and augmenting along a sequence of simple
paths from source to sink.
396 §22.3 CHAPTER TWENTY-TWO
22.43 Experiment with hybrid methods that use one augmenting-path method
at the beginning, then switch to a different augmenting path to finish up (part of
your task is to decide what are appropriate criteria for when to switch). Run
empirical studies for various networks (see Exercises 22.7–12) to compare
these to the basic methods, studying methods that perform better than others
in more detail.
22.44 Experiment with hybrid methods that alternate between two or more
different augmenting-path methods. Run empirical studies for various net-
works (see Exercises 22.7–12) to compare these to the basic methods, study-
ing variations that perform better than others in more detail.
◦ 22.45 Experiment with hybrid methods that choose randomly among two or
more different augmenting-path methods. Run empirical studies for various
networks (see Exercises 22.7–12) to compare these to the basic methods,
studying variations that perform better than others in more detail.
22.46 [Gabow] Develop a maxflow implementation that uses m = lg M
phases, where the ith phase solves the maxflow problem using the leading
i bits of the capacities. Start with zero flow everywhere; then, after the first
phase, initialize the flow by doubling the flow found during the previous
phase. Run empirical studies for various networks (see Exercises 22.7–12) to
compare this implementation to the basic methods.
• 22.47 Prove that the running time of the algorithm described in Exer-
cise 22.46 is O(V E lg M).
◦ 22.48 Write a flow-network ADT function that, given an integer c, finds an
edge for which increasing the capacity of that edge by c increases the maxflow
by the maximum amount. Your function may assume that the client has
already called GRAPHmaxflow to compute a maximum flow.
•• 22.49 Suppose that you are given a mincut for a network. Does this infor-
mation make it easier to compute a maxflow? Develop an algorithm that uses
a given mincut to speed up substantially the search for maximum-capacity
augmenting paths.
• 22.50 Write a client program that does dynamic graphical animations of
augmenting-path algorithms. Your program should produce images like Fig-
ure 22.18 and the other figures in this section (see Exercises 17.55–59). Test
your implementation for the Euclidean networks among Exercises 22.7–12.
0 0 0
0 0 0 0
3
1 1 1 1 1 1 1
2 3 2 3 2 3 2 3 2 2 3 2 3
4 4 4 4 4 4 4
1 2 3 3 3 1 2
earlier algorithms. It is widely used because of its simplicity, flexibility, Figure 22.26
Preflow-push example
and efficiency.
In the preflow-push algorithm,
As defined in Section 22.1, a flow must satisfy the equilibrium
we maintain a list of the active
conditions that the outflow from the source is equal to the inflow to nodes that have more incoming
the sink and that inflow is equal to the outflow at each of the internal than outgoing flow (shown below
nodes. We refer to such a flow as a feasible flow. An augmenting- each network). One version of the
path algorithm always maintains a feasible flow: It increases the flow algorithm is a loop that chooses
an active node from the list and
along augmenting paths until a maxflow is achieved. By contrast, pushes flow along outgoing edges
the preflow-push algorithms that we consider in this section maintain until it is no longer active, perhaps
maxflows that are not feasible because some vertices have more inflow creating other active nodes in the
than outflow: They push flow through such vertices until a feasible process. In this example, we push
flow along 0-1, which makes 1
flow is achieved (no such vertices remain). active. Next, we push flow along
Definition 22.5 In a flow network, a preflow is a set of positive edge 1-2 and 1-3, which makes 1 inac-
tive but 2 and 3 both active. Then,
flows satisfying the conditions that the flow on each edge is no greater we push flow along 2-4, which
than that edge’s capacity and that inflow is no smaller than outflow makes 2 inactive. But 3-4 does
for every internal vertex. An active vertex is an internal vertex whose not have sufficient capacity for
inflow is larger than its outflow (by convention, the source and sink us to push flow along it to make
3 inactive, so we also push flow
are never active). back along 3-1 to do so, which
We refer to the difference between an active vertex’s inflow and makes 1 active. Then we can push
the flow along 1-2 and then 2-4,
outflow as that vertex’s excess. To change the set of active vertices, we
which makes all nodes inactive
choose one and push its excess along an outgoing edge, or, if there is and leaves a maxflow.
insufficient capacity to do so, push the excess back along an incoming
edge. If the push equalizes the vertex’s inflow and outflow, the vertex
becomes inactive; the flow pushed to another vertex may activate that
vertex. The preflow-push method provides a systematic way to push
excess out of active vertices repeatedly, such that the process terminates
in a maxflow, with no active vertices. We keep active vertices on a
generalized queue. As for the augmenting-path method, this decision
gives a generic algorithm that encompasses a whole family of more
specific algorithms.
398 §22.3 CHAPTER TWENTY-TWO
0
The intuition behind height functions is the following: When an active 5
1 2
node’s height is less than the height of the source, it is possible that 4 3
there is some way to push flow from that node down to the sink; when 1 2
an active node’s height exceeds the height of the source, we know 3 4
0
that that node’s excess needs to be pushed back to the source. To 5
establish this latter fact, we reorient our view of Property 22.9, where
we thought about the length of the shortest path to the sink as an 0 1 2 3 4 5
function and assign zero flow to all edges except those connected to
the source, which we fill to capacity. Then, we repeat the following
step until no active vertices remain:
Choose an active vertex. Push flow through some eligible
edge leaving that vertex (if any). If there are no such edges,
increment the vertex’s height.
We do not specify what the initial height function is, how to choose
the active vertex, how to choose the eligible edge, or how much flow to
push. We refer to this generic method as the edge-based preflow-push
algorithm.
The algorithm depends on the height function to identify eligible
edges. We also use the height function to prove that the algorithm
computes a maxflow, and to analyze performance. Therefore, it is
critical to ensure that the height function remains valid throughout the
execution of the algorithm.
Property 22.10 The edge-based preflow-push algorithm preserves
the validity of the height function.
Proof : We increment h(u) only if there are no edges u-v with h(u) =
h(v)+1. That is, h(u) < h(v)+1 for all edges u-v before incrementing
h(u), so h(u) ≤ h(v) + 1 afterward. For any incoming edges w-u,
incrementing h(u) certainly preserves the inequality h(w) ≤ h(u) + 1.
Incrementing h(u) does not affect inequalities corresponding to any
other edge, and we never increment h(t) (or h(s)). Together, these
observations imply the stated result.
All the excess flow emanates from the source. Informally, the
generic preflow-push algorithm tries to push the excess flow to the
sink; if it cannot do so, it eventually pushes the excess flow back to the
source. It behaves in this manner because nodes with excess always
stay connected to the source in the residual network.
Property 22.11 While the preflow-push algorithm is in execution on
a flow network, there exists a (directed) path in that flow network’s
residual network from each active vertex to the source, and there are
no (directed) paths from source to sink in the residual network.
Proof : By induction. Initially, the only flow is in the edges leaving the
source, which are filled to capacity, so the destination vertices of those
edges are the only active vertices. Since the edges are filled to capacity,
NETWORK FLOW §22.3 401
Figure 22.29
Residual networks (FIFO
preflow-push)
cap flow 0 0
0-1 2 2 1-0 2 This figure shows the flow net-
0-2 3 3 1 2 2-0 3 1 2 works (left) and the residual net-
1-3 3 0 1-3 3 works (right) for each phase of the
1-4 1 0 1-4 1 FIFO preflow-push algorithm op-
2-3 1 0 2-3 1
2-4 1 0 2-4 1
erating on our sample network.
3 4 3 4
3-5 2 0 3-5 2 Queue contents are shown below
4-5 3 0 5 4-5 3 5 the flow networks and distance la-
bels below the residual networks.
1 2 3 2 2 1 1 0 In the initial phase, we push flow
through 0-1 and 0-2, thus mak-
ing 1 and 2 active. In the second
cap flow 0 0 phase, we push flow from these
0-1 2 2 1-0 2
two vertices to 3 and 4, which
0-2 3 3 1 2 2-0 3 1 2
1-3 3 1 1-3 2 3-1 1 makes them active and 1 inactive
1-4 1 1 4-1 1 (2 remains active and its distance
2-3 1 1 3-2 1 label is incremented). In the third
2-4 1 1 3 4 4-2 1 3 4 phase, we push flow through 3 and
3-5 2 0 3-5 2 4 to 5, which makes them inactive
4-5 3 0 5 4-5 3 5
(2 still remains active and its dis-
3 2 3 1 1 0 tance label is again incremented).
1 2 3 4 2
In the fourth phase, 2 is the only
active node, and the edge 2-0 is
cap flow admissible because of the distance-
0 0
0-1 2 2 1-0 2 label increments, and one unit of
0-2 3 3 1 2 2-0 3 1 2 flow is pushed back along 2-0 to
1-3 3 1 1-3 2 3-1 1 complete the computation.
1-4 1 1 4-1 1
2-3 1 1 3-2 1
2-4 1 1 3 4 4-2 1 3 4
3-5 2 2 5-3 2
4-5 3 2 5 4-5 1 5-4 2 5
3 4 2 2 3 2 4 1 1 0
cap flow 0 0
0-1 2 2 1-0 2
0-2 3 2 1 2 0-2 1 2-0 2 1 2
1-3 3 1 1-3 2 3-1 1
1-4 1 1 4-1 1
2-3 1 1 3-2 1
2-4 1 1 3 4 4-2 1 3 4
3-5 2 2 5-3 2
4-5 3 2 5 4-5 1 5-4 2 5
404 §22.3 CHAPTER TWENTY-TWO
0
Figure 22.30
FIFO preflow-push worst case
This network represents a family 1 2 3 4 5 6 7 8 9 10
of networks with V vertices such
that the total running time of the 9 8 7 6 5 4 3 2 1
preflow-push algorithm is propor-
tional to V 2 . It consists of unit- 0
capacity edges emanating from
the source (vertex 0) and hori-
zontal edges of capacity v − 2 1 2 3 4 5 6 7 8 9 10
running from left to right towards
the sink (vertex 10). In the ini-
tial phase of the preflow-push al- 9 8 7 6 5 4 3 2
gorithm (top), we push one unit
of flow out each edge from the 0
source, making all the vertices ac-
tive except the source and the sink.
In a standard adjacency-lists rep- 1 2 3 4 5 6 7 8 9 10
resentation, they appear on the
FIFO queue of active vertices in
reverse order, as shown below the 9 8 7 6 5 4 3
network. In the second phase (cen-
ter), we push one unit of flow from
9 to 10, making 9 inactive (tem-
porarily); then we push one unit Because our implementations maintain an implicit representation
of flow from 8 to 9, making 8 in- of the residual network, they examine edges leaving a vertex even
active (temporarily) and making 9 when those edges are not in the residual network (to test whether or
active; then we push one unit of
not they are there). It is possible to show that we can reduce the
flow from 7 to 8, making 7 inactive
(temporarily) and making 8 active; bound in Property 22.13 from V 2 E to V 3 for an implementation that
and so forth. Only 1 is left inac- eliminates this cost by maintaining an explicit representation of the
tive. In the third phase (bottom), residual network. Although the theoretical bound is the lowest that
we go through a similar process
we have seen for the maxflow problem, this change may not be worth
to make 2 inactive, and the same
process continues for V −2 phases. the trouble, particularly for the sparse graphs that we see in practice
(see Exercises 22.64 through 22.66).
Again, these worst-case bounds tend to be pessimistic and thus
not necessarily useful for predicting performance on real networks
(though the gap is not as excessive as we found for augmenting-path
algorithms). For example, the FIFO algorithm finds the flow in the
network illustrated in Figure 22.31 in 15 phases, whereas the bound
in the proof of Property 22.13 says only that it must do so in fewer
than 182.
To improve performance, we might try using a stack, a random-
ized queue, or any other generalized queue in Program 22.4. One
NETWORK FLOW §22.3 407
Figure 22.31
Preflow-push algorithm
(FIFO)
This sequence illustrates how
the FIFO implementation of the
preflow-push method finds a maxi-
mum flow in a sample network. It
proceeds in phases: First it pushes
as much flow as it can from the
source along the edges leaving the
source (top left). Then, it pushes
flow from each of those nodes,
continuing until all nodes are in
equilibrium.
408 §22.3 CHAPTER TWENTY-TWO
vertices edges
graphs) has been proved for this algorithm (see reference section);
as usual, this bound is pessimistic. Many other preflow-push vari-
ants have been proposed, several of which reduce the worst-case time
bound to be close to V E (see reference section).
Table 22.2 shows performance results for preflow-push algo-
rithms corresponding to those for augmenting-path algorithms in Ta-
ble 22.1, for the two network models discussed in Section 22.2. These
experiments show much less performance variation for the various
NETWORK FLOW §22.3 409
Exercises
22.51 Describe the operation of the preflow-push algorithm in a network
whose capacities are in equilibrium.
22.52 Use the concepts described in this section (height functions, eligi-
ble edges, and pushing of flow through edges) to describe augmenting-path
maxflow algorithms.
22.53 Show, in the style of Figure 22.29, the flow and residual networks after
each phase when you use the FIFO preflow-push algorithm to find a maxflow
in the flow network shown in Figure 22.11.
22.56 Plot the number of active vertices and the number of vertices and edges
in the residual network as the FIFO preflow-push algorithm proceeds, for
specific instances of various networks (see Exercises 22.7–12).
◦ 22.59 Evaluate the idea of pushing excess flow out of vertices by spreading it
evenly among the outgoing edges, rather than perhaps filling some and leaving
others empty.
22.60 Run empirical tests to determine whether the shortest-paths computa-
tion for the initial height function is justified in Program 22.4 by comparing
its performance as given for various networks (see Exercises 22.7–12)with its
performance when the vertex heights are just initialized to zero.
◦ 22.65 Sharpen the bound in Property 22.13 to O(V 3 ) for the implementation
of Exercise 22.64. Hint: Prove separate bounds on the number of pushes that
correspond to deletion of edges in the residual network and on the number of
pushes that do not result in full or empty edges.
22.66 Run empirical studies for various networks (see Exercises 22.7–12) to
determine the effect of using an explicit representation of the residual network
(see Exercise 22.64) on actual running times.
22.67 For the edge-based generic preflow-push algorithm, prove that the num-
ber of pushes that correspond to deleting an edge in the residual network is less
than 2V E. Assume that the implementation keeps an explicit representation
of the residual network.
NETWORK FLOW §22.4 411
• 22.68 For the edge-based generic preflow-push algorithm, prove that the num-
ber of pushes that do not correspond to deleting an edge in the residual network
is less than 4V 2 (V + E). Hint: Use the sum of the heights of the active vertices
as a potential function.
• 22.69 Run empirical studies to determine the actual number of edges ex-
amined and the ratio of the running time to V for several versions of the
preflow-push algorithm for various networks (see Exercises 22.7–12). Con-
sider various algorithms described in the text and in the previous exercises, and
concentrate on those that perform the best on huge sparse networks. Compare
your results with your result from Exercise 22.36.
• 22.70 Write a client program that does dynamic graphical animations of
preflow-push algorithms. Your program should produce images like Fig-
ure 22.31 and the other figures in this section (see Exercise 22.50). Test
your implementation for the Euclidean networks among Exercises 22.7–12.
cap
0
need only to show a reduction to the standard problem. Given a flow 0-1 2
0-2 3
network with capacity constraints, construct a standard flow network 1-3 3
1 2
with two vertices u and u* corresponding to each original vertex u, 1-4 1
with all incoming edges to the original vertex going to u, all outgoing 2-3 1
2-4 1 3 4
edges coming from u*, and an edge u-u* of capacity equal to the vertex 3-5 2
capacity. This construction is illustrated in Figure 22.33. The flows 4-5 3 5
in the edges of the form u*-v in any maxflow for the transformed 0 1 2 3 4 5
network give a maxflow for the original network that must satisfy the capV 4 4 3 1 3 4
digraphs with edge weights that could be negative (see Section 21.7), 11
which is simple to solve in linear time if there are no cycles but NP-
complete if cycles are allowed. But the maxflow problem, remarkably, Figure 22.33
is no easier for acyclic networks. Removing vertex capacities
To solve the problem of finding a
Property 22.16 The maxflow problem for acyclic networks is equiv- maxflow in the network at the top
alent to the standard maxflow problem. such that flow through each ver-
tex does not exceed the capacity
Proof : Again, we need only to show that the standard problem reduces bound given in the vertex-indexed
to the acyclic problem. Given any network with V vertices and E array capV, we build the standard
edges, we construct a network with 2V + 2 vertices and E + 3V edges network at the bottom: Associate a
that is not just acyclic but has a simple structure. new vertex u* (where u* denotes
u+V) with each vertex u, add an
Let u* denote u+V, and build a bipartite digraph consisting of edge u-u* whose capacity is the
two vertices u and u* corresponding to each vertex u in the original capacity of u, and include an edge
network and one edge u-v* corresponding to each edge u-v in the u*-v for each edge u-v. Each
original network with the same capacity. Now, add to the bipartite u-u* pair is encircled in the dia-
gram. Any flow in the bottom net-
digraph a source s and a sink t and, for each vertex u in the original work corresponds directly to a flow
graph, an edge s-u and an edge u*-t, both of capacity equal to the sum in the top network that satisfies the
of the capacities of u’s outgoing edges in the original network. Also, vertex-capacity constraints.
414 §22.4 CHAPTER TWENTY-TWO
let X be the sum of the capacities of the edges in the original network,
0-1 2
0
and add edges from u to u*, with capacity X + 1. This construction is
0-2 3
1-2 3 illustrated in Figure 22.34.
1 2
1-4 2
2-3 2
To show that any maxflow in the original network corresponds
2-4 1 to a maxflow in the transformed network, we consider cuts rather than
3-1 3
3-5 2
3 4 flows. Given any st-cut of size c in the original network, we show how
4-3 3 5
to construct an st-cut of size c + X in the transformed network; and,
4-5 3
given any minimal st-cut of size c + X in the transformed network, we
show how to construct an st-cut of size c in the original network. Thus,
0-6 25 12-0 5 6-13 5 given a minimal cut in the transformed network, the corresponding cut
1-7 25 12-1 5 7-13 5
2-8 25 12-2 3 8-13 3 in the original network is minimal. Moreover, our construction gives
3-9 25 12-3 5 9-13 5 a flow whose value is equal to the minimal-cut capacity, so it is a
4-10 25 12-4 6 10-13 6
5-11 25 12-5 0 11-13 0 maxflow.
0-7 2 Given any cut of the original network that separates the source
0-8 3 12
1-8 3 from the sink, let S be the source’s vertex set and T the sink’s vertex
1-10 2 set. Construct a cut of the transformed network by putting vertices in
2-9 2
2-10 1 0 1 2 3 4 5 S in a set with s and vertices in T in a set with t and putting u and u*
3-7 3 on the same side of the cut for all u, as illustrated in Figure 22.34. For
3-11 2
4-9 3 6 7 8 9 10 11
every u, either s-u or P|u*-t| is in the cut set, and u-v* is in the cut set
4-11 3 if and only if u-v is in the cut set of the original network; so the total
capacity of the cut is equal to the capacity of the cut in the original
13
network plus X.
Figure 22.34 Given any minimal st-cut of the transformed network, let S ∗ be
Reduction to acyclic network s’s vertex set and T ∗ t’s vertex set. Our goal is to construct a cut of
Each vertex u in the top network the same capacity with u and u* both in the same cut vertex set for
corresponds to two vertices u all u, so that the correspondence of the previous paragraph gives a cut
and u* (where u* denotes u+V) in the original network, completing the proof. First, if u is in S ∗ and
in the bottom network and each
edge u-v in the top network cor-
u* in T ∗ , then u-u* must be a crossing edge, which is a contradiction:
responds to an edge u-v* in the u-u* cannot be in any minimal cut, because a cut consisting of all the
bottom network. Additionally, the edges corresponding to the edges in the original graph is of lower cost.
bottom network has uncapacitated Second, if u is in T ∗ and u* is in S ∗ , then s-u must be in the cut,
edges u-u*, a source s with an
edge to each unstarred vertex and because that is the only edge connecting s to u. But we can create a
a sink t with an edge from each cut of equal cost by substituting all the edges directed out of u for s-u,
starred vertex. The shaded and un- moving u to S ∗ .
shaded vertices (and edges which
connect shaded to unshaded) illus-
Given any flow in the transformed network of value c + X, we
trate the direct relationship among simply assign the same flow value to each corresponding edge in the
cuts in the two networks (see text). original network to get a flow with value c. The cut transformation
NETWORK FLOW §22.4 415
at the end of the previous paragraph does not affect this assignment,
because it manipulates edges with flow value zero.
The result of the reduction not only is an acyclic network, but also
has a simple bipartite structure. The reduction says that we could, if we
wished, adopt these simpler networks, rather than general networks, as
our standard. It would seem that perhaps this special structure would
lead to faster maxflow algorithms. But the reduction shows that we
could use any algorithm that we found for these special acyclic net-
works to solve maxflow problems in general networks, at only modest
extra cost. Indeed, the classical maxflow algorithms exploit the flex-
ibility of the general network model: Both the augmenting-path and
preflow-push approaches that we have considered use the concept of a
residual network, which involves introducing cycles into the network.
When we have a maxflow problem for an acyclic network, we typically
use the standard algorithm for general networks to solve it.
The construction of Property 22.16 is elaborate, and it illustrates
that reduction proofs can require care, if not ingenuity. Such proofs
are important because not all versions of the maxflow problem are
equivalent to the standard problem, and we need to know the extent
of the applicability of our algorithms. Researchers continue to explore
this topic because reductions relating various natural problems have
not yet been established, as illustrated by the following example.
Maxflow in undirected networks An undirected flow network
is a weighted graph with integer edge weights that we interpret to be
capacities. A circulation in such a network is an assignment of weights
and directions to the edges satisfying the conditions that the flow on
each edge is no greater than that edge’s capacity and that the total
flow into each vertex is equal to the total flow out of that vertex. The
undirected maxflow problem is to find a circulation that maximizes the
flow in specified direction in a specified edge (that is, from some vertex
s to some other vertex t). This problem perhaps corresponds more
naturally than the standard problem to our liquid-flowing-through-
pipes model: It corresponds to allowing liquid to flow through a pipe
in either direction.
0 1 2 3 4 5
supply 3 3 1
demand 1 1 5
Figure 22.36
problem answers the basic question of whether it is possible to find a
Feasible flow
way to ship the goods such that supply meets demand everywhere.
In a feasible-flow problem, we
Property 22.18 The feasible-flow problem reduces to the maxflow specify supply and demand con-
problem. straints at the vertices in addition
to the capacity constraints on the
Proof : Given a feasible-flow problem, construct a network with the edges. We seek any flow for which
outflow equals supply plus in-
same vertices and edges but with no weights on the vertices. Instead,
flow at supply vertices and inflow
add a source vertex s that has an edge to each supply vertex with equals outflow plus demand at de-
weight equal to that vertex’s supply and a sink vertex t that has an mand vertices. Three solutions to
edge from each demand vertex with weight equal to the negation of the feasible-flow problem at left
that vertex’s demand (so that the edge weight is positive). Solve the are shown on the right.
maxflow problem on this network. The original network has a feasible
flow if and only if all the edges out of the source and all the edges into
the sink are filled to capacity in this flow. Figure 22.37 illustrates an
example of this reduction.
Developing ADT functions that implement reductions of the
type that we have been considering can be a challenging software-
engineering task, primarily because the objects that we are manipu-
lating are represented with complicated data structures. To reduce
another problem to a standard maxflow problem, should we create a
new network? Some of the problems require extra data, such as vertex
capacities or supply and demand, so creating a standard network with-
out these data might be justified. But if we do so and then compute
a maxflow, what are we to do with the result? Transferring the com-
puted flow (a weight on each edge) from one network to another when
both are represented with adjacency lists is not a trivial computation.
If we do not make a copy of the network, we certainly may disturb
it by adding dummy vertices and computing a maxflow; so we need
418 §22.4 CHAPTER TWENTY-TWO
to take care to restore the network to its original state after the com-
putation. Program 22.5 is an implementation that illustrates some of
these considerations in a network ADT function for the feasible-flow
problem using the reduction of Property 22.16.
A canonical example of a flow problem that we cannot han-
dle with the maxflow model, and that is the subject of Sections 22.5
and 22.6, is an extension of the feasible-flow problem. We add a sec-
ond set of edge weights that we interpret as costs, define flow costs
in terms of these weights, and ask for a feasible flow of minimal cost.
This model formalizes the general merchandise-distribution problem.
We are interested not just in whether it is possible to move the goods,
but also in what is the lowest-cost way to move them.
All the problems that we have considered so far in this section cap
have the same basic goal (computing a flow in a flow network), so it is 0-1 2
0-2 3
perhaps not surprising that we can handle them with a flow-network 1-3 3 6
problem-solving model. As we saw with the maxflow–mincut theorem, 1-4 1 0
2-3 1
we can use maxflow algorithms to solve graph-processing problems 2-4 1 1 2
that seem to have little to do with flows. We now turn to examples of 3-5 2
4-5 3
this kind. 6-0 3
3 4
Maximum-cardinality bipartite matching Given a bipartite 6-1 3
6-3 1
graph, find a set of edges of maximum cardinality such that each 2-7 1
5
7
vertex is connected to at most one other vertex. 4-7 1
5-7 5
For brevity, we refer to this problem simply as the bipartite-
matching problem except in contexts where we need to distinguish
it from similar problems. It formalizes the job-placement problem Figure 22.37
Reduction from feasible flow
discussed at the beginning of this chapter. Vertices correspond to
individuals and employers; edges correspond to a “mutual interest This network is a standard network
constructed from the feasible-flow
in the job” relation. A solution to the bipartite-matching problem problem in Figure 22.36 by adding
maximizes total employment. Figure 22.38 illustrates the bipartite edges from a new source vertex to
graph that models the example problem in Figure 22.3. the supply vertices (each with ca-
It is an instructive exercise to think about finding a direct solution pacity equal to the amount of the
supply) and edges to a new sink
to the bipartite-matching problem, without using the graph model. For vertex from the demand vertices
example, the problem amounts to the following combinatorial puzzle: (each with capacity equal to the
“Find the largest subset of a set of pairs of integers (drawn from disjoint amount of the demand). The net-
sets) with the property that no two pairs have the same integer.” The work in Figure 22.36 has a feasi-
ble flow if and only if this network
example depicted in Figure 22.38 corresponds to solving this puzzle has a flow (a maxflow) that fills all
on the pairs 0-6, 0-7, 0-8, 1-6, and so forth. The problem seems the edges from the sink and all the
straightforward at first, but, as was true of the Hamilton-path problem edges to the source.
420 §22.4 CHAPTER TWENTY-TWO
#include <stdio.h>
#include "GRAPH.h"
6 7 8 9 10 11
main(int argc, char *argv[])
{ Graph G; int i, v, w, E, V = atoi(argv[1]);
G = GRAPHinit(2*V+2); 12
vertices edges
Proof : We can compute the minimum size of any cut that separates
two given vertices by computing the maxflow in the st-network formed
from the graph by assigning unit capacity to each edge. The edge
connectivity is equal to the minimum of these values over all pairs of
vertices.
We do not need to do the computation for all pairs of vertices,
however. Let s* be a vertex of minimal degree in the graph. Note
that the degree of s* can be no greater than 2E/V . Consider any
minimum cut of the graph. By definition, the number of edges in the
cut set is equal to the graph’s edge connectivity. The vertex s* appears
in one of the cut’s vertex sets, and the other set must have some vertex
t, so the size of any minimal cut separating s* and t must be equal
to the graph’s edge connectivity. Therefore, if we solve V-1 maxflow
problems (using s* as the source and each other vertex as the sink),
the minimum flow value found is the edge connectivity of the network.
NETWORK FLOW §22.4 425
22.77 Show that the maxflow problem reduces to the feasible-flow problem—
that the two problems are equivalent.
22.78 Find a feasible flow for the flow network shown in Figure 22.11,
given the additional constraints that 0, 2, and 3 are supply vertices with weight
4, and that 1, 4, and 5 are supply vertices with weights 1, 3, and 5, respectively.
22.79 Modify Program 22.5 to free the memory for the list nodes corre-
sponding to the edges that are added and removed.
◦ 22.80 Write a program that takes as input a sports league’s schedule and
current standings and determines whether a given team is eliminated. Assume
that there are no ties. Hint: Reduce to a feasible-flow problem with one source
node that has a supply value equal to the total number of games remaining to
play in the season, sink nodes that correspond to each pair of teams having
a demand value equal to the number of remaining games between that pair,
and distribution nodes that correspond to each team. Edges should connect
the supply node to each team’s distribution node (of capacity equal to the
number of games that team would have to win to beat X if X were to win all
its remaining games), and there should be an (uncapacitated) edge connecting
each team’s distribution node to each of the demand nodes involving that team.
• 22.81 Prove that the maxflow problem for networks with lower bounds on
edges reduces to the standard maxflow problem.
22.82 Prove that, for networks with lower bounds on edges, the problem
of finding a minimal flow (that respects the bounds) reduces to the maxflow
problem (see Exercise 22.81).
••• 22.83 Prove that the maxflow problem for st-networks reduces to the
maxflow problem for undirected networks, or find a maxflow algorithm for
undirected networks that has a worst-case running time substantially better
than those of the algorithms in Sections 22.2 and 22.3.
22.84 Find all the matchings with five edges for the bipartite graph in
Figure 22.38.
22.85 Extend Program 22.6 to use symbolic names instead of integers to
refer to vertices (see Program 17.10).
◦ 22.86 Prove that the bipartite-matching problem is equivalent to the prob-
lem of finding maxflows in networks where all edges are of unit capacity.
22.87 We might interpret the example in Figure 22.3 as describing student
preferences for jobs and employer preferences for students, the two of which
may not be mutual. Does the reduction described in the text apply to the di-
rected bipartite-matching problem that results from this interpretation, where
edges in the bipartite graph are directed (in either direction) from one set to
the other? Prove that it does or provide a counterexample.
◦ 22.88 Construct a family of bipartite-matching problems where the average
length of the augmenting paths used by any augmenting-path algorithm to
solve the corresponding maxflow problem is proportional to E.
428 §22.4 CHAPTER TWENTY-TWO
22.89 Show, in the style of Figure 22.29, the operation of the FIFO preflow-
push network-flow algorithm on the bipartite-matching network shown in
Figure 22.39.
◦ 22.90 Extend Table 22.3 to include various preflow-push algorithms.
• 22.91 Suppose that the two sets in a bipartite-matching problem are of size
S and T , with S << T . Give as sharp a bound as you can for the worst-case
running time to solve this problem, for the reduction of Property 22.19 and the
maximal-augmenting-path implementation of the Ford–Fulkerson algorithm
(see Property 22.8).
• 22.92 Exercise 22.91 for the FIFO-queue implementation of the preflow-
push algorithm (see Property 22.13).
22.93 Extend Table 22.3 to include implementations that use the all-
augmenting-paths approach described in Exercise 22.39.
•• 22.94 Prove √ that the running time of the method described in Exer-
cise 22.93 is O( V E) for BFS.
◦ 22.95 Do empirical studies to plot the expected number of edges in a max-
imal matching in random bipartite graphs with V + V vertices and E edges,
for a reasonable set of values for V and sufficient values of E to plot a smooth
curve that goes from zero to V .
◦ 22.96 Prove Menger’s theorem (Property 22.20) for undirected graphs.
• 22.97 Prove that the minimum number of vertices whose removal discon-
nects two vertices in a digraph is equal to the maximum number of vertex-
disjoint paths between the two vertices. Hint: Use a vertex-splitting transfor-
mation, similar to the one illustrated in Figure 22.33.
• 22.98 Extend your proof for Exercise 22.97 to apply to undirected graphs.
22.99 Implement the edge-connectivity algorithm described in this section
as an ADT function for the graph ADT of Chapter 17 that returns a given
graph’s connectivity.
22.100 Extend your solution to Exercise 22.99 to put in a user-supplied
array a minimal set of edges that separates the graph. How big an array
should the user allocate?
• 22.101 Develop an algorithm for computing the edge connectivity of di-
graphs (the minimal number of edges whose removal leaves a digraph that is
not strongly connected). Implement your algorithm as an ADT function for
the digraph ADT of Chapter 19.
• 22.102 Develop algorithms based on your solutions to Exercises 22.97
and 22.98 for computing the vertex connectivity of digraphs and undirected
graphs. Implement your algorithms as ADT functions for the digraph ADT
of Chapter 19 and the graph ADT of Chapter 17, respectively (see Exer-
cises 22.99 and 22.100).
NETWORK FLOW §22.5 429
int GRAPHcost(Graph G)
{ int i; link u; int cost = 0;
for (i = 0; i < G->V; i++)
for (u = G->adj[i]; u != NULL; u = u->next)
if ((u->cap > 0) && (u->cost != C))
cost += (u->flow)*(u->cost);
return cost;
}
defined as follows: For each edge u-v in the original, let f be the flow,
c the capacity, and x the cost. If f is positive, include an edge v-u in
the residual with capacity f and cost -x; if f is less than c, include an
edge u-v in the residual with capacity c-f and cost x.
vertex, so the flow remains a maxflow, but they change the network’s
cost by x times the cost of the cycle, which is negative, thereby contra-
dicting the assertion that the cost of the original flow was minimal.
To prove the converse, suppose that we have a maxflow F
with no negative-cost cycles whose cost is not minimal, and con-
sider any mincost maxflow M. By an argument identical to the flow-
decomposition theorem (Property 22.2), we can find at most E directed
cycles such that adding those cycles to the flow F gives the flow M.
But, since F has no negative cycles, this operation cannot lower the
cost of F, a contradiction. In other words, we should be able to con-
vert F to M by augmenting along cycles, but we cannot do so because
we have no negative-cost cycles to use to lower the flow cost.
This property leads immediately to a simple generic algorithm for
solving the mincost-flow problem, called the cycle-canceling algorithm:
Find a maxflow. Augment the flow along any negative-cost
cycle in the residual network, continuing until none remain.
This method brings together machinery that we have developed over
this chapter and the previous one to provide effective algorithms for
solving the wide class of problems that fit into the mincost-flow model.
Like several other generic methods that we have seen, it admits several
different implementations, since the methods for finding the initial
maxflow and for finding the negative-cost cycles are not specified.
Figure 22.42 shows an example mincost-maxflow computation that
uses cycle canceling.
Since we have already developed algorithms for computing a
maxflow and for finding negative cycles, we immediately have the im-
plementation of the cycle-canceling algorithm given in Program 22.8.
We use any maxflow implementation to find the initial maxflow and
the Bellman–Ford algorithm to find negative cycles (Program 21.9).
To these two implementations, we need to add only a loop to augment
flow along the cycles.
We can eliminate the initial maxflow computation in the cycle-
canceling algorithm, by adding a dummy edge from source to sink and
assigning to it a cost that is higher than the cost of any source–sink path
in the network (for example, V C + 1) and a flow that is higher than
the maxflow (for example, higher than the source’s outflow). With
this initial setup, cycle canceling moves as much flow as possible out
of the dummy edge, so the resulting flow is a maxflow. A mincost-flow
434 §22.5 CHAPTER TWENTY-TWO
implementations that admit a bad worst case (in theory) but use sub-
stantially fewer iterations on the problems that arise in practice than
predicted by the worst-case bounds.
The mincost-flow problem represents the most general problem-
solving model that we have yet examined, so it is perhaps surprising
that we can solve it with such a simple implementation. Because of
the importance of the model, numerous other implementations of the
cycle-canceling method and numerous other different methods have
been developed and studied in detail. Program 22.8 is a remarkably
simple and effective starting point, but it suffers from two defects that
can potentially lead to poor performance. First, each time that we
seek a negative cycle, we start from scratch. Can we save intermediate
information during the search for one negative cycle that can help
us find the next? Second, Program 22.8 just takes the first negative
cycle that the Bellman–Ford algorithm finds. Can we direct the search
towards negative cycles with particular properties? In Section 22.6,
we consider an improved implementation, still generic, that represents
a response to both of these questions.
Exercises
22.107 Modify your solution to Exercise 22.13 to check that the source’s
outflow equals the sink’s inflow and that outflow equals inflow at every internal
vertex. Also check that the cost is the same sign as flow and capacity for all
edges and that the cost of u-v and the cost of v-u sum to zero for all u and v.
22.108 Expand your ADT for feasible flows from Exercise 22.75 to include
costs. Include an ADT function for solving the mincost–feasible-flow problem
that uses the standard flow network ADT and calls GRAPHmincost.
22.109 Given a flow network whose edges are not all maximal capacity and
cost, give an upper bound better than ECM on the cost of a maxflow.
22.110 Prove that, if all capacities and costs are integers, then the mincost-
flow problem has a solution where all flow values are integers.
22.111 Modify Program 22.8 to initialize with flow in a dummy edge instead
of computing a flow.
◦ 22.112 Give all possible sequences of augmenting cycles that might have
been depicted in Figure 22.42.
◦ 22.113 Give all possible sequences of augmenting cycles that might have
been depicted in Figure 22.43.
22.114 Show, in the style of Figure 22.42, the flow and residual networks
after each augmentation when you use the cycle-canceling implementation
NETWORK FLOW §22.6 439
of Program 22.8 to find a mincost flow in the flow network shown in Fig-
ure 22.11, with cost 2 assigned to 0-2 and 0-3; cost 3 assigned to 2-5 and 3-5;
cost 4 assigned to 1-4; and cost 1 assigned to all of the other edges. Assume
that the maxflow is computed with the shortest-augmenting-path algorithm.
22.115 Answer Exercise 22.114, but assume that the program is modified to
start with a maxflow in a dummy edge from source to sink, as in Figure 22.43.
22.116 Extend your solutions to Exercises 22.7 and 22.8 to handle costs in
flow networks.
cap flow 0
0-1 3 1
22.117 Extend your solutions to Exercises 22.10 through 22.12 to include 0-2 3 3* 1 2
costs in the networks. Take each edge’s cost to be roughly proportional to the 1-3 2 1
Euclidean distance between the vertices that the edge connects. 1-4 2 0
2-3 1 1*
2-4 2 2* 3 4
3-5 2 2*
22.6 Network Simplex Algorithm 4-5 2 2* 5
holds for φ , so we must also have φ (v) = φ (u) − c(u, v). Figure 22.46
Subtracting, we find that φ(v) − φ (v) = φ(u) − φ (u) for any Spanning tree for dummy
u and v connected by a tree edge. Denoting this difference by Δ for maxflow
any vertex and applying the equality along the edges of any search If we start with flow on a dummy
tree of the spanning tree, immediately gives the desired result that edge from source to sink, then that
φ(u) = φ (u) + Δ for all u. is the only possible partial edge,
so we can use any spanning tree
Another way to imagine the process of defining a set of valid of the remaining nodes to build a
spanning tree for the flow. In the
vertex potentials is that we start by fixing one value, then compute
example, the edges 0-5, 0-1, 0-2,
the values for all vertices connected to that vertex by tree edges, then 1-3, and 1-4 comprise a spanning
compute them for all vertices connected to those vertices, and so forth. tree for the initial maxflow. All of
No matter where we start the process, the potential difference between the nontree edges are empty.
442 §22.6 CHAPTER TWENTY-TWO
The left-hand side of the sum of these equations gives the total cost of
the cycle and the right-hand side collapses to c∗ (u, v). In other words,
the edge’s reduced cost gives the cycle cost, so only the edges described
can give a negative-cost cycle
cost
3-0 3
5 5 5
1-9 3
0-2 2 7 6 7 6 7 6
3-6 2
4-0 3 9 3 9 3 9 3
7-5 2
0 0 0
8-0 2 1 1 1
6-9 2
2 8 2 8 2 8
6-5 2 4 4 4
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
pt 1 4 0 2 pt 1 3 4 0 2 0 pt 1 3 -1 4 0 2 0
Figure 22.48
Computing potentials through potential values. Once we have traveled along a path, we do not revisit
parent links any of its edges, so this process runs in time proportional to V .
We start at 0, follow the path to
Given two nodes in a tree, their least common ancestor (LCA)
the root, set pt[5] to 0, then work
down the path, first setting 6 to is the root of the smallest subtree that contains them both. The cycle
make pt[6] - pt[5] equal to the that we form by adding an edge connecting two nodes consists of that
cost of 6-5, then setting p[3] to edge plus the edges on the two paths from the two nodes to their LCA.
make p[3] - p[6] equal to the
cost of 3-6, and so forth (left).
To augment along a cycle, we do not need to consider edges in order
Then we start at 1 and follow around the cycle; it suffices to consider them all (in either direction).
parent links until hitting a vertex Accordingly, we can augment along the cycle by simply following paths
whose potential is known (6 in this from each of the two nodes to their LCA. To augment along the cycle
case) and work down the path to
compute potentials on 9 and 1 formed by the addition of u-v, we find the LCA of u and v (say, t)
(center). When we start at 2, we and push flow from u to v, from v along the path to t, and from u
can compute its potential from its along the path to t, but in reverse direction for each edge. To compute
parent (right); when we start at 3,
the amount of flow to push, we first traverse the edges on the cycle
we see that its potential is already
known, and so forth. In this exam- in the same manner to determine the maximum amount that can be
ple, when we try each vertex after pushed. Program 22.10 is an implementation of this idea, in the form
1, we either find that its potential of a function that augments a cycle and also returns an edge that is
is already done or we can com-
pute the value from its parent. We
emptied or filled by the augmentation.
never retrace an edge, no matter The implementation in Program 22.10 uses a simple technique
what the tree structure, so the total to avoid paying the cost of initializing all the marks each time that we
running time is linear.
call it. We maintain the marks as global variables, initialized to zero.
Each time that we seek an LCA, we increment a global counter and
mark vertices by setting their corresponding entry in a vertex-indexed
array to that counter. After initialization, this technique allows us
to perform the computation in time proportional to the length of the
cycle. In typical problems, we might augment along a large number of
NETWORK FLOW §22.6 447
5
5 5
7 11
7 11 7 11
13 3
13 3 13 3
12 1 6
12 1 0 6 12 1 0 6
15 2
15 14 8 15 14 8
10 9 4 14
10 2 10 2
0
9 4 9 4
8
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
st 3 13 14 11 2 5 3 5 0 2 15 5 13 11 0 1
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
st 14 13 1 11 2 5 3 5 0 2 15 5 13 11 2 1
Figure 22.49
Spanning tree substitution small cycles, so the time saved can be substantial. As we learn, the same
This example illustrates the basic technique is useful in saving time in other parts of the implementation.
tree-manipulation operation in the Our third tree-manipulation task is to substitute an edge u-v for
network simplex algorithm for the another edge in the cycle that it creates with tree edges. Program 22.11
parent-link representation. At left is
a sample tree with links all point-
is an implementation of a function that accomplishes this task for the
ing upwards, as indicated by the parent-link representation. Again, the LCA of u and v is important,
parent-link structure in the array because the edge to be removed is either on the path from u to the
st. Our code actually maintains LCA or on the path from v to the LCA. Removing an edge detaches
the parent-link values implicitly
through links into the network
all its descendents from the tree, but we can repair the damage by
structure, so tree links can repre- reversing the links between u-v and the removed edge, as illustrated
sent network edges in either ori- in Figure 22.49.
entation (see text). Adding the
These three implementations support the basic operations under-
edge 1-2 creates a cycle with the
paths from 1 and 2 to their LCA, lying the network simplex algorithm: we can choose an eligible edge
11. If we then delete one of those by examining reduced costs and flows; we can use the parent-link rep-
edges, say 0-3, the structure re- resentation of the spanning tree to augment along the negative cycle
mains a tree. To update the parent-
formed with tree edges and the chosen eligible edge; and we can update
link array to reflect the change,
we switch the directions of all the the tree and recalculate potentials. These operations are illustrated for
links from 2 up to 3 (center). The an example flow network in Figures 22.50 and 22.51.
tree at right is the same tree with Figure 22.50 illustrates initialization of the data structures using
node positions changed so that
links all point up, as indicated by
a dummy edge with the maxflow on it, as in Figure 22.43. Shown there
the parent-link array that represents are an initial feasible spanning tree with its parent-link representation,
the tree (bottom right). the corresponding vertex potentials, the reduced costs for the nontree
edges, and the initial set of eligible edges. Also, rather than computing
NETWORK FLOW §22.6 449
the maxflow value in the implementation, we use the outflow from the
source, which is guaranteed to be no less than the maxflow value; we
use the maxflow value here to make the operation of the algorithm
easier to trace.
Figure 22.51 illustrates the changes in the data structures for each
of a sequence of eligible edges and augmentations around negative-cost
cycles. The sequence does not reflect any particular method for choos-
ing eligible edges; it represents the choices that make the augmenting
paths the same as depicted in Figure 22.43. These figures show all
450 §22.6 CHAPTER TWENTY-TWO
should take time proportional to the size of the smaller of the two subtrees
created when the tree edge is deleted.
◦ 22.130 Modify the auxiliary functions in this section to maintain, in addition
to the parent-link array, two other vertex-indexed arrays: one containing each
vertex’s distance to the root, the other containing each vertex’s successor in a
DFS. Your functions to augment along a cycle and substitute an eligible edge
for a tree edge should take time proportional to the length of the augmenting
cycle and your function to compute potentials should take time proportional
to the size of the smaller of the two subtrees created when the tree edge is
deleted.
• 22.131 Explore the idea of maintaining a generalized queue of eligible edges.
Consider various generalized-queue implementations and various improve-
ments to avoid excessive edge-cost calculations, such as restricting attention
to a subset of the eligible edges so as to limit the size of the queue or possibly
allowing some ineligible edges to remain on the queue.
• 22.132 Run empirical studies to determine the number of iterations, the
number of vertex potentials calculated, and the ratio of the running time to E
for several versions of the network simplex algorithm, for various networks
(see Exercises 22.7–12). Consider various algorithms described in the text
and in the previous exercises, and concentrate on those that perform the best
on huge sparse networks.
• 22.133 Write a client program that does dynamic graphical animations of
network simplex algorithms. Your program should produce images like those
in Figure 22.51 and the other figures in this section (see Exercise 22.50). Test
your implementation for the Euclidean networks among Exercises 22.7–12.
on the flow, the cost of any flow in the transformed network is equal
to the cost of the corresponding flow in the original network plus the
sum of the products of the capacities and costs of all the negative-cost
edges (which is a negative quantity). Any minimal-cost flow in the
transformed network is a minimal-cost flow in the original network.
going to the better applicants) and for each applicant to quantify his or
her desire for each company. Then, a solution to the assignment prob-
lem would provide a reasonable way to take these relative preferences
into account.
not all graphs have such a path, we relax the restriction that edges
must appear only once.
Mail carrier Given a network (weighted digraph), find a cyclic
path of minimal weight that includes each edge at least once (see
Figure 22.53). Recall that our basic definitions in Chapter 17 make
the distinction between cyclic paths (which may revisit vertices and
edges) and cycles (which consist of distinct vertices, except the first
and the final, which are the same).
The solution to this problem would describe the best route for a
mail carrier (who has to cover all the streets on her route) to follow.
A solution to this problem might also describe the route that a snow-
plow should take during a snowstorm, and there are many similar
applications.
The mail carrier’s problem is an extension of the Euler tour prob-
lem that we discussed in Section 17.7: The solution to Exercise 17.92
is a simple test for whether a digraph has an Euler tour, and Pro-
gram 17.14 is an effective way to find an Euler tour for such digraphs.
That tour solves the mail carrier’s problem because it includes each
edge exactly once—no path could have lower weight. The problem
becomes more difficult when indegrees and outdegrees are not neces-
sarily equal. In the general case, some edges must be traversed more
than once: The problem is to minimize the total weight of all the
multiply traversed edges.
0-1 4
1-2 5 2
Property 22.33 The mail carrier’s problem reduces to the mincost- 2-3 5 0
a network with positive costs and finding a mincost maxflow of the new
network.
22.145 Implement an ADT function that finds mincost maxflows in networks
with negative costs using GRAPHmincost, which assumes that costs are all
nonnegative.
22.158 The job-placement problem described in the text favors the employers
(their total weights are maximized). Formulate a version of the problem such
that applicants also express their wishes. Explain how to solve your version.
22.159 Do empirical studies to compare the performance of the two network-
simplex implementations in Section 22.6 for solving random instances of the
assignment problem (see Exercise 22.156) with V vertices and E edges, for a
reasonable set of values for V and E.
22.160 The mail carrier’s problem clearly has no solution for networks that
are not strongly connected (the mail carrier can visit only those vertices that
are in the strong component where she starts), but that fact is not mentioned
in the reduction of Property 22.33. What happens when we use the reduction
on a network that is not strongly connected?
22.161 Run empirical studies for various weighted graphs (see Exercises 21.4–
8) to determine average length of the mail carrier’s path.
22.162 Give a direct proof that the single-source shortest-paths problem
reduces to the assignment problem.
22.163 Describe how to formulate an arbitrary assignment problem as an
LP problem.
◦ 22.164 Do Exercise 22.20 for the case where the cost value associated with
each edge is -1 (so you minimize unused space in the trucks).
◦ 22.165 Devise a cost model for Exercise 22.20 such that the solution is a
maxflow that takes a minimal number of days.
22.8 Perspective
Our study of graph algorithms appropriately culminates in the study
of network-flow algorithms for four reasons. First, the network-flow
model validates the practical utility of the graph abstraction in count-
less applications. Second, the maxflow and mincost-flow algorithms
that we have examined are natural extensions of graph algorithms that
we studied for simpler problems. Third, the implementations exem-
plify the important role of fundamental algorithms and data structures
in achieving good performance. Fourth, the maxflow and mincost-flow
models illustrate the utility of the approach of developing increasingly
general problem-solving models and using them to solve broad classes
of problems. Our ability to develop efficient algorithms that solve
these problems leaves the door open for us to develop more general
models and to seek algorithms that solve those problems.
468 §22.8 CHAPTER TWENTY-TWO
475
476
negative weights, 335, 342, 344, disjoint, 11, 111, 423 Existence problem, 69-70
346 down, 93-94, 154, 194-196
and Prim’s algorithm, 281 dummy, 433-437, 441, 457 Feasible flow, 397, 416-419, 430
Directed acyclic graph (DAG), 14, eligible, 398, 442-443, 450-453 Feasible spanning tree, 439-441
142, 178-196 flows, 361 Feedback vertex set, 215
binary, 180-182 incident, 9 Fibonacci computation, 180, 256
defined, 148 parallel, 8, 18, 24, 29, 41, 224, FIFO queue, 115-119, 125-130
detection, 155-157 266 PFS, 288
dominators, 214-215 random, 41 preflow-push, 403, 405-407
kernel, 149-150, 208-212 relaxation, 273-275, 309 topological sort, 188-190
longest paths, 191 separation, 107 Find edge operation, 34
partial orders, 176-177 tree, 93, 107, 109, 154, 194 Flow cost, 429
scheduling problem, 178-179 uncapacitated, 368
sink/source, 187-190 Flow network, 15, 353-471
Edge data type, 17, 32 augmenting-flow method, 370-
strong components, 149 Edge-based preflow-push algo-
topological sorting, 179, 183- 394
rithm, 400 backward/forward edges, 371
193 Edge-connected graph, 106-107,
transitive closure, 193-196 capacities, 358, 361, 373, 412-
111-113, 424-425 413
weighted, 300-307
Edge-separable graph, 106-107 circulation, 363-364, 415
Directed cycle, 14, 147, 155-157
Edmonds, J., 380-381 cut problems, 355-356, 373-374
Directed graph: see Digraph, Di-
Eligible edge, 398, 442-443, 450- defined, 361
rected acyclic graph (DAG)
453 distribution problems, 354, 416-
Directed path, 66, 144, 147, 215
Empirical analysis 417, 430-431, 460
Disjoint paths, 11, 111
augmenting path, 389 equilibrium, 362-363
Distances matrix, 276-277, 346
Distribution network, 430-431 bipartite matching, 420 flows, 358, 361
Distribution problem, 354, 416- connectivity, 138 inflow/outflow, 361-363
417, 430-431, 460 minimum spanning tree, 258 matching problems, 355, 419-
Dominator, 214-215 preflow–push, 406 422, 461, 467-468
Down link, 93-94, 154, 194-196 transitive closure, 172 maxflow-mincut theorem, 372-
Dummy edge, 433-437, 440-441, Enumeration, graph, 142-143 375
457 Equal weights, 220-221 model, 359-360
Dummy vertex, 362-363 Equivalence class, 176 network simplex algorithm,
Dynamic algorithm, 19, 44 Equivalence relation, 175-176 358, 439-459, 464
Dynamic reachability, 213 Equivalent problems, 314 preflow-push method, 396-409
Erdös, P., 136 random, 289-290
Edge, 7-10 Euclidean graph, 10 reductions, 353, 394, 411-426
array of edges, 18-19 MSTs, 261-263 representations, 365-368
back, 93, 107, 109, 154-157, neighbor graphs, 43 residual, 376-377, 398, 403,
194-195 networks, 308-312, 389-390 431-432, 439
backward/forward, 371 Euclidean heuristic, 310-312 st-network, 361, 415-416
connectivity, 424-425 Euler path, 56-61 value, 361
costs, 459-460 directed, 215 see also Maxflow problem,
critical, 385 mail carrier, 463 Mincost flow problem, Net-
cross, 154, 194, 196 two-way Euler tour, 102-103 work
crossing, 229, 373 Even cycle, 67-68, 215 Floyd’s algorithm, 168, 277, 291,
directed, 14 Excess, vertex, 397 295-298
478
Library programming, 323 reductions, 356, 394, 411-426 Kruskal’s algorithm, 231-232,
LIFO stack, 125-127, 130 running time, 420-422 246-251, 256
Linear programming (LP), 319, spanning tree, 440-441 performance, 255-260, 257
329, 351 standard, 411-412 Prim’s algorithm, 231, 235-244,
maxflow, 425-426 undirected networks, 415-416 251, 256-257
mincost flow, 411, 464 Maxflow-mincut theorem, 372- representations, 226-227
multicommodity flow, 468 375, 423-424 weighted-graph ADTs, 222-225
network flow algorithms, 357- Maximal connected subgraph, 11- Minimum-distance point match-
358 12 ing, 355
Linear-time algorithms, 53 Maximum-capacity augmenting Modular arithmetic, 176
Link, 4, 8 path, 381, 386-388, 391-392 Multicommodity flow, 468
back, 93-94 Maximum-cardinality matching, Multigraph, 8
DFS tree, 93-94 419, 467 Multisource paths, 301-304
down, 93-94, 154, 194-196 Maze, 76-80
parent, 91-94, 157 Menger’s theorem, 112-113, 423 Negative cost, 432-433
Locality property, 42 Merchandise distribution, 354, Negative cycle, 267-268, 333-336,
Longest paths, 69, 191, 302-306 416-417, 430-431, 460 432-440
augmenting paths, 382-383 Mincost flow problem, 358, 429- Negative weight, 331-351
difference constraints, 320-321 464 arbitrage, 334-335
and shortest paths, 316 assignment problem, 461-462 Bellman-Ford algorithm, 338-
cycle-canceling algorithm, 358, 342, 346
Mail carrier problem, 68, 215, 433-438, 455 Dijkstra’s algorithm, 335, 342,
462-464 edge costs, 459-460 344, 346
Map, 4, 146, 312 eligible edges, 442-443 Floyd’s algorithm, 336-337,
Matching, 5, 67 feasible flow, 430-431, 458 341-342
bipartite, 68, 419-422, 461 flow cost, 429 Neighbor graph, 43, 136
maximum, 467-468 LP formulation, 411, 464 Network, 5, 15, 265-271
maximum-cardinality, 419, 467 mail carrier, 462-464 acyclic, 300-307, 320-321, 413-
minimum-distance point match- and maxflow problem, 411, 415
ing, 355 457-458 adjacency representations, 32
Mathematical programming, 329 mincost maxflow, 430-431 communications, 354-355
Matrix, adjacency, 21-26 reductions, 457-464 distribution, 430-431
Maxflow problem, 358, 361-364, running time, 437 reliability, 356
370-426 single-source shortest paths, 458 residual, 376-377, 398, 403,
acyclic networks, 413-415 transportation, 460-462 431-432, 439
augmenting-path method, 370- see also Network simplex algo- reweighting, 310, 343-346
394 rithm st-network, 361, 415-416
bipartite matching, 419-422 Mincut, 373-375 telephone, 356
capacity constraints, 412-413 Minimum spanning tree (MST), undirected, 415-416
connectivity, 423-425 66, 219-263 weighted diameter, 291
feasible flow, 416-418 Boruvka’s algorithm, 232-233, see also Flow network, Shortest
general networks, 412 251-255 paths
linear programming, 425-426 cut and cycle properties, 228- Network simplex algorithm, 358,
and mincost flow problem, 411, 233 439-455
457-458 defined, 220 assignment problem, 462
mincost maxflow, 430-431 equal weights, 220-221 eligible edges, 442-443, 450-453
preflow-push method, 396-409 Euclidean, 261-263 feasible spanning tree, 439-441
480
implementations, 452, 454 see also Longest paths, Shortest highest-vertex, 408
initialization, 450 paths performance, 406-408
parent-link representation, 444- Paths matrix, 276-277, 346 vertex-based, 402
449 Performance, 6, 71-73 Preorder numbering, 91-92, 95,
performance, 453-455 abstraction, 36 154
shortest paths, 458-459 adjacency-lists representation, Preprocessing, 101, 120, 208-209
simplex method, 464 29-30, 32-33, 136-139 shortest paths, 269-270, 291-
vertex potentials, 440-441, 454 adjacency-matrix representation, 292
see also Mincost flow problem 25-26, 32-33, 136, 138 Prim’s algorithm, 231, 235-244,
Node, 8 array-of-edges, 32-33 251
Nonlinear cost, 468 augmenting-path method, 379, and Dijkstra’s algorithm, 281
NP-hard problem, 69, 71, 325- 381-394, 421-422 running time, 256-257
326 cycle canceling, 438 Priority queue, 130, 256
dense/sparse graphs, 13 augmenting-path method, 376-
DFS forests, 96-97 379
Online algorithm, 19
Dijkstra’s algorithm, 287-288, Kruskal’s algorithm, 249-251
Operations research (OR), 329,
311-312 multiway heap, 259
352
equivalent problems, 317 Priority-first search (PFS), 128,
Optimization, 70 309
Kruskal’s algorithm, 248-249
or operation, 161-162 augmenting-path method, 376-
MST algorithms, 255-260
Ordered pair, 14, 175 379
network simplex algorithm,
Outdegree, 14, 146-147 BFS, 241, 288
453-455
Outflow, 361 Dijkstra’s algorithm, 283-288
path search, 55-56, 58-59
PFS, 243-244, 285-287 DFS, 241
Parallel edges, 8, 18, 24 preflow-push, 406-408 Prim’s algorithm, 239-244
adjacency-lists representation, preprocessing time, 101, 120, Shortest paths in Euclidean net-
29 208-209 works, 309-310
networks, 266 shortest-paths algorithms, 350- Program structure, 5
random graphs, 41 351 Pushdown stack, 115
weighted graphs, 224 static graphs, 35
Parent-link representation transitive closure, 171-172, 213 Quicksort, 248, 250, 256
cycle detection, 157 union-find algorithm, 139
DFS tree, 91-94 worst-case, 37-38 Radar tracking system, 355
network simplex algorithm, see also Running time Radix sort, 248, 256
444-449 PERT chart, 142 Random flow network, 389-390
Partial order, 176-177 Planar graph, 9, 67 Random graph, 41-43, 134-137
Passage, 76 Planarity problem, 66-67 Randomized queue, 130, 388
Path, 10-11, 50-62 + operator, 279 Reachability, 144, 150, 158-159
cyclic, 10, 148, 462-463 Polynomial-time reduction, 425 DAGs, 193-196
directed, 66, 144, 147, 215 Postorder numbering, 95, 154, digraphs, 197
disjoint, 11, 111 185-186 dynamic, 213
Euler, 56-61, 102-103 Potential function, 405, 440-441, see also Transitive closure
Hamilton, 53-55, 215, 325-326 454 Reduced cost, 440-443
mail carrier, 463 Precedence constraint, 179, 318 Reduction, 169, 271, 469-470
relaxation, 274, 276-277 Preflow, 397 difference constraints, 319-322,
simple, 51-53, 55, 100, 267-268 Preflow-push method, 396-409 324
weight, 265 edge-based, 400 equivalent problems, 314
INDEX 481
flow networks, 353 see also Performance Simple connectivity, 65-66, 100-
implications, 327 102
job scheduling, 318-324 Scheduling problem, 4, 142, 468 Simple graph, 8
linear programming, 319, 329 DAGs, 178-179 Simple path, 10, 51-53, 55
maxflow, 356, 394, 411-426 precedence-constrained, 318- DFS, 100
mincost flow, 457-464 324 networks, 267-268
polynomial-time, 425 see also Topological sorting Simplex method, 464
shortest paths, 314-329 Search: see Graph search Single-source longest paths, 320-
transitive, 215 Selection, 317-318 321
upper/lower bounds, 328-329 Self-loop, 8, 18, 24 Single-source shortest paths, 66,
Reflexive relation, 175 120, 269
digraphs, 144
Relation, 175 acyclic networks, 301-302
networks, 266
Relaxation, 273-279, 309 Dijkstra’s algorithm, 280-281
relations, 175
Remove edge operation, 29, 34 and mincost flow problem, 458
Sentinel weight, 223, 266, 275
Renyi, A., 136 negative weights, 336-338
Separability, 106-111
Residual network, 376-377, 439 reachability, 158-159
Separation vertex, 110-112
mincost flow, 431-432 Sink, 146, 187-188, 269, 361
Set-inclusion DAG, 176
preflow-push, 398, 403 Sollin, G., 255
Shortest paths, 168, 265-352 Sorting, 317-318
Reverse topological sorting, 185-
186 acyclic networks, 300-307 Source vertex, 14, 146, 187-188,
Reweighting, 310, 343-346 augmenting paths, 380, 384- 269, 361
Road map, 270 386, 391-393 Source-sink shortest path, 269,
Running time, 134 Bellman-Ford algorithm, 338- 281, 308-312
augmenting-path method, 379, 342, 346 Spanning forest, 12
390 BFS, 114-115 Spanning tree, 12, 66, 103
Bellman-Ford algorithm, 341- defined, 266-268 feasible, 439-441
342 Dijkstra’s algorithm, 280-288, maxflow, 440-441
Boruvka’s algorithm, 253 292-294, 335, 342-346 network simplex algorithm,
DFS, 88-89 Euclidean networks, 308-312 443-449
digraphs, 213-214 Floyd’s algorithm, 291, 295- see also Minimum spanning tree
Dijkstra’s algorithm, 283, 286, 298, 336-337, 341-342 Sparse graph, 13, 25-26
298 and longest paths, 316 Stack, LIFO, 125-127, 130
Floyd’s algorithm, 296-298 multisource, 301-304 Stack-based augmenting-path
Kruskal’s algorithm, 248-249 negative weights, 331-347 method, 386, 388
maxflow, 420-422 network simplex algorithm, Static graph, 18-19, 35, 44
mincost flow, 437 458-459 st-connectivity, 113
MST algorithms, 257-258 NP-hard problems, 325-326 st-cut, 373-375, 414
NP-hard problems, 325 performance, 287, 350-351 st-network, 361, 415-416
path search, 55 reduction, 314-329 Strong components, 149-150, 197-
preflow-push, 405 relaxation, 273-279 206
Prim’s algorithm, 256-257 shortest paths tree (SPT), 267, transitive closure, 209-210
random graph connectivity, 136 274-275, 281 Strong connectivity, 66, 148-150,
shortest paths algorithms, 287 source-sink, 269, 281, 308-312 197
strong components in digraphs, terminology, 271, 300 Subgraph, 9
197-198 see also All-pairs shortest paths, forbidden, 67
transitive closure, 164-166, 169, Network, Single-source short- maximal connected, 11-12
172, 213-214 est paths Subset inclusion, 176
482
Supply lines problem, 356, 373- Transportation problem, 354, 460- dummy, 362-363
374 462 excess, 397
Symbol table Traveling salesperson problem, 70 fringe, 241
find edge, 34 Traversal function, 19 height, 398-402
graph-building function, 44-45 Tree, 12, 66 indegree/outdegree, 14
indexing function, 46 BFS, 118-122 inflow/outflow, 361
Symmetric relation, 175 binary, 180-182 marking, 86
and DAGs, 180 ordered pair, 14
Tarjan, R. E., 396 DFS, 84, 91-97, 103, 107-110, potentials, 405, 440-441, 454
Tarjan’s algorithm 153-154 reachable, 144, 150
planarity, 67, edge, 93, 107, 109, 154, 194 removing, 34-35
strong connectivity, 198, 202- preorder tree traversal, 95 search, 103
204 shortest-paths tree (SPT), 267, separation, 110-112
Telephone network, 356 274-275, 281 sink/source, 14, 146, 187-188,
Topological sorting, 142, 179, spanning, 103, 439-441, 443- 269, 361
183-191 449 Vertex-based preflow-push algo-
DFS, 185-186, 193-196 see also Minimum spanning tree rithm, 402
multisource shortest-paths, 301- (MST) Vertex-indexed array, 32, 90
304 Tree link, 93-94
relabeling/rearrangement, 183- Trémaux exploration, 77-80, 82- Warshall’s algorithm, 164-167,
184 84, 102-103 277, 295-296
reverse, 185-187 Two-coloring, 103-105 Weighted diameter, 291
Total order, 177 Two-way Euler tour, 61, 102-103 Weighted graph, 15, 219-221
Tour, 10 adjacency-matrix representation,
Euler, 56-61, 102-103 Uncapacitated edge, 368 32
Hamilton, 53-55 Undirected graph, 14-15, 31 ADTs, 222-225
mail carrier problem, 68 and digraphs, 145, 148-154, arbitrary weights, 220
traveling salesperson problem, 157-159, 171-172 bipartite matching, 68
70 networks, 415-416 digraphs, 265-266
Tractable problem, 66 reachability, 197 equal weights, 220-221
Traffic flow, 355 underlying, 15 integer weights, 367
Transaction, 5 Uniconnected digraph, 215 negative weights, 331-351
Transaction graph, 43-44 Union, 12 path weight, 265
Transitive closure, 66, 161-172 Union-find algorithm, 19, 37 reweighting, 310, 343-346
abstract, 166-167, 208-212 in Boruvka’s algorithm, 252-253 sentinel weight, 223, 266
and all-pairs shortest paths, and DFS, 101-102 see also Minimum spanning tree
167, 315 in Kruskal’s algorithm, 251 (MST), Shortest paths
Boolean matrix multiplication, performance, 139 Whitney’s theorem, 112
161-164, 168-169 World Wide Web, 4
DAGs, 193-196 Vertex, 7-10 Worst-case performance, 37-38
DFS-based, 169-171 active, 397
performance, 164-166, 169, adjacent, 9
171-172, 213-214 connectivity, 112, 424
of a relation, 175 cut, 110-112
Warshall’s algorithm, 164-167 degree of, 9
Transitive reduction, 215 destination, 14
Transitive relation, 175 disjoint, 11