Data Structures and Algorithms With Object-Oriented Design Patterns in Python
Data Structures and Algorithms With Object-Oriented Design Patterns in Python
Bruno R. Preiss
B.A.Sc., M.A.Sc., Ph.D., P.Eng.
MMIV
To Patty
Contents
Preface
xi
1 Introduction
1.1 What This Book Is About . . . . . . . . . .
1.2 Object-Oriented Design . . . . . . . . . . .
1.3 Object Hierarchies and Design Patterns . .
1.4 The Features of Python You Need to Know
1.5 How This Book Is Organized . . . . . . . .
2 Algorithm Analysis
2.1 A Detailed Model of the Computer .
2.2 A Simplified Model of the Computer
Exercises . . . . . . . . . . . . . . . . . .
Programming Projects . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
1
2
3
5
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7
8
21
29
32
3 Asymptotic Notation
3.1 An Asymptotic Upper BoundBig Oh
3.2 An Asymptotic Lower BoundOmega
3.3 More NotationTheta and Little Oh .
3.4 Asymptotic Analysis of Algorithms . .
Exercises . . . . . . . . . . . . . . . . . . .
Programming Projects . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
33
33
43
46
46
58
60
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
63
63
69
75
83
84
.
.
.
.
87
87
88
104
105
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
vii
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Contents
viii
6 Stacks, Queues, and
6.1 Stacks . . . . . .
6.2 Queues . . . . . .
6.3 Deques . . . . . .
Exercises . . . . . . .
Programming Projects
Deques
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
107
107
118
126
133
134
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
137
137
158
169
170
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
173
173
176
180
188
193
201
212
214
216
9 Trees
9.1 Basics . . . . . . . .
9.2 N -ary Trees . . . . .
9.3 Binary Trees . . . .
9.4 Tree Traversals . . .
9.5 Expression Trees . .
9.6 Implementing Trees
Exercises . . . . . . . . .
Programming Projects . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
219
220
223
226
227
229
231
254
256
10 Search Trees
10.1 Basics . . . . . . . . . . . .
10.2 Searching a Search Tree . .
10.3 Average Case Analysis . . .
10.4 Implementing Search Trees
10.5 AVL Search Trees . . . . . .
10.6 M -Way Search Trees . . . .
10.7 B-Trees . . . . . . . . . . .
10.8 Applications . . . . . . . . .
Exercises . . . . . . . . . . . . .
Programming Projects . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
257
257
259
260
266
271
283
290
299
300
303
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Contents
11 Heaps and Priority Queues
11.1 Basics . . . . . . . . . . .
11.2 Binary Heaps . . . . . . .
11.3 Leftist Heaps . . . . . . .
11.4 Binomial Queues . . . . .
11.5 Applications . . . . . . . .
Exercises . . . . . . . . . . . .
Programming Projects . . . . .
ix
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
305
305
307
316
323
336
339
341
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
343
344
345
351
357
368
369
371
13 Garbage Collection
13.1 What is Garbage? . . . . . . . . . . . .
13.2 Reference Counting Garbage Collection
13.3 Mark-and-Sweep Garbage Collection . .
13.4 Stop-and-Copy Garbage Collection . . .
13.5 Mark-and-Compact Garbage Collection
Exercises . . . . . . . . . . . . . . . . . . . .
Programming Projects . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
373
374
376
380
382
384
387
388
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
391
391
394
403
411
418
427
429
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
431
431
432
434
438
449
457
461
462
468
469
472
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Contents
x
16 Graphs and Graph Algorithms
16.1 Basics . . . . . . . . . . . . . . . .
16.2 Implementing Graphs . . . . . . .
16.3 Graph Traversals . . . . . . . . . .
16.4 Shortest-Path Algorithms . . . . .
16.5 Minimum-Cost Spanning Trees . .
16.6 Application: Critical Path Analysis
Exercises . . . . . . . . . . . . . . . . .
Programming Projects . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
475
476
483
495
507
515
523
526
530
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
533
533
533
535
535
541
541
547
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
549
C Character Codes
551
Index
557
Preface
This book was motivated by my experience in teaching the course E&CE 250: Algorithms and Data Structures in the Computer Engineering program at the University
of Waterloo. I have observed that the advent of object-oriented methods and the
emergence of object-oriented design patterns has lead to a profound change in the
pedagogy of data structures and algorithms. The successful application of these
techniques gives rise to a kind of cognitive unification: Ideas that are disparate and
apparently unrelated seem to come together when the appropriate design patterns
and abstractions are used.
This paradigm shift is both evolutionary and revolutionary. On the one hand,
the knowledge base grows incrementally as programmers and researchers invent
new algorithms and data structures. On the other hand, the proper use of objectoriented techniques requires a fundamental change in the way the programs are
designed and implemented. Programmers who are well schooled in the procedural
ways often find the leap to objects to be a difficult one.
Goals
The primary goal of this book is to promote object-oriented design using Python and
to illustrate the use of the emerging object-oriented design patterns. Experienced
object-oriented programmers find that certain ways of doing things work best and
that these ways occur over and over again. The book shows how these patterns are
used to create good software designs. In particular, the following design patterns
are used throughout the text: singleton, container, iterator, adapter and visitor.
Virtually all of the data structures are presented in the context of a single,
unified, polymorphic class hierarchy. This framework clearly shows the relationships
between data structures and it illustrates how polymorphism and inheritance can
be used effectively. In addition, algorithmic abstraction is used extensively when
presenting classes of algorithms. By using algorithmic abstraction, it is possible
to describe a generic algorithm without having to worry about the details of a
particular concrete realization of that algorithm.
A secondary goal of the book is to present mathematical tools just in time.
Analysis techniques and proofs are presented as needed and in the proper context.
In the past when the topics in this book were taught at the graduate level, an author
could rely on students having the needed background in mathematics. However,
because the book is targeted for second- and third-year students, it is necessary
to fill in the background as needed. To the extent possible without compromising
correctness, the presentation fosters intuitive understanding of the concepts rather
than mathematical rigor.
xi
Preface
xii
Approach
One cannot learn to program just by reading a book. It is a skill that must be
developed by practice. Nevertheless, the best practitioners study the works of others
and incorporate their observations into their own practice. I firmly believe that after
learning the rudiments of program writing, students should be exposed to examples
of complex, yet well-designed program artifacts so that they can learn about the
designing good software.
Consequently, this book presents the various data structures and algorithms
as complete Python program fragments. All the program fragments presented in
this book have been extracted automatically from the source code files of working
and tested programs. It has been my experience that by developing the proper
abstractions, it is possible to present the concepts as fully functional programs
without resorting to pseudo-code or to hand-waving.
Outline
This book presents material identified in the Computing Curricula 1991 report of
the ACM/IEEE-CS Joint Curriculum Task Force[47]. The book specifically addresses the following knowledge units: AL1: Basic Data structures, AL2: Abstract
Data Types, AL3: Recursive Algorithms, AL4: Complexity Analysis, AL6: Sorting
and Searching, and AL8: Problem-Solving Strategies. The breadth and depth of
coverage is typical of what should appear in the second or third year of an undergraduate program in computer science/computer engineering.
In order to analyze a program, it is necessary to develop a model of the computer.
Chapter 2 develops several models and illustrates with examples how these models
predict performance. Both average-case and worst-case analyses of running time
are considered. Recursive algorithms are discussed and it is shown how to solve a
recurrence using repeated substitution. This chapter also reviews arithmetic and
geometric series summations, Horners rule and the properties of harmonic numbers.
Chapter 3 introduces asymptotic (big-oh) notation and shows by comparing
with Chapter 2 that the results of asymptotic analysis are consistent with models
of higher fidelity. In addition to O(), this chapter also covers other asymptotic notations ((), (), and o()) and develops the asymptotic properties of polynomials
and logarithms.
Chapter 4 introduces the foundational data structuresthe array and the linked
list. Virtually all the data structures in the rest of the book can be implemented
using either one of these foundational structures. This chapter also covers multidimensional arrays and matrices.
Chapter 5 deals with abstraction and data types. It presents the recurring
design patterns used throughout the text as well a unifying framework for the
data structures presented in the subsequent chapters. In particular, all of the data
structures are viewed as abstract containers.
Chapter 6 discusses stacks, queues, and deques. This chapter presents implementations based on both foundational data structures (arrays and linked lists).
Applications for stacks and queues are presented.
Chapter 7 covers ordered lists, both sorted and unsorted. In this chapter, a list
is viewed as a searchable container. Again several applications of lists are presented.
Chapter 8 introduces hashing and the notion of a hash table. This chapter
addresses the design of hashing functions for the various basic data types as well as
Preface
for the abstract data types described in Chapter 5. Both scatter tables and hash
tables are covered in depth and analytical performance results are derived.
Chapter 9 introduces trees and describes their many forms. Both depth-first and
breadth-first tree traversals are presented. Completely generic traversal algorithms
based on the use of the visitor design pattern are presented, thereby illustrating the
power of algorithmic abstraction. This chapter also shows how trees are used to represent mathematical expressions and illustrates the relationships between traversals
and the various expression notations (prefix, infix, and postfix).
Chapter 10 addresses trees as searchable containers. Again, the power of algorithmic abstraction is demonstrated by showing the relationships between simple
algorithms and balancing algorithms. This chapter also presents average case performance analyses and illustrates the solution of recurrences by telescoping.
Chapter 11 presents several priority queue implementations, including binary
heaps, leftist heaps, and binomial queues. In particular this chapter illustrates how
a more complicated data structure (leftist heap) extends an existing one (tree).
Discrete-event simulation is presented as an application of priority queues.
Chapter 12 covers sets and multisets. Also covered are partitions and disjoint
set algorithms. The latter topic illustrates again the use of algorithmic abstraction.
Garbage collection is discussed in Chapter 13. This is a topic that is not found
often in texts of this sort. However, because the Python language relies on garbage
collection, it is important to understand how it works and how it affects the running
times of programs.
Chapter 14 surveys a number of algorithm design techniques. Included are
brute-force and greedy algorithms, backtracking algorithms (including branch-andbound), divide-and-conquer algorithms, and dynamic programming. An objectoriented approach based on the notion of an abstract solution space and an abstract
solver unifies much of the discussion. This chapter also covers briefly random number generators, Monte Carlo methods, and simulated annealing.
Chapter 15 covers the major sorting algorithms in an object-oriented style based
on the notion of an abstract sorter. Using the abstract sorter illustrates the relationships between the various classes of sorting algorithm and demonstrates the use
of algorithmic abstractions.
Finally, Chapter 16 presents an overview of graphs and graph algorithms. Both
depth-first and breadth-first graph traversals are presented. Topological sort is
viewed as yet another special kind of traversal. Generic traversal algorithms based
on the visitor design pattern are presented, once more illustrating algorithmic abstraction. This chapter also covers various shortest path algorithms and minimumspanning-tree algorithms.
At the end of each chapter is a set of exercises and a set of programming projects.
The exercises are designed to consolidate the concepts presented in the text. The
programming projects generally require the student to extend the implementation
given in the text.
xiii
Preface
xiv
Acknowledgments
Insert acknowledgements here.
Waterloo, Canada
September 28, 2003