Data Structures and Algorithms
Data Structures and Algorithms
Bruno R. Preiss
B.A.Sc., M.A.Sc., Ph.D., P.Eng.
Associate Professor
Department of Electrical and Computer Engineering
University of Waterloo, Waterloo, Canada
Contents
Preface
Chapter 0 Introduction
A Simple Example
About Polynomials Again
o More Notation-Theta and Little Oh
o Asymptotic Analysis of Algorithms
Rules For Big Oh Analysis of Running Time
Example-Prefix Sums
Example-Fibonacci Numbers
Example-Bucket Sort
Reality Check
Checking Your Analysis
o Exercises
o Projects
Foundational Data Structures
o Dynamic Arrays
Default Constructor
Array Constructor
Copy Constructor
Destructor
Array Member Functions
Array Subscripting Operator
Resizing an Array
o Singly-Linked Lists
An Implementation
List Elements
Default Constructor
Destructor and Purge Member Function
Accessors
Iterators
The NullIterator Class
Direct vs. Indirect Containment
Ownership of Contained Objects
Associations
Implementation
Searchable Containers
o Exercises
o Projects
Stacks, Queues and Deques
o Stacks
Array Implementation
Member Variables
Constructor and Destructor
Push, Pop, and Top Member Functions
The Accept Member Function
Iterator
Linked List Implementation
Member Variables
Constructor and Destructor
Push, Pop, and Top Member Functions
The Accept Member Function
Iterator
Applications
Evaluating Postfix Expressions
Implementation
o Queues
Array Implementation
Member Variables
Constructor and Destructor
Head, Enqueue, and Dequeue Member Functions
Linked List Implementation
Member Variables
Constructor and Destructor
Head, Enqueue and Dequeue Member Functions
Applications
Implementation
o Deques
Array Implementation
Tail, EnqueueHead, and DequeueTail Member Functions
Linked List Implementation
Tail, EnqueueHead, and DequeueTail Member Functions
Doubly-Linked and Circular Lists
o Exercises
o Projects
Ordered Lists and Sorted Lists
o Ordered Lists
Array Implementation
Member Variables
Inserting and Accessing Items in a List
Finding Items in a List
Removing Items from a List
Implementation
Analysis
o Exercises
o Projects
Hashing, Hash Tables and Scatter Tables
o Hashing-The Basic Idea
Example
Keys and Hash Functions
Avoiding Collisions
Spreading Keys Evenly
Ease of Computation
o Hashing Methods
Division Method
Middle Square Method
Multiplication Method
Fibonacci Hashing
o Hash Function Implementations
Integral Keys
Floating-Point Keys
Character String Keys
Hashing Objects
Hashing Containers
Using Associations
o Hash Tables
Separate Chaining
Implementation
Constructor and Destructor
Terminology
More Terminology
Alternate Representations for Trees
o N-ary Trees
o Binary Trees
o Tree Traversals
Preorder Traversal
Postorder Traversal
Inorder Traversal
Breadth-First Traversal
o Expression Trees
Infix Notation
Prefix Notation
Postfix Notation
o Implementing Trees
Tree Traversals
Depth-First Traversal
Preorder, Inorder and Postorder Traversals
Breadth-First Traversal
Accept Member Function
Tree Iterators
Member Variables
Constructor and Reset Member Function
Operator Member Functions
General Trees
Member Variables
Member Functions
Constructor, Destructor, and Purge Member Function
Key and Subtree Member Functions
AttachSubtree and DetachSubtree Member Functions
N-ary Trees
Member Variables
Member Functions
Constructors
IsEmpty Member Function
Key, AttachKey and DetachKey Member Functions
Subtree, AttachSubtree and DetachSubtree Member
Functions
Binary Trees
Member Variables
Constructors
Destructor and Purge Member Functions
Binary Tree Traversals
Comparing Trees
Applications
Implementation
o Exercises
o Projects
Search Trees
o Basics
M-Way Search Trees
Binary Search Trees
o Searching a Search Tree
o Applications
Discrete Event Simulation
Implementation
o Exercises
o Projects
Sets, Multisets and Partitions
o Basics
Implementing Sets
o Array and Bit-Vector Sets
Basic Operations
Union, Intersection and Difference
Comparing Sets
Bit-Vector Sets
Basic Operations
Union, Intersection and Difference
o Multisets
Array Implementation
Basic Operations
Union, Intersection and Difference
Linked List Implementation
Union
Intersection
o Partitions
Representing Partitions
Implementing a Partition using a Forest
Implementation
Constructors and Destructor
Releasing an Area
o Applications
Implementation
o Exercises
o Projects
Algorithmic Patterns and Problem Solvers
o Brute-Force and Greedy Algorithms
Example-Counting Change
Brute-Force Algorithm
Greedy Algorithm
Example-0/1 Knapsack Problem
o Backtracking Algorithms
Example-Balancing Scales
Representing the Solution Space
Abstract Backtracking Solvers
Depth-First Solver
Breadth-First Solver
Branch-and-Bound Solvers
Depth-First, Branch-and-Bound Solver
Example-0/1 Knapsack Problem Again
o Top-Down Algorithms: Divide-and-Conquer
Example-Binary Search
Example-Computing Fibonacci Numbers
Example-Merge Sorting
Running Time of Divide-and-Conquer Algorithms
Case 1 (
Case 2 (
Case 3 (
Summary
Example-Matrix Multiplication
o Bottom-Up Algorithms: Dynamic
Programming
Example-Generalized Fibonacci Numbers
Example-Computing Binomial Coefficients
Application: Typesetting Problem
Example
Implementation
o Randomized Algorithms
Generating Random Numbers
The Minimal Standard Random Number Generator
Implementation
Random Variables
Implementation
Monte Carlo Methods
Example-Computing
Simulated Annealing
Example-Balancing Scales
o Exercises
o Projects
Sorting Algorithms and Sorters
o Basics
o Sorting and Sorters
Sorter Class Hierarchy
o Insertion Sorting
Straight Insertion Sort
Implementation
Average Running Time
Binary Insertion Sort
o Exchange Sorting
Bubble Sort
Quicksort
Implementation
Running Time Analysis
Worst-Case Running Time
Best-Case Running Time
Average Running Time
Selecting the Pivot
o Selection Sorting
Straight Selection Sorting
Implementation
Sorting with a Heap
Implementation
Building the Heap
Running Time Analysis
The Sorting Phase
o Merge Sorting
Implementation
Merging
Two-Way Merge Sorting
Running Time Analysis
Iterators
Graph Traversals
Implementing Undirected Graphs
Using Adjacency Matrices
Using Adjacency Lists
Edge-Weighted and Vertex-Weighted Graphs
Comparison of Graph Representations
Space Comparison
Time Comparison
o Graph Traversals
Depth-First Traversal
Implementation
Running Time Analysis
Breadth-First Traversal
Implementation
Running Time Analysis
Topological Sort
Implementation
Running Time Analysis
Graph Traversal Applications:
Testing for Cycles and Connectedness
Connectedness of an Undirected Graph
Connectedness of a Directed Graph
Testing for Cycles in a Directed Graph
o Shortest-Path Algorithms
Single-Source Shortest Path
Dijkstra's Algorithm
Pass By Reference
The Trade-off
Constant Parameters
o Objects and Classes
Member Variables and Member Functions
Constructors and Destructors
Default Constructor
Copy Constructor
The Copy Constructor, Parameter Passing and Function Return
Values
Destructors
Accessors and Mutators
Mutators
Member Access Control
o Inheritance and Polymorphism
Derivation and Inheritance
Derivation and Access Control
Polymorphism
Virtual Member Functions
Abstract Classes and Concrete Classes
Algorithmic Abstraction
Multiple Inheritance
Run-Time Type Information and Casts
o Templates
o Exceptions
Class Hierarchy Diagrams
Character Codes
References
Index
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 objectoriented 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 object-oriented 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
C++ 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.
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 welldesigned program artifacts so that they can learn about the designing
good software.
Consequently, this book presents the various data structures and
algorithms as complete C++ program fragments. All the program
fragments presented in this book have been extracted automatically from
the source code files of working and tested programs.
The full functionality of the proposed draft ANSI standard C++ language
is used in the examples--including templates, exceptions and run-time
type information[3]. 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[38]. 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 develops several models and illustrates with
, this chapter