Search for notes by fellow students, in your own course and all over the country.

Browse our notes for titles which look like what you need, you can preview any of the notes via a sample of the contents. After you're happy these are the notes you're after simply pop them into your shopping cart.

My Basket

You have nothing in your shopping cart yet.

Title: Data structures C++
Description: complete notes of data structure using C++by Malik. Array, linked-list, stack, queue, binary search tree, sorting algorithm, graph, standard template library. for beginner all the way to professional level with detail explanation and concept and example.

Document Preview

Extracts from the notes are below, to see the PDF you'll receive please use the links above


DATA STRUCTURES
USING C++
SECOND EDITION

D
...
MALIK

Australia  Brazil  Japan  Korea  Mexico  Singapore  Spain  United Kingdom  United States

Data Structures Using C++, Second Edition
D
...
Malik
Executive Editor: Marie Lee
Acquisitions Editor: Amy Jollymore
Senior Product Manager: Alyssa Pratt
Editorial Assistant: Zina Kresin
Marketing Manager: Bryant Chrzan

ª 2010 Course Technology, Cengage Learning
ALL RIGHTS RESERVED
...


Content Project Manager: Heather Furrow
Art Director: Faith Brosnan
Image credit: ª Fancy Photography/Veer

(Royalty Free)
Cover Designer: Roycroft Design
Compositor: Integra

For product information and technology assistance, contact us at
Cengage Learning Customer & Sales Support, 1-800-354-9706
For permission to use material from this text or product, submit
all requests online at cengage
...
com
ISBN-13: 978-0-324-78201-1
ISBN-10: 0-324-78201-2
Course Technology
20 Channel Center Street
Boston, MA 02210
USA
Cengage Learning is a leading provider of customized learning solutions
with office locations around the globe, including Singapore, the United
Kingdom, Australia, Mexico, Brazil, and Japan
...
cengage
...

For your lifelong learning solutions, visit
www
...
com/coursetechnology
Purchase any of our products at your local college store or at our
preferred online store www
...
com
Some of the product names and company names used in this book
have been used for identification purposes only and may be
trademarks or registered trademarks of their respective
manufacturers and sellers
...
At
the time this book was printed, any such data was fictional and not
belonging to any real persons or companies
...

The programs in this book are for instructional purposes only
...
The author and the
publisher do not offer any warranties or representations, nor do they
accept any liabilities with respect to the programs
...
Software Engineering Principles and C++ Classes
2
...
Pointers and Array-Based Lists

131

4
...
Linked Lists

265

6
...
Stacks

395

8
...
Searching and Hashing Algorithms

497

10
...
Binary Trees and B-Trees

599

12
...
Standard Template Library (STL) II

731

APPENDIX A

Reserved Words

807

APPENDIX B

Operator Precedence

809

APPENDIX C

Character Sets

811

APPENDIX D

Operator Overloading

815

APPENDIX E

Header Files

817

vi |

Data Structures Using C++, Second Edition

APPENDIX F

Additional C++ Topics

825

APPENDIX G

C++ for Java Programmers

833

APPENDIX H

References

857

APPENDIX I

INDEX

Answers to Odd-Numbered
Exercises

859
879

TABLE OF C ONTENTS

Preface

1

xxiii

SOFTWARE ENGINEERING PRINCIPLES
AND C++ CLASSES

1

Software Life Cycle

2

Software Development Phase

3

Analysis
Design

3
3

Implementation
Testing and Debugging

5
7

Algorithm Analysis: The Big-O Notation
Classes
Constructors

8
17
21

Unified Modeling Language Diagrams
Variable (Object) Declaration

22
23

Accessing Class Members

24

Implementation of Member Functions
Reference Parameters and Class Objects (Variables)

25
30

Assignment Operator and Classes
Class Scope

31
32

Functions and Classes
Constructors and Default Parameters

32
32

Destructors

33

Structs

33

viii |

Data Structures Using C++, Second Edition

Data Abstraction, Classes, and Abstract Data Types
Programming Example: Fruit Juice Machine

38

Identifying Classes, Objects, and Operations

48

Quick Review

49

Exercises

51

Programming Exercises

2

33

57

OBJECT-ORIENTED DESIGN (OOD) AND C++

59

Inheritance
Redefining (Overriding) Member Functions of the Base Class

60
63

Constructors of Derived and Base Classes

69

Header File of a Derived Class
Multiple Inclusions of a Header File

75
76

Protected Members of a Class
Inheritance as public, protected, or private

78
78

Composition

79

Polymorphism: Operator and Function Overloading

84

Operator Overloading

85

Why Operator Overloading Is Needed

85

Operator Overloading
Syntax for Operator Functions

86
86

Overloading an Operator: Some Restrictions
The Pointer this

87
87

Friend Functions of Classes

91

Operator Functions as Member Functions and Nonmember
Functions

94

Overloading Binary Operators
Overloading the Stream Insertion (<<) and Extraction (>>)
Operators

95
98

Operator Overloading: Member Versus Nonmember

102

Programming Example: Complex Numbers

103

Function Overloading

108

Table of Contents

Templates

|

108

Function Templates
Class Templates

109
111

Header File and Implementation File of a Class Template

112

Quick Review
Exercises

115

Programming Exercises

3

113

124

POINTERS AND ARRAY-BASED LISTS

131

The Pointer Data Type and Pointer Variables

132

Declaring Pointer Variables

132

Address of Operator (&)
Dereferencing Operator (*)

133
133

Pointers and Classes
Initializing Pointer Variables

137
138

Dynamic Variables

138

Operator new
Operator delete

138
139

Operations on Pointer Variables
Dynamic Arrays

145
147

Array Name: A Constant Pointer

148

Functions and Pointers
Pointers and Function Return Values

149
150

Dynamic Two-Dimensional Arrays
Shallow Vs
...
Designed for the CS2 C++ course,
this text will provide a breath of fresh air to you and your students
...
This text is a
culmination and development of my classroom notes throughout more than 50 semesters of
teaching successful programming and data structures to computer science students
...
The approach taken in this book to
present the material is similar to the one used in the CS1 book and therefore driven by the
students’ demand for clarity and readability
...
Most of the examples in this book resulted from student
interaction in the classroom
...
However, if you need to review these
concepts or you have taken Java as a first program language, you will find the relevant material
in Appendix G
...
In addition, some adequate mathematics
background such as college algebra is required
...




To create generic code to process data in linked lists, Chapter 5 uses the concept of
abstract classes to capture the basic properties of linked lists and then derive two
separate classes to process unordered and ordered lists
...


In Chapter 3, a section on creating and manipulating dynamic two-dimensional
arrays, a section on virtual functions, and a section on abstract classes is included
...




In Chapter 9, the discussion of hashing is expanded with additional examples illustrating how to resolve collisions
...




Throughout the book, new exercises and programming exercises have been added
...

Chapter 12, on graphs, contains a new section on how to find Euler circuits in a graph
...


These changes were implemented based on comments from the reviewers of the second
proposal and readers of the first edition
...
The programming examples given in this book effectively
use OOD techniques to solve and program a particular problem
...
After describing the life cycle of a
software, this chapter discusses why algorithm analysis is important and introduces the Big-O
notation used in algorithm analysis
...
Encapsulation in C++ is achieved via the use of classes
...
If you are familiar with how to create
and use your own classes, you can skip this section
...

Chapter 2 continues with the principles of OOD and discusses inheritance and two types of
polymorphism
...

The three basic data types in C++ are simple, structured, and pointers
...
The
structured data type class is introduced in Chapter 1
...
This chapter also discusses the relationship between pointers
and classes
...
Chapter 3 also discusses virtual functions and abstract classes
...
Among other things, the STL
provides code to process lists (contiguous or linked), stacks, and queues
...
In particular, this chapter discusses the sequence containers vector and

Preface to Second Edition |

xxv

deque
...

Chapter 5 discusses linked lists in detail, by first discussing the basic properties of linked lists
such as item insertion and deletion and how to construct a linked list
...
Doubly linked lists are also
discussed in some detail
...
This chapter also discusses the STL class list
...

Chapters 7 and 8 discuss stacks and queues in detail
...
The programming code developed in these chapters is generic
...
After analyzing the sequential search
algorithm, it discusses the binary search algorithm and provides a brief analysis of this
algorithm
...

Sorting algorithms such as selection sort, insertion sort, Shellsort, quicksort, mergesort, and
heapsort are introduced and discussed in Chapter 10
...
Chapter 12 introduces graphs and discusses graph algorithms such as shortest
path, minimum spanning tree, topological sorting, and how to find Euler circuits in a graph
...
In particular, it
introduces the STL associative containers and algorithms
...
Appendix B shows the precedence and associativity of the C++ operators
...
Appendix D lists the C++ operators that can be overloaded
...
Appendix F contains the detailed analysis of the
insertion sort and quicksort algorithms
...
One of its objectives is
to provide a quick review of the basic elements of C++
...

Therefore, if you have taken Java as a first programming language, Appendix G helps familiarize
you with these basic elements of C++
...
Appendix I gives the
answers to odd-numbered exercises in the text
...
To do so, the book discusses data structures such as
linked lists, stacks, queues, and binary trees
...
However, our emphasis is to
teach you how to develop your own code
...
Chapter 4 of this book introduces STL
...
The book can, therefore, be used in various ways
...

Chapter 6 discusses recursion
...

If you read Chapter 6 after these chapters, then you can skip the section ‘‘Removing
Recursion’’ in Chapter 7, and read this section after reading Chapter 6
...

Therefore, we recommend that you should study Chapter 6 before Chapter 9
...

Chapter 1
Chapter 2
Chapter 3

Chapter 4

Chapter 5

Chapter 6
Chapter 7

Chapter 8

Chapter 9

Chapter 10

Chapter 11
Chapter 13

Chapter 12

A dotted arrow means that the chapter is not essential to study the
following chapter
...
From beginning to end, the
concepts are introduced at an appropriate pace
...
The writing style of this book is simple and
straightforward
...
Here is a brief summary of the
various pedagogical features in each chapter:



Learning objectives offer an outline of C++ programming concepts that will be
discussed in detail within the chapter
...





Numbered Examples within each chapter illustrate the key concepts with relevant code
...




Programming Exercises challenge students to write C++ programs with a specified
outcome
...
The
book contains over 295 figures
...
These
examples contain the accurate, concrete stages of Input, Output, Problem Analysis
and Algorithm Design, and a Program Listing
...

Exercises further reinforce learning and ensure that students have, in fact, learned the
material
...
Before introducing a key
concept, we explain why certain elements are necessary
...

Each chapter contains two types of programs
...
Each line of the programming code in these
examples is numbered
...
The rationale behind each line is discussed in detail
...
These Programming Examples form the backbone of the book
...
Beginning with Problem Analysis, the
Programming Example is then followed by Algorithm Design
...
In addition to teaching problem-solving techniques, these detailed
programs show the user how to implement concepts in an actual C++ program
...

Quick Review sections at the end of each chapter reinforce learning
...
Many readers refer to the Quick Review as a way
to quickly review the chapter before an exam
...

Programs can be compiled with various compilers such as Microsoft Visual C++ 2008
...
All of the teaching tools available with this book are provided to the instructor on a
single CD-ROM
...

ExamView includes hundreds of questions that correspond to the topics covered in this text,
enabling students to generate detailed study guides that include page references for further
review
...


PowerPoint Presentations
This book comes with Microsoft PowerPoint slides for each chapter
...
Instructors can modify slides or add their own slides to
tailor their presentations
...
For more
information on how to bring distance learning to your course, contact your local Cengage
Learning sales representative
...
cengage
...
If an input file is needed to run a program, it is included
with the source code
...
cengage
...
If an input file is needed to run a
programming exercise, it is included with the solution file
...
Additionally, I
express thanks to the reviewers of the proposal package: Ted Krovetz, California State
University; Kenneth Lambert, Washington and Lee University; Stephen Scott, University
of Nebraska; and Deborah Silver, Rutgers, The State University of New Jersey
...
Next, I express thanks to Amy Jollymore, Acquisitions
Editor, for recognizing the importance and uniqueness of this project
...
I extend my
sincere thanks to Alyssa, as well as to Content Project Manager Heather Furrow
...

I would like to thank Chris Scriver and Serge Palladino of QA department of Cengage
Learning for patiently and carefully proofreading the text, testing the code, and discovering
typos and errors
...
Finally, I
would like to thank my wife Sadhana and my daughter Shelly
...

I welcome any comments concerning the text
...
edu
...
S
...


Learn about software engineering principles


...


Become aware of structured design and object-oriented design programming methodologies


...


Become aware of private, protected, and public members of a class


...


Become aware of Unified Modeling Language (UML) notation


...


Become aware of abstract data type (ADT)


...
Software are
computer programs designed to accomplish a specific task
...
This book, for example, was created with the help of a
´
´
word processor
...
Instead, they use word processing software to complete their term papers
...

Powerful, yet easy-to-use software has drastically changed the way we live and communicate
...
With the help of computers and the software running on them, you
can send letters to, and receive letters from, loved ones within seconds
...
You can watch how stocks perform in real time,
and instantly buy and sell them
...
It is the software that enables you to do things
that were, perhaps, fiction a few years ago
...

From the time a software program is conceived until it is delivered, it goes through
several phases
...
Most colleges and universities offer a course in software engineering
...

However, this chapter briefly describes some of the basic software engineering principles
that can simplify program design
...
The three fundamental stages through which a
program goes are development, use, and maintenance
...
The new program is created in the
software development stage
...

Once the program is considered complete, it is released for the user to use
...
The problems and/or ideas for improvements are conveyed to the software
developer, and the program goes through the maintenance phase
...
If there are serious/numerous changes, typically, a new
version of the program is created and released for use
...


Software Development Phase

| 3

The software development phase is the first and perhaps most important phase of the
software life cycle
...
The next section describes this phase
...


Analysis
Analyzing the problem is the first and most important step
...

Understand the problem requirements
...

Suppose that you need to develop a program to make an automated teller
machine (ATM) operational
...
Here, you determine the necessary operations performed by the machine, such as withdraw money, deposit
money, transfer money, check account balance, and so on
...

To make it user-friendly, you must understand their requirements and
add any necessary operations
...
That is, you need to look at sample data
...

• If the problem is complex, divide the problem into subproblems, analyze
each subproblem, and understand each subproblem’s requirements
...
If you broke the problem into subproblems, you need to design an
algorithm for each subproblem
...

STRUCTURED DESIGN
Dividing a problem into smaller subproblems is called structured design
...
In structured design, the problem is divided into smaller problems
...

The solutions of all the subproblems are then combined to solve the overall problem
...

OBJECT-ORIENTED DESIGN
In object-oriented design (OOD), the first step in the problem-solving process is to
identify the components called objects, which form the basis of the solution, and
determine how these objects interact with one another
...
The
two main objects in this problem are the video and the customer
...
For example, for a video object, the
data might include the movie name, starring actors, producer, production company,
number of copies in stock, and so on
...

This illustrates that each object consists of data and operations on that data
...
In OOD, the final program is
a collection of interacting objects
...
You will learn about the
many advantages of OOD in later chapters
...
How classes
are implemented in C++ is described later in this chapter
...

In object-oriented design, you decide what classes you need and their relevant data
members and member functions
...


Software Development Phase

| 5

Implementation
In the implementation phase, you write and compile programming code to implement the
classes and functions that were discovered in the design phase
...
It contains many case studies—called Programming
Examples—to solve real-world problems
...
Some
functions are part of the main program; others are used to implement various operations
on objects
...
To use a function, the user needs to know only how to use the function and
what the function does
...
Let us illustrate this with the help of the
following example
...
The conversion formula is 1 inch ¼ 2
...
The
following function accomplishes the job:
double inchesToCentimeters(double inches)
{
if (inches < 0)
{
cerr << "The given measurement must be nonnegative
...
0;
}
else
return 2
...
Unlike the
object cout (whose output first goes to the buffer), the output of cerr is immediately
sent to the standard error stream, which is usually the screen
...
0; otherwise, the function
returns the equivalent length in centimeters
...
However, the user must know that in order to get the
valid answer, the input must be a nonnegative number
...
0
...

Precondition: A statement specifying the condition(s) that must be true before the
function is called
...

The precondition and postcondition for the function inchesToCentimeters can be
specified as follows:
//Precondition: The value of inches must be nonnegative
...
0; otherwise, the function returns the
//
equivalent length in centimeters
...
" << endl;
return -1
...
54 * inches;
}

In certain situations, you could use C++’s assert statement to validate the input
...

//Postcondition: If the value of inches is < 0, the function
//
terminates; otherwise, the function returns the
//
equivalent length in centimeters
...
54 * inches;
}

However, if the assert statement fails, the entire program will terminate, which
might be appropriate if the remainder of the program depends on the execution of
the function
...

To use the assert function, you need to include the header file cassert in your
program
...
This directive must be placed before the statement #include

...
Because the user of a function need not be concerned with the details of the
function, the preconditions and postconditions are specified with the function prototype
...

//Postcondition: If the value of inches is < 0, the function
//
returns -1
...


As another example, to use a function that searches a list for a specific item, the list must
exist before the function is called
...

bool search(int list[], int listLength, int searchItem);
//Precondition: The list must exist
...


Testing and Debugging
The term testing refers to testing the correctness of the program; that is, making sure that
the program does what it is supposed to do
...

Once a function and/or an algorithm is written, the next step is to verify that it works
properly
...

Therefore, to increase the reliability of the program, errors must be discovered and fixed
before the program is released to the user
...
However, for large and complex programs, this technique
alone might not be enough because errors can be made in the proof
...
The program is run through a
series of specific tests, called test cases, in an attempt to find problems
...
Because a test case can be repeated several times, it must be properly
documented
...
It is, therefore,
impractical (although possible) to create test cases for all possible inputs
...
Clearly, it is not possible to create a test case
for each integer
...
An equivalence category is a set of input values that are likely to produce the
same output
...
In this case, you can
form two equivalence categories—one consisting of negative numbers and the other
consisting of nonnegative numbers
...
In black-box
testing, you do not know the internal working of the algorithm or function
...
Black-box testing is based on inputs and outputs
...
If a function works for one input in the equivalence category, it is
expected to work for other inputs in the same category
...
In black-box testing, the function is
tested on values that surround and fall on the boundaries, called boundary values, as
well as general values from the equivalence categories
...

White-box testing relies on the internal structure and implementation of a function or
algorithm
...
Suppose that you want to ensure whether an if statement works
properly
...
Loops and other
structures can be tested similarly
...
Usually, there are various ways to
design a particular algorithm
...

Let us consider the following problem
...
They have hired
extra delivery people to deliver the packages on time
...
Suppose that 50 packages are to be delivered to 50 different houses
...
(See Figure 1-1, in which each dot represents a house and the distance between
houses is 1 mile
...


Gift shop and each dot representing a house

To deliver 50 packages to their destinations, one of the drivers picks up all 50 packages,
drives one mile to the first house and delivers the first package
...
Figure 1-2 illustrates this delivery scheme
...


Package delivering scheme

It now follows that using this scheme, the distance driven by the driver to deliver the
packages is:
1 þ 1 þ 1 þ ::: þ 1 ¼ 50 miles
Therefore, the total distance traveled by the driver to deliver the packages and then
getting back to the shop is:
50 þ 50 ¼ 100 miles
Another driver has a similar route to deliver another set of 50 packages
...
Next,
the driver picks up the second package, drives 2 miles, delivers the second package, and
then returns to the shop
...
Figure 1-3 illustrates this delivery scheme
...


Another package delivery scheme

The driver delivers only one package at a time
...
Using this scheme, the total
distance traveled by this driver to deliver the packages and then getting back to the store is:
2 Á ð1 þ 2 þ 3 þ
...
If the packages are delivered using the
first scheme, the following equation gives the total distance traveled:
1 þ 1 þ
...
þ nÞ ¼ 2 Á ðnðn þ 1Þ=2Þ ¼ n2 þ n

ð1-2Þ

10 |

Chapter 1: Software Engineering Principles and C++ Classes

In Equation (1-1), we say that the distance traveled is a function of n
...
In this equation, for large values of n, we will find that the term consisting
of n2 will become the dominant term and the term containing n will be negligible
...
Table 1-1 evaluates Equations
(1-1) and (1-2) for certain values of n
...
)
TABLE 1-1

Various values of n, 2n, n2, and n2 + n

n

2n

n2

n2 + n

1

2

1

2

10

20

100

110

100

200

10,000

10,100

1000

2000

1,000,000

1,001,000

10,000

20,000

100,000,000

100,010,000

While analyzing a particular algorithm, we usually count the number of operations
performed by the algorithm
...
This is because a particular algorithm can be
implemented on a variety of computers and the speed of the computer can affect the
execution time
...
Let us consider the following examples
...
(Assume that all variables are properly declared
...
Either Line 4
or Line 6 executes
...
In this algorithm, the number of operations executed is fixed
...
Similarly,
there are nine or eight operations after the while loop, depending on whether Line 11 or
Line 13 executes
...

Thus, Lines 5 through 8 have five operations
...
One extra operation is also executed at Line 5 to
terminate the loop
...

If the while loop executes 10 times, the total number of operations executed is:
10 Á 5 þ 1 þ 5 þ 9 or 10 Á 5 þ 1 þ 5 þ 8
that is,
10 Á 5 þ 15 or 10 Á 5 þ 14
We can generalize it to the case when the while loop executes n times
...


12 |

Chapter 1: Software Engineering Principles and C++ Classes

Usually, in an algorithm, certain operations are dominant
...
Similarly, in a search
algorithm, because the search item is compared with the items in the list, the dominant
operations would be comparison, that is, the relational operation
...
For another example, suppose
that we write a program to multiply matrices
...
Because multiplication takes more computer time to execute,
to analyze a matrix multiplication algorithm, we count the number of multiplications
...
If there are various algorithms to accomplish a particular task, the algorithm
analysis allows the programmer to choose between various options
...
Suppose that you want to determine whether an item is in a list
...
To determine whether the item is in the
list, there are various algorithms, as you will see in Chapter 9
...
Therefore, the performance of the
algorithm depends on the number of comparisons
...
Suppose that,
on a particular computer, it takes c units of computer time to execute one operation
...
Clearly, the
constant c depends on the speed of the computer and, therefore, varies from computer to
computer
...

If we know how the function f(n) grows as the size of the problem grows, we can
determine the efficiency of the algorithm
...

TABLE 1-2

Growth rates of various functions

n

log2n

n log2n

n2

2n

1

0

0

1

2

2

1

2

2

4

4

2

8

16

16

8

3

24

64

256

16

4

64

256

65,536

32

5

160

1024

4,294,967,296

Algorithm Analysis: The Big-O Notation

|

13

Table 1-2 shows how certain functions grow as the parameter n, that is, the problem size,
grows
...
From Table 1-2, it follows that if the
number of basic operations is a function of f(n) ¼ n2, the number of basic operations is
quadrupled
...
However, if the number of operations is a function of f(n) ¼
log2n, the change in the number of basic operations is insignificant
...
Table 1-3
shows the time that the computer takes to execute f(n) basic operations
...
01ms

0
...
033ms

0
...
02ms

0
...
086ms

0
...
03ms

0
...
147ms

0
...
04ms

0
...
213ms

1
...
3min

50

0
...
006ms

0
...
5ms

13 days

100

0
...
007ms

0
...
00ms

0
...
966ms

1ms

10,000

10ms

0
...
10ms

0
...
67ms

10s

1,000,000

1 ms

0
...
93ms

16
...
01s

0
...
23s

1
...
10s

0
...
66s

115
...


1

14 |

Chapter 1: Software Engineering Principles and C++ Classes

Figure 1-4 shows the growth rate of functions in Table 1-3
...
That is, we develop a notation that is useful in describing the
behavior of the algorithm, which gives us the most useful information about the algorithm
...

Let f be a function of n
...

Consider the functions g(n) ¼ n2 and f(n) ¼ n2 + 4n + 20
...
Consider Table 1-4
...
For large values of n, we can predict the
behavior of f(n) by looking at the behavior of g(n)
...

Let f and g be real-valued functions
...

Definition: We say that f(n) is Big-O of g(n), written f(n) ¼ O(g(n)), if there exists
positive constants c and n0 such that f(n) cg(n) for all n ! n0
...
Note that f is a constant
function
...


Let c ¼ a, n0 ¼ a, and g(n) ¼ 1
...


cg(n) for all n ! n0
...

EXAMPLE 1-4
Let f(n) ¼ 2n + 5, n ! 0
...


Let c ¼ 3, n0 ¼ 5, and g(n) ¼ n
...


cg(n) for all n ! 5
...
Note that 3n + 2
that
f(n) ¼ n2+ 3n + 2

n 2+ n 2

Let c ¼ 2 and n0 ¼ 4
...
This implies

2n2¼ 2g(n) for all n ! 4
...
It now follows that f(n) ¼ O(g(n)) ¼ O(n2)
...
Here we state the theorem without proof
...
Then f(n) ¼
O(nm)
...

EXAMPLE 1-6
In the following, f(n) is a nonnegative real-valued function
...

f (n) ¼ n2 + 5n + 1
f (n) ¼ 4n6 + 3n3 + 1
f (n) ¼ 10n7 + 23
f (n) ¼ 6n15

f (n)
f (n)
f (n)
f (n)
f (n)

¼
¼
¼
¼
¼

O (n)
O (n2)
O (n6)
O (n7)
O (n15)

EXAMPLE 1-7
Suppose that f(n) ¼ 2log2n + a, where a is a real number
...


EXAMPLE 1-8
Consider the following code, where m and n are int variables and their values are
nonnegative:
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
cout << i * j << endl;

//Line 1
//Line 2
//Line 3

This code contains nested for loops
...

For each iteration of the outer loop, the inner loop, at Line 2, executes n times
...
It follows that the
total number of iterations of the nested for loop is mn
...
Therefore, this algorithm is O(mn)
...


Classes

|

17

Table 1-5 shows some common Big-O functions that appear in the algorithm analysis
...

TABLE 1-5

Some Big-O functions that appear in algorithm analysis

Function g (n)

Growth rate of f (n)

g (n) ¼ 1

The growth rate is constant and so does not depend on n, the size of the
problem
...
Because a logarithm function grows
slowly, the growth rate of the function f is also slow
...
The growth rate of f is directly proportional to the
size of the problem
...


g (n) ¼ n2

The growth rate of such functions increases rapidly with the size of the
problem
...


g (n) ¼ 2n

The growth rate is exponential
...


It can be shown that
O (1)

O (log2n)

O (n)

O (nlog2n)

O (n2)

O (2n)
...
If you are familiar with how classes are implemented in C++, you can skip this section
...
In
C++, the mechanism that allows you to combine data and the operations on that data in a
single unit is called a class
...
The
components of a class are called the members of the class
...
That is, a
member of a class can be either a variable (to store data) or a function
...
Furthermore, in the definition of the class, you cannot initialize
a variable when you declare it
...

• If a member of a class is a function, it can (directly) access any member of
the class—data members and function members
...
The only obvious
condition is that you must declare an identifier before you can use it
...
It
announces the declaration of a class
...
The
semicolon is part of the syntax
...

The members of a class are classified into three categories: private, public, and
protected, called member access specifiers
...

Following are some facts about private and public members of a class:





By default, all members of a class are private
...

A public member is accessible outside the class
...


In C++, private, protected, and public are reserved words
...
Furthermore, suppose that the time is represented as a set of three integers: one
to represent the hours, one to represent the minutes, and one to represent the seconds
...

2
...

4
...

6
...


Set the time
...

Print the time
...

Increment the time by one minute
...

Compare two times for equality
...

Some members of the class clockType will be private; others will be public
...
The general rule is that any member that needs to be accessed
outside the class is declared public; any member that should not be accessed directly by
the user should be declared private
...
Therefore, the members that set the time and print the time should be
declared public
...
On the other hand, to control the direct manipulation of the data
members hr, min, and sec, we will declare these data members private
...

The following statements define the class clockType:
class clockType
{
public:
void setTime(int hours, int minutes, int seconds);
//Function to set the time
//The time is set according to the parameters
//Postcondition: hr = hours; min = minutes; sec = seconds
//
The function checks whether the values of hours,
//
minutes, and seconds are valid
...

void getTime(int& hours, int& minutes, int& seconds) const;
//Function to return the time
//Postcondition: hours = hr; minutes = min; seconds = sec
void printTime() const;
//Function to print the time
//Postcondition: Time is printed in the form hh:mm:ss
...

//
If the before-increment time is 23:59:59, the time
//
is reset to 00:00:00
...

//
If the before-increment time is 23:59:53, the time
//
is reset to 00:00:53
...

//
If the before-increment time is 23:45:53, the time
//
is reset to 00:45:53
...
It has three data members: hr, min, and sec
...

The seven function members—setTime, getTime, printTime,
incrementSeconds, incrementMinutes, incrementHours, and
equalTime—can directly access the data members (hr, min, and sec)
...

In the function equalTime, the parameter otherClock is a constant
reference parameter
...
You could
have declared otherClock as a value parameter, but that would require
otherClock to copy the value of the actual parameter, which could result
in poor performance
...
)
The word const at the end of the member functions getTime,
printTime, and equalTime specifies that these functions cannot modify
the data members of a variable of type clockType
...
The only thing
you need to remember is that, by default, all members of a class are private
...
If you decide to declare
the private members after the public members (as is done in the case of clockType),
you must use the private label to begin the declaration of the private members
...
However, a function member can also be private
...

Similarly, a data member of a class can also be public
...
You will learn how to write them shortly
...
The
given values are passed as parameters to the function setTime
...
The function incrementSeconds increments
the time by one second, the function incrementMinutes increments the time by one
minute, the function incrementHours increments the time by one hour, and the function
equalTime compares the two times for equality
...
Therefore, when
an object is instantiated, there is no guarantee that the data members of the object will be
initialized
...
There are two types of constructors: with parameters and without parameters
...

Constructors have the following properties:








The name of a constructor is the same as the name of the class
...
That is, it is
neither a value-returning function nor a void function
...
However, all constructors of
a class have the same name
...
That is, either they have a different
number of formal parameters or, if the number of formal parameters is
the same, the data type of the formal parameters, in the order you list,
must differ in at least one position
...

Because they have no types, they cannot be called like other functions
...


1

22 |

Chapter 1: Software Engineering Principles and C++ Classes

Let us extend the definition of the class clockType by including two constructors:
class clockType
{
public:
//Place the function prototypes of the functions setTime,
//getTime, printTime, incrementSeconds, incrementMinutes,
//incrementHours, and equalTime as described earlier, here
...

//Postconditions: hr = hours; min = minutes; sec = seconds
//
The constructor checks whether the values of hours,
//
minutes, and seconds are valid
...

clockType();
//Default constructor with parameters
//The time is set to 00:00:00
...
For example, Figure 1-5 shows the UML class
diagram of the class clockType
...
The middle box contains the data members and
their data types
...
A + (plus) sign in front of a member indicates that this member is
a public member; a – (minus) sign indicates that this is a private member
...


Variable (Object) Declaration
Once a class is defined, you can declare variables of that type
...
To help you become familiar with this
terminology, from now on we will use the term class object, or simply object, for a class variable
...
Therefore, when you declare a class object, either the default constructor
executes or the constructor with parameters executes
...
In this case, the default constructor
executes and the instance variables of myClock are initialized to 0
...
In
fact, if you accidentally include the empty parentheses, the compiler generates a syntax error
message
...
);

where each of argument1, argument2, and so on is either a variable or an expression
...

• If the type of the arguments does not match the formal parameters of any
constructor (in the order given), C++ uses type conversion and looks for
the best match
...
Any ambiguity will result
in a compile-time error
...
Here, we are passing
three values of type int, which matches the type of the formal parameters of the
constructor with a parameter
...

Consider the following statements that declare two objects of type clockType:
clockType myClock(8, 12, 30);
clockType yourClock(12, 35, 45);

Each object has 10 members: seven member functions and three instance variables
...

In actuality, memory is allocated only for the instance variables of each class object
...


Accessing Class Members
Once an object of a class is declared, it can access the members of the class
...
memberName

In C++, the dot,
...

The class members that a class object can access depend on where the object is declared
...
(We
will elaborate on this when we write the definition of the member
function equalTime of the class clockType in the section ‘‘Implementation of Member Functions,’’ later in this chapter
...

Example 1-10 illustrates how to access the members of a class
...
setTime(5, 2, 30);
myClock
...
equalTime(yourClock))

...


...

In the first statement, myClock
...
The values 5, 2, and 30 are passed as parameters to the function setTime, and
the function uses these values to set the values of the three instance variables hr, min, and
sec of myClock to 5, 2, and 30, respectively
...

In the third statement, the member function equalTime executes and compares the three
instance variables of myClock to the corresponding instance variables of yourClock
...
So it needs one more object, which in
this case is yourClock, to compare
...

The objects myClock and yourClock can access only public members of the class
...
hr = 10;
myClock
...
min;

//illegal
//illegal

Implementation of Member Functions
When we defined the class clockType, we included only the function prototype for
the member functions
...
One way to implement these functions is to provide the function definition
rather than the function prototype in the class itself
...
Another reason for providing function
prototypes instead of function definitions relates to information hiding; that is, we want
to hide the details of the operations on the data
...
That
is, we will write the definitions of the functions setTime, getTime, printTime,
incrementSeconds, equalTime, and so on
...

To reference these identifiers, we use the scope resolution operator, :: (double colon)
...
For example, the definition
of the function setTime is as follows:
void clockType::setTime(int hours, int minutes, int seconds)
{
if (0 <= hours && hours < 24)
hr = hours;
else
hr = 0;
if (0 <= minutes && minutes < 60)
min = minutes;
else
min = 0;

}

if (0 <= seconds && seconds < 60)
sec = seconds;
else
sec = 0;

Note that the definition of the function setTime checks for the valid values of hours,
minutes, and seconds
...

Suppose that myClock is an object of type clockType (as declared previously)
...
Consider the following statement:
myClock
...
setTime(3, 48, 52);, setTime is accessed by the object
myClock
...
Thus, the values 3,
48, and 52, which are passed as parameters in the preceding statement, are assigned to the
three instance variables of myClock by the function setTime (see the body of the
function setTime)
...


myClock

3

min

48

sec

FIGURE 1-6

hr

52

Object myClock after the statement myClock
...

The definitions of these functions are simple and easy to follow
...

The function equalTime has the following definition:
bool clockType::equalTime(const clockType& otherClock) const
{
return (hr == otherClock
...
min
&& sec == otherClock
...

Suppose that myClock and yourClock are objects of type clockType, as declared previously
...


myClock

sec

yourClock

14

min

FIGURE 1-7

hr

hr

14

8

min

25

25

sec

54

Objects myClock and yourClock

Consider the following statement:
if (myClock
...


...


In the expression
myClock
...
Because otherClock is a
reference parameter, the address of the actual parameter yourClock is passed to the
formal parameter otherClock, as shown in Figure 1-8
...
In other words, when the body of the function equalTime executes, the
value of otherClock
...
min is 25, and the value of
otherClock
...
The function equalTime is a member of myClock
...
Therefore, the member hr
of myClock is compared with otherClock
...
min, and the member sec of myClock is compared with
otherClock
...

Once again, from the definition of the function equalTime, it is clear why this function
has only one parameter
...
Notice that within the
definition of this function, the object otherClock accesses the instance variables hr, min,
and sec
...
So is there any violation? The
answer is no
...
Moreover, otherClock is an object of type
clockType
...

The same is true for any member function of a class
...

This definition of the class clockType includes two constructors: one with three
parameters and one without any parameters
...

clockType::clockType()
{
hr = 0;
min = 0;
sec = 0;
}

//default constructor

clockType::clockType(int hours, int minutes, int seconds)
{
if (0 <= hours && hours < 24)
hr = hours;
else
hr = 0;
if (0 <= minutes && minutes < 60)
min = minutes;
else
min = 0;

1

30 |

}

Chapter 1: Software Engineering Principles and C++ Classes

if (0 <= seconds && seconds < 60)
sec = seconds;
else
sec = 0;

From the definitions of these constructors, it follows that the default constructor sets the
three instance variables—hr, min, and sec—to 0
...

Moreover, we can write the definition of the constructor with parameters by calling
the function setTime, as follows:
clockType::clockType(int hours, int minutes, int seconds)
{
setTime(hours, minutes, seconds);
}

Once a class is properly defined and implemented, it can be used in a program
...

When you declare objects of the class clockType, every object has its own copy of the
instance variables hr, min, and sec
...


Reference Parameters and Class Objects (Variables)
Recall that when a variable is passed by value, the formal parameter copies the value
of the actual parameter
...
As a parameter, a class object can be passed
by value
...
The corresponding formal parameter
then receives a copy of the data of the variable
...
This operation might require, in addition to a large amount of storage
space, a considerable amount of computer time to copy the value of the actual parameter
into the formal parameter
...
Therefore, an efficient way to pass a variable as a
parameter is by reference
...
Sometimes, however, you do not
want the function to be able to change the values of the instance variables
...
As an example, consider
the following function definition:

Classes

|

31

void testTime(const clockType& otherClock)
{
clockType dClock;

...


...
The parameter
otherClock is declared using the keyword const
...
For
example, after the following statement executes, the value of myClock will not be altered:
testTime(myClock);

Generally, if you want to declare a class object as a value parameter, you declare it as a
reference parameter using the keyword const, as described previously
...
That is, you can use an assignment
statement to change the value of the formal parameter (which, of course, would have
no effect on the actual parameter)
...
Therefore, within
the definition of the function testTime, you cannot alter the value of otherClock
...
setTime(5, 34, 56); //illegal
otherClock = dClock;
//illegal

BUILT-IN OPERATIONS ON CLASSES
The two built-in operations that are defined for class objects are member access (
...
You have seen how to access an individual member of a class by using the
name of the class object, then a dot, and then the member name
...


Assignment Operator and Classes
Suppose that myClock and yourClock are variables of type clockType as defined
previously
...
That is, the value of yourClock
...
hr; the value of yourClock
...
min; and the value
of yourClock
...
sec
...
Therefore, an assignment statement performs a memberwise copy
...
Also, you can declare an array of class objects
...
A member of a class is local to the class
...


Functions and Classes
The following rules describe the relationship between functions and classes:


Class objects can be passed as parameters to functions and returned as
function values
...

• If a class object is passed by value, the contents of the instance variables of
the actual parameter are copied into the corresponding instance variables
of the formal parameter
...
In such a case, the rules for declaring
formal parameters are the same as those for declaring default formal parameters in a
function
...
Using the rules for defining
default parameters, in the definition of the class clockType, you can replace both
constructors using the following statement
...
)
clockType clockType(int = 0, int = 0, int = 0); //Line 1

In the implementation file, the definition of this constructor is the same as the definition
of the constructor with parameters
...
The data member hr of clock2 is
initialized to 5, and the data members min and sec of clock2 are initialized to 0
...
The data
member hr of clock4 is initialized to 7, the data member min of clock4 is initialized
to 34, and the data member sec of clock4 is initialized to 18
...


Destructors
Like constructors, destructors are also functions
...
That is, it is neither a value-returning function nor a void function
...
The
name of a destructor is the tilde character ($), followed by the name of the class
...


Structs
Structs are a special type of classes
...
In C++, you define structs by
using the reserved word struct
...
A struct is
defined just like a class
...

Most people are not concerned with the complexity of how the engine works
...
Our daily life has other similar examples
...

Separating the design details (that is, how the car’s engine works) from its use is called
abstraction
...
Thus, abstraction is the process of separating the logical properties from
the implementation details
...
We have an abstract view of what the
engine does, but are not interested in the engine’s actual implementation
...
Earlier sections of this chapter defined a data type
clockType
...

2
...

4
...

6
...


Set the time
...

Print the time
...

Increment the time by one minute
...

Compare two times to see whether they are equal
...

Data abstraction is defined as a process of separating the logical properties of the data from
its implementation
...

Abstract data type (ADT): A data type that separates the logical properties from the
implementation details
...
Following these conventions, we can define the
clockType ADT as follows:
dataTypeName
clockType
domain
Each clockType value is a time of day in the form of hours,
minutes, and seconds
...

Return the time
...

Increment the time by
Increment the time by
Increment the time by
Compare the two times

one second
...

one hour
...


To implement an ADT, you must represent the data and write algorithms to perform the
operations
...
Furthermore, our
definition of a class consisted only of the specifications of the operations; functions to

Data Abstraction, Classes, and Abstract Data Types |

35

implement the operations were written separately
...
In fact, in C++, classes were specifically designed
to handle ADTs
...
Because all values in a list are of the
same type, a convenient way to represent and process a list is to use an array
...

operations
Check to see whether the list is empty
...

Search the list for a given item
...

Insert an item in the list
...

Print the list
...
To be specific, suppose that the list is a set
of elements of the type int
...

//Precondition: The list must exist
...

bool isFull();
//Function to determine whether the list is full
...

//Postcondition: Returns true if the list is full,
//
false otherwise
...

//Postcondition: If searchItem is in the list, returns its
//
index, that is, its position in the list;
//
otherwise, it returns -1
...

//Precondition: The list must exist and must not be full
...


1

36 |

Chapter 1: Software Engineering Principles and C++ Classes

void remove(int removeItem);
//Function to delete removeItem from the list
...

void printList();
//Function to output the elements of the list
...

//Postcondition: The elements of the list are
//
printed on the standard output device
...

EXAMPLE 1-12
The most common attributes of a person are the person’s first name and last name
...
The
following statements define a class with these properties
...
S
...

//************************************************************
#include
using namespace std;
class personType
{
public:
void print() const;
//Function to output the first name and last name
//in the form firstName lastName
...

//Postcondition: firstName = first; lastName = last

Data Abstraction, Classes, and Abstract Data Types |

string getFirstName() const;
//Function to return the first name
...

string getLastName() const;
//Function to return the last name
...

personType();
//Default constructor
//Sets firstName and lastName to null strings
...

//Sets firstName and lastName according to the parameters
...


personType
–firstName: string
–lastName: string
+print(): void
+setName(string, string): void
+getFirstName() const: string
+getLastName() const: string
+personType()
+personType(string, string)

FIGURE 1-9

UML class diagram of the class personType

We now give the definitions of the member functions of the class personType
...
The machine dispenses apple juice, orange
juice, mango lassi, and fruit punch in recyclable containers
...

The program should do the following:
1
...

2
...

3
...

4
...

5
...

Input

The item selection and the cost of the item
...


Programming Example: Fruit Juice Machine |

39

PROBLEM
ANALYSIS AND
ALGORITHM
DESIGN

A juice machine has two main components: a built-in cash register and several
dispensers to hold and release the products
...
The cash register has some cash on
hand, it accepts the amount from the customer, and if the amount deposited is more
than the cost of the item, then—if possible—the cash register returns the change
...
The cash register should also be able to show the juice machine’s owner
the amount of money in the register at any given time
...

//****************************************************************
// Author: D
...
Malik
//
// class cashRegister
// This class specifies the members to implement a cash register
...

//Postcondition: The value of cashOnHand is returned
...

//Postcondition: cashOnHand = cashOnHand + amountIn;
cashRegister();
//Default constructor
//Sets the cash in the register to 500 cents
...

cashRegister(int cashIn);
//Constructor with a parameter
...

//Postcondition: cashOnHand = cashIn;
private:
int cashOnHand; //variable to store the cash in the register
};

1

40 |

Chapter 1: Software Engineering Principles and C++ Classes

Figure 1-10 shows the UML class diagram of the class cashRegister
...
The definitions of these functions are simple and easy to
follow
...
It
returns the value of the instance variable cashOnHand
...
The dispenser should show

the number of items in the dispenser and the cost of the item
...
Let us call this class dispenserType
...
S
...

//************************************************************
class dispenserType
{
public:
int getNoOfItems() const;
//Function to show the number of items in the machine
...

int getCost() const;
//Function to show the cost of the item
...

void makeSale();
//Function to reduce the number of items by 1
...

//Postcondition: numberOfItems = 50; cost = 50;
dispenserType(int setNoOfItems, int setCost);
//Constructor with parameters
//Sets the cost and number of items in the dispenser
//to the values specified by the user
...


dispenserType
–numberOfItems: int
–cost: int
+getNoOfItems() const: int
+getCost() const: int
+makeSale(): void
+dispenserType()
+dispenserType(int, int)

FIGURE 1-11

UML class diagram of the class dispenserType

Because the juice machine sells four types of items, we shall declare four objects of
type dispenserType
...

Following the definitions of the class dispenserType, the definitions of the
member functions and constructors are as follows:
int dispenserType::getNoOfItems() const
{
return numberOfItems;
}
int dispenserType::getCost() const
{
return cost;
}
void dispenserType::makeSale()
{
numberOfItems--;
}
dispenserType::dispenserType()
{
numberOfItems = 50;
cost = 50;
}

Programming Example: Fruit Juice Machine |

43

dispenserType::dispenserType(int setNoOfItems, int setCost)
{
if (setNoOfItems >= 0)
numberOfItems = setNoOfItems;
else
numberOfItems = 50;

}
MAIN
PROGRAM

if (setCost >= 0)
cost = setCost;
else
cost = 50;

When the program executes, it must do the following:
1
...

2
...

3
...

Furthermore, these instructions must be displayed after processing each selection
(except exiting the program), so that the user need not remember what to do if he
or she wants to buy two or more items
...
If the user has opted to buy a product
and if that product is available, the juice machine should show the cost of the product
and ask the user to deposit the money
...

This discussion translates into the following algorithm:
1
...

2
...

3
...

We divide this program into three functions—showSelection, sellProduct, and main
...
The definition of this function is:
void showSelection()
{
cout << "*** Welcome to Shelly's Fruit Juice Shop ***" << endl;
cout << "To select an item, enter " << endl;
cout << "1 for apple juice" << endl;
cout << "2 for orange juice" << endl;
cout << "3 for mango lassi" << endl;
cout << "4 for fruit punch" << endl;
cout << "9 to exit" << endl;
}//end showSelection

1

44 |

sellProduct

Chapter 1: Software Engineering Principles and C++ Classes

This function attempts to sell the product selected by the customer
...
The first thing that this function
does is check whether the dispenser holding the product is empty
...
If the dispenser
is not empty, it tells the user to deposit the necessary amount to buy the product
...
If the user fails to deposit
enough money, in two tries, to buy the product, the function simply returns
the money
...
) If the amount deposited by the user is sufficient, it accepts the money and sells
the product
...
(We also assume that this program does not return the extra money deposited by
the customer
...
)
From this discussion, it is clear that the function sellProduct must have access to
the dispenser holding the product (to decrement the number of items in the dispenser
by 1 and to show the cost of the item) as well as the cash register (to update the cash)
...
Furthermore, both parameters must be
referenced
...
If the dispenser is not empty
a
...

b
...

c
...
Show and prompt the customer to enter the additional amount
...
Calculate the total amount entered by the customer
...
If the amount entered by the customer is at least the cost of the
product,
i
...


Update the amount in the cash register
...

iii
...

e
...


Programming Example: Fruit Juice Machine |

45

2
...

The definition of the function sellProduct is:
void sellProduct(dispenserType& product, cashRegister& pCounter)
{
int amount; //variable to hold the amount entered
int amount2; //variable to hold the extra amount needed
if (product
...
getCost()
<< " cents" << endl;
cin >> amount;
if (amount < product
...
getCost()- amount << " cents" << endl;
cin >> amount2;
amount = amount + amount2;
}
if (amount >= product
...
acceptAmount(amount);
product
...
"
<< endl;
}
else
cout << "The amount is not enough
...
" << endl;
cout << "*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
<< endl << endl;
}
else
cout << "Sorry, this item is sold out
...
Create the cash register—that is, declare a variable of type
cashRegister
...
Create four dispensers—that is, declare four objects of type
dispenserType and initialize these objects
...
The
number of items in the dispenser is 75, and the cost of an item is 45 cents
...

4
...

6
...

Show the selection; call the function showSelection
...

While not done (a selection of 9 exits the program),
a
...

b
...

c
...

The definition of the function main is as follows:
int main()
{
cashRegister counter;
dispenserType appleJuice(100, 50);
dispenserType orangeJuice(100, 65);
dispenserType mangoLassi(75, 45);
dispenserType fruitPunch(100, 85);
int choice;

//variable to hold the selection

showSelection();
cin >> choice;
while (choice != 9)
{
switch (choice)
{
case 1:
sellProduct(appleJuice, counter);
break;
case 2:
sellProduct(orangeJuice, counter);
break;
case 3:
sellProduct(mangoLassi, counter);
break;
case 4:
sellProduct(fruitPunch, counter);
break;
default:
cout << "Invalid selection
...
S
...

// *************************************************************
#include
#include "cashRegister
...
h"
using namespace std;
void showSelection();
void sellProduct(dispenserType& product, cashRegister& pCounter);
//Place the definitions of the functions main, showSelection, and
//sellProduct here
...

*** Welcome to Shelly's Fruit Juice Shop ***
To select an item, enter
1 for apple juice
2 for orange juice
3 for mango lassi
4 for fruit punch
9 to exit
1
Please deposit 50 cents
50
Collect your item at the bottom and enjoy
...


Identifying Classes, Objects, and Operations
The hardest part of OOD is to identify the classes and objects
...

We begin with a description of the problem and then identify all of the nouns and verbs
...

For example, suppose that we want to write a program that calculates and prints the
volume and surface area of a cylinder
...

In this statement, the nouns are bold and the verbs are italic
...
The nouns—dimensions, surface area, and volume—
are characteristics of a cylinder and, thus, can hardly be considered classes
...
For
example, from the list of verbs for the cylinder problem description—write, input, calculate,
and print—the possible operations for a cylinder object are input, calculate, and print
...
The center of the base,
radius of the base, and height of the cylinder are the characteristics of the dimensions
...


Quick Review |

49

The verb calculate applies to determining the volume and the surface area
...
Similarly, the
verb print applies to the display of the volume and the surface area on an output device
...
There are several other OOD techniques in the literature
...


QUICK REVIEW
1
...

3
...

5
...

7
...

9
...

11
...

13
...

15
...

17
...

A program goes through many phases from the time it is first conceived
until the time it is retired, called the life cycle of the program
...

The new program is created in the software development stage
...

A program is retired if no new version of the program will be released
...

During the design phase, algorithms are designed to solve the problem
...

Two well-known design techniques are structured-design and objectoriented design
...
Each
subproblem is solved, and the solutions of all the subproblems are then
combined to solve the problem
...

An object consists of data and operations on those data
...

In the implementation phase, you write and compile programming code to
implement the classes and functions that were discovered in the design phase
...

A postcondition is a statement specifying what is true after the function call
is completed
...

19
...

21
...

23
...

25
...

27
...

29
...

31
...

33
...

35
...

37
...

39
...

Debugging refers to finding and fixing the errors, if they exist
...

A test case consists of a set of inputs, user actions, or other initial conditions,
and the expected output
...

While analyzing a particular algorithm, we usually count the number of
operations performed by the algorithm
...
The term asymptotic refers to the study of the
function f as n becomes larger and larger without bound
...

Components of a class are called the members of the class
...

In C++, class is a reserved word
...

The private members of a class are not accessible outside the class
...

By default, all members of a class are private
...

The private members are declared using the member access specifier
private
...

If any member of a class is a function, you usually use the function
prototype to declare it
...

In the definition of the class, you cannot initialize a variable when you
declare it
...
The middle box contains the data
members and their data types
...

A + (plus) sign in front of a member indicates that this member is a
public member; a – (minus) sign indicates that this is a private
member
...


Exercises

40
...

42
...

44
...

46
...

48
...

50
...

52
...

54
...

56
...
No memory is allocated; memory is
allocated for the class variables when you declare them
...

A class member is accessed using the class variable name, followed by the
dot operator (
...

The only built-in operations on classes are the assignment and member
selection
...

As parameters to functions, classes can be passed either by value or by
reference
...

The name of a constructor is the same as the name of the class
...

A constructor without parameters is called the default constructor
...

Destructors automatically execute when a class object goes out of scope
...

The name of a destructor is the tilde ($), followed by the class name (no
spaces in between)
...
As a result, they cannot be called like
other functions
...

An easy way to identify classes, objects, and operations is to describe the
problem in English and then identify all of the nouns and verbs
...


EXERCISES
1
...

a
...


c
...

The three fundamental stages of software are development, use, and
discard
...


|

51

1

52 |

Chapter 1: Software Engineering Principles and C++ Classes

The instance variables of a class must be of the same type
...
The function members of a class must be public
...

A class can have more than one constructor
...
A class can have more than one destructor
...
Both constructors and destructors can have parameters
...


2
...

4
...


What should be the pre- and postconditions for this function?
Each of the following expressions represents the number of operations for
certain algorithms
...
5n + 2n + 8
2
c
...
5(6n + 4)
e
...

4n log2n + 3n + 8
Consider the following function:
a
...


void funcExercise6(int x, int y)
{
int z;

}

7
...

Consider the following function:
int funcExercise7(int list[], int size)
{
int sum = 0;
for (int index = 0; index < size; index++)
sum = sum + list[index];
}

return sum;

Exercises

a
...

b
...


What is the order of the function funcExercise7?
Consider the following function prototype:

c
...


int funcExercise8(int x);

9
...


The function funcExercise8 returns the value as follows: If 0 <¼ x <¼ 50,
it returns 2x; if –50 <¼ x < 0, it returns x2; otherwise it returns –999
...
What is the order of your function?
Characterize the following algorithm in terms of Big-O notation
...
(Assume that all
variables are properly declared
...


Characterize the following algorithm in terms of Big-O notation
...
(Assume that all variables are properly declared
...


Characterize the following algorithm in terms of Big-O notation
...


Characterize the following algorithm in terms of Big-O notation
...


Find the syntax errors in the definitions of the following classes:
a
...


c
...


class BB
{
int one ;
int two;
public:
bool equal();
print();
BB(int, int);
}
class CC
{
public;
void set(int, int);
void print();
CC();
CC(int, int);
bool CC(int, int);
private:
int u;
int v;
};

Consider the following declarations:
class xClass
{
public:
void func();
void print() const;
xClass ();
xClass (int, double);
private:
int u;
double w;
};
xClass x;
a
...

c
...


e
...


How many members does class xClass have?
How many private members does class xClass have?
How many constructors does class xClass have?
Write the definition of the member function func so that u is set to 10
and w is set to 15
...

Write the definition of the member function print that prints the
contents of u and w
...


Exercises

Write a C++ statement that prints the values of the data members of
the object x
...
Write a C++ statement that declares an object t of the type xClass,
and initializes the data members of t to 20 and 35
...

Consider the definition of the following class:
g
...


class CC
{
public:
CC();
CC(int);
CC(int, int);
CC(double, int);

...


...


//Line
//Line
//Line
//Line

Give the line number containing the constructor that is executed in
each of the following declarations:
i
...


CC two(5, 6);

iii
...
5, 8);

Write the definition of the constructor in Line 1 so that the private
data members are initialized to 0
...
Write the definition of the constructor in Line 2 so that the private
data member u is initialized according to the value of the parameter,
and the private data member v is initialized to 0
...
Write the definition of the constructors in Lines 3 and 4 so that the
private data members are initialized according to the values of the
parameters
...


17
...
printTime();
cout << endl;
clock2
...
setTime(6, 59, 39);
clock1
...
incrementMinutes();
clock1
...
setTime(0, 13, 0);
if (clock1
...
" << endl;
else
cout << "The two times are different
...


Write the definition of a class that has the following properties:
a
...


c
...


The name of the class is secretType
...

The class secretType has the following member functions:

print—Outputs the data stored in the instance variables with the
appropriate titles
setName—Function to set the name
setAge—Function to set the age
setWeight—Function to set the weight
setHeight—Function to set the height
getName—Value-returning function to return the name
getAge—Value-returning function to return the age
getWeight—Value-returning function to return the weight
getHeight—Value-returning function to return the height
Default constructor—Sets name to the empty string and age, weight,
and height to 0
Constructor with parameter—Sets the values of the instance variables
to the values specified by the user
d
...

Assume the definition of the class personType as given in this chapter
...


b
...


Write a C++ statement that declares student to be a personType
object, and initialize its first name to "Buddy" and last name to "Arora"
...

Write C++ statements that change the first name of student to
"Susan" and the last name to "Miller"
...


Write a program that converts a number entered in Roman numerals to
decimal form
...
An
object of romanType should do the following:
a
...

c
...

Convert and store the number into decimal form
...
(Write two separate functions—one to print the number as a
Roman numeral and the other to print the number as a decimal number
...


Remember, a larger numeral preceding a smaller numeral means addition,
so LX is 60
...
Any place in a decimal number, such as the 1s place, the
10s place, and so on, requires from zero to four Roman numerals
...
Test your program using the following Roman numerals: MCXIV,
CCCLIX, and MDCLXVI
...
The class dayType should store the day, such as
Sunday for Sunday
...

b
...

c
...

d
...

e
...

f
...

For example, if the current day is Monday and we add 4 days, the day to
be returned is Friday
...

g
...

Write the definitions of the functions to implement the operations for the
class dayType as defined in Programming Exercise 2
...

a
...


1000
500
100
50
10
5
1

|

57

1

58 |

4
...

The member functions that we included merely print the name and set the
name of a person
...

b
...

d
...


5
...


Set the first name only
...

Store and set the middle name
...

Check whether a given last name is the same as the last name of this person
...
Also, write a program to test various operations on this class
...

Rewrite the definition of the function sellProduct so that it keeps prompting
the user to enter more money as long as the user has not entered enough money
to buy the product
...

The equation of a line in standard form is ax + by ¼ c, where a and b both
cannot be zero, and a, b, and c are real numbers
...
If a ¼ 0, then it is a horizontal line, and if b ¼ 0, then it is a
vertical line
...
Two lines are parallel if
they have the same slope or both are vertical lines
...
Design the class lineType to store a line
...
Your class must contain the following operations:
If a line is nonvertical, then determine its slope
...
Determine if two lines are equal
...
)
c
...

d
...

e
...

Add appropriate constructors to initialize variables of lineType
...

(Tic-Tac-Toe) Write a program that allows two players to play the tic-tactoe game
...
Include a 3 by 3 two-dimensional array, as a private
instance variable, to create the board
...
Some of the operations on a ticTacToe object are printing the
current board, getting a move, checking if a move is valid, and determining
the winner after each move
...

a
...


Chapter 1: Software Engineering Principles and C++ Classes

2

CHAPTER

O BJECT -O RIENTED D ESIGN
(OOD) AND C++
I N T H I S C H A P T E R , YO U W I L L :


...


Learn about derived and base classes


...


Examine how the constructors of base and derived classes work


...


Explore three types of inheritance: public, protected, and private


...


Become familiar with the three basic principles
of object-oriented design


...


Become aware of the restrictions on operator overloading


...


Learn about friend functions


...


Discover how to overload various operators


...


Explore how to construct function templates and class templates

60 |

Chapter 2: Object-Oriented Design (OOD) and C++

Chapter 1 introduced classes, abstract data types (ADT), and ways to implement ADT in
C++
...
An object,
therefore, becomes a self-contained entity
...

In addition to implementing ADT, classes have other features
...
This important feature encourages code reuse
...
The main features associated with a part-time
employee are the name, pay rate, and number of hours worked
...
Every part-time employee
is a person
...

Of course, we do not want to make the necessary changes directly to the class
personType—that is, edit the class personType, and add and/or delete members
...
For
example, because the class personType already has data members to store the first
name and last name, we will not include any such members in the class
partTimeEmployee
...
(We will design such a class in Example 2-2
...
The class clockType has three data members to store hours,
minutes, and seconds
...
In this case, we would likely extend the
definition of the class clockType and create a class, extClockType, to accommodate
this new information
...
In C++, the mechanism that
allows us to accomplish this task is the principle of inheritance
...
’’
Inheritance lets us create new classes from existing classes
...
The derived class inherits the properties of the base classes
...

Each derived class, in turn, becomes a base class for a future derived class
...
In a single inheritance,

Inheritance |

61

the derived class is derived from a single base class; in a multiple inheritance, the derived
class is derived from more than one base class
...

Inheritance can be viewed as a treelike, or hierarchical, structure wherein a base class is
shown with its derived classes
...


shape

circle

rectangle

square

FIGURE 2-1

Inheritance hierarchy

In this diagram, shape is the base class
...
Every circle and
every rectangle is a shape
...

The general syntax of a derived class is:
class className: memberAccessSpecifier baseClassName
{
member list
};

where memberAccessSpecifier is public, protected, or private
...
(We
discuss protected inheritance later in this chapter
...
The following statements specify that
the class circle is derived from shape, and it is a public inheritance:
class circle: public shape
{

...


...


...

};

This is a private inheritance
...
So any object of type circle cannot directly
access these members
...


...

};

That is, if we do not use either the memberAccessSpecifier public or private, the
public members of a base class are inherited as private members
...

1
...

In other words, when you write the definitions of the member functions
of the derived class, you cannot directly access the private members
of the base class
...
The public members of a base class can be inherited either as public
members or as private members by the derived class
...

3
...

4
...
That is, in the derived class, you can have a member function with
the same name, number, and types of parameters as a function in the
base class
...

5
...
Similarly, the member functions of the base class (unless
redefined) are also member functions of the derived class
...
)
The next sections describe two important issues related to inheritance
...
While discussing

Inheritance |

63

this issue, we also address how to access the private (data) members of the base class in the
derived class
...
The constructor of a derived class cannot directly access the private member variables of the base
class
...


Redefining (Overriding) Member Functions of the Base Class
Suppose that a class derivedClass is derived from the class baseClass
...
It then
follows that the member variables of the class derivedClass are its own member
variables, together with the member variables of baseClass
...

Now derivedClass contains member variables in addition to the member variables
inherited from baseClass
...
You can give any name to this function
...
This is called redefining (or overriding) the member function of the
base class
...

To redefine a public member function of a base class in the derived class, the
corresponding function in the derived class must have the same name, number, and types
of parameters
...
If the corresponding
functions in the base class and the derived class have the same name but different sets
of parameters, this is function overloading in the derived class, which is also allowed
...
S
...

//***************************************************************
class rectangleType
{
public:
void setDimension(double l, double w);
//Function to set the length and width of the rectangle
...

//Postcondition: The value of length is returned
...

//Postcondition: The value of width is returned
...

//Postcondition: The area of the rectangle is calculated
//
and returned
...

//Postcondition: The perimeter of the rectangle is
//
calculated and returned
...

rectangleType();
//default constructor
//Postcondition: length = 0; width = 0;
rectangleType(double l, double w);
//constructor with parameters
//Postcondition: length = l; width = w;
private:
double length;
double width;
};

Figure 2-2 shows the UML class diagram of the class rectangleType
...
S
...

//***************************************************************
class boxType: public rectangleType
{
public:
void setDimension(double l, double w, double h);
//Function to set the length, width, and height of the box
...

//Postcondition: The value of height is returned
...

//Postcondition: The surface area of the box is
//
calculated and returned
...

//Postcondition: The volume of the box is calculated and
//
returned
...

boxType();
//Default constructor
//Postcondition: length = 0; width = 0; height = 0;
boxType(double l, double w, double h);
//Constructor with parameters
//Postcondition: length = l; width = w; height = h;
private:
double height;
};

Inheritance |

67

Figure 2-3 shows the UML class diagram of the class boxType and the inheritance
hierarchy
...
Therefore, all public members of the class rectangleType are public members of the
class boxType
...

In general, while writing the definitions of the member functions of a derived class to
specify a call to a public member function of the base class, we do the following:


If the derived class overrides a public member function of the base class,
then to specify a call to that public member function of the base class, you
use the name of the base class, followed by the scope resolution operator,
::, followed by the function name with the appropriate parameter list
...

(See the following note for member functions of the base class that are
overloaded in the derived class
...
For example, the
class boxType overloads the member function setDimension of the class
rectangleType
...
)

68 |

Chapter 2: Object-Oriented Design (OOD) and C++

Next, let us write the definition of the member function print of the class boxType
...
The
member function print of the class boxType prints the values of these member
variables
...
Therefore, when writing the definition of the function print
of the class boxType, we cannot access length and width directly
...
Therefore, when writing the definition of the member function print of the class boxType, we first
call the member function print of the class rectangleType to print
the values of length and width
...

To call the member function print of rectangleType in the definition of the member
function print of boxType, we must use the following statement:
rectangleType::print();

This statement ensures that we call the member function print of the base class
rectangleType, not of the class boxType
...

The definition of the function setDimension is as follows:
void boxType::setDimension(double l, double w, double h)
{
rectangleType::setDimension(l, w);

}

if (h >= 0)
height = h;
else
height = 0;

Notice that in the preceding definition of the function setDimension, a call to the
member function setDimension of the class rectangleType is preceded by the
name of the class and the scope resolution operator, even though the class boxType
overloads—not overrides—the function setDimension
...

To determine the surface area of a box, we need to access the length and width of the
box, which are declared as private members of the class rectangleType
...
Because the class boxType does not
contain any member functions that have the names getLength or getWidth, we call
these member functions of the class rectangleType without using the name of the
base class
...

To determine the volume of a box, you multiply the length, width, and height of the
box, or multiply the area of the base of the box by its height
...
To do this, you can use the
member function area of the class rectangleType to determine the area of the base
...


Constructors of Derived and Base Classes
A derived class can have its own private member variables, and so a derived class can
explicitly include its own constructors
...
When we declare a derived class object, this object inherits the
members of the base class, but the derived class object cannot directly access the private
(data) members of the base class
...
That is, the member functions of a derived class cannot directly access the private
members of the base class
...
Thus, when a
derived class object is declared, it must also automatically execute one of the constructors
of the base class
...
This is, in fact, what happens
...

In the preceding section, we defined the class rectangleType and derived the class
boxType from it
...
Let us now discuss how to write the definitions of the constructors of the class boxType
...
The class
boxType has three member variables: length, width, and height
...

First, let us write the definition of the default constructor of the class boxType
...

Because the class rectangleType contains the default constructor, when writing
the definition of the default constructor of the class boxType, we do not specify any
constructor of the base class
...
0;
}

Next, we discuss how to write the definitions of constructors with parameters
...

Consider the following definition of the constructor with parameters of the class
boxType:
boxType::boxType(double l, double w, double h)
: rectangleType(l, w)
{
if (h >= 0)
height = h;
else
height = 0;
}

In this definition, we specify the constructor of rectangleType with two parameters
...


Inheritance |

71

Consider the following statements:
rectangleType myRectangle(5
...
0);
boxType myBox(6
...
0, 4
...
Thus, the
object myRectangle has two member variables: length and width
...
Thus, the object myBox has three member
variables: length, width, and height
...


myBox
myRectangle

length
width

length

3
...
0

width

5
...
0

height

FIGURE 2-4

4
...
print();
cout << endl;
myBox
...
In the statement in Line 5, the function print associated with the class
boxType is executed
...
Thus, the
output of the statement in Line 3 is:
Length = 5
...
0

The output of the statement in Line 5 is:
Length = 6
...
0; Height = 4
...
Therefore, a derived class can also have a
constructor with default parameters
...
(To save space, these definitions have no
documentation
...


Inheritance |

73

Suppose that a base class, baseClass, has private member variables and
constructors
...
Therefore, the member
variables of derivedClass are the ones inherited from baseClass
...
To guarantee
the initialization of the inherited member variables of an object of type
derivedClass, even though derivedClass has no member variables, it must
have the appropriate constructors
...
Therefore,
when you write the definition of the constructor (with parameters) of derivedClass,
the heading of the definition of the constructor contains a call to an appropriate
constructor (with parameters) of baseClass, and the body of the constructor is
empty—that is, it contains only the opening and closing braces
...
There
are both full-time employees and part-time employees
...
Suppose that you want to
define a class to keep track of a part-time employee’s information such as name, pay
rate, and hours worked
...
Because every employee is a person, and Example 1-12 (Chapter 1)
defined the class personType to store the first name and the last name together with
the necessary operations on name, we can define a class partTimeEmployee based on
the class personType
...

//***************************************************************
// Author: D
...
Malik
//
// class partTimeEmployee
// This class is derived from the class personType and it
// specifies the members to implement the properties of a
// part-time employee
...

//Postcondition: Outputs: firstName lastName wages are $$$$
...

//Postcondition: Pay is calculated and returned
...

//Postcondition: firstName = first; lastName = last;
//
payRate = rate; hoursWorked = hours
partTimeEmployee(string first = "", string last = "",
double rate = 0, double hours = 0);
//Constructor with parameters
//Sets the first name, last name, payRate, and hoursWorked
//according to the parameters
...

//Postcondition: firstName = first; lastName = last;
//
payRate = rate; hoursWorked = hours
private:
double payRate;
//variable to store the pay rate
double hoursWorked; //variable to store the hours worked
};

Figure 2-5 shows the UML class diagram of the class partTimeEmployee and the
inheritance hierarchy
...

To define new classes, you create new header files
...
Thus, to create new classes based on the
previously defined classes, the header files of the new classes contain commands that tell
the computer where to look for the definitions of the base classes
...
h
...
h—must contain the preprocessor directive:
#include "personType
...
To be specific, the header file
partTimeEmployee
...
h"
//***************************************************************
// Author: D
...
Malik
//
// class partTimeEmployee
// This class is derived from the class personType and it
// specifies the members to implement the properties of a
// part-time employee
...

//Postcondition: Outputs: firstName lastName wages are $$$$
...

//Postcondition: Pay is calculated and returned
...

//Postcondition: firstName = first; lastName = last;
//
payRate = rate; hoursWorked = hours
partTimeEmployee(string first = "", string last = "",
double rate = 0, double hours = 0);
//Constructor with parameters
//Sets the first name, last name, payRate, and hoursWorked
//according to the parameters
...

//Postcondition: firstName = first; lastName = last;
//
payRate = rate; hoursWorked = hours
private:
double payRate;
//variable to store the pay rate
double hoursWorked; //variable to store the hours worked
};

The definitions of the member functions can be placed in a separate file (whose extension
is
...
Recall that to include a system-provided header file, such as iostream, in a user
program, you enclose the header file between angular brackets; to include a user-defined
header file in a program, you enclose the header file between double quotation marks
...
To include
a header file in a program, you use the preprocessor command include
...
Consider the
following header file:
//Header file test
...
h includes the file test
...
To be specific, suppose that the header file testA
...
h
#include "test
...


...


Now consider the following program code:
//Program headerTest
...
h"
#include "testA
...


...


When the program headerTest
...
The preprocessor includes first the header file test
...
h
...
h is included, because it contains the preprocessor
directive #include "test
...
h is included twice in the program
...
h results in compile-time errors, such as the
identifier ONE already being declared
...
h has already defined the variables ONE and TWO
...

Let us first rewrite the header file test
...

//Header file test
...
#ifndef H_test means ‘‘if not defined H_test’’
b
...
#endif means ‘‘end if’’
Here H_test is a preprocessor identifier
...
If the header file test
...
In fact, all header files are written using similar preprocessor commands
...
Only member functions of that class can access the private members
...

However, it is sometimes necessary for a derived class to access a private member of a
base class
...
Recall that the members of a class are classified into three categories: public,
private, and protected
...
Thus, the accessibility of a protected
member of a class is in between public and private
...

To summarize, if a derived class needs to access a member of a base class, that member of
the base class should be declared under the member access specifier protected
...
Then B cannot directly access the private
members of A
...
What about the
public and protected members of A? This section gives the rules that generally apply
when accessing the members of a base class
...


...

};

In this statement, memberAccessSpecifier is either public, protected, or private
...
If memberAccessSpecifier is public—that is, the inheritance is
public—then
The public members of A are public members of B
...

b
...
They
can be directly accessed by the member functions (and friend
functions) of B
...
The private members of A are hidden to B
...


a
...
If memberAccessSpecifier is protected—that is, the inheritance is
protected—then
The public members of A are protected members of B
...

b
...
They
can be accessed by the member functions (and friend functions) of B
...
The private members of A are hidden to B
...

3
...


The public members of A are private members of B
...

b
...
They can
be accessed by the member functions (and friend functions) of B
...
The private members of A are hidden to B
...


a
...


Composition
Composition is another way to relate two classes
...
Composition is a ‘‘has-a’’ relationship; for
example, ‘‘every person has a date of birth
...
The class personType
stores a person’s first name and last name
...
Because every person has a personal ID and a date of birth, we can
define a new class, called personalInfoType, in which one of the members is an object
of type personType
...

First, we define another class, dateType, to store only a person’s date of birth, and then
construct the class personalInfoType from the classes personType and dateType
...


2

80 |

Chapter 2: Object-Oriented Design (OOD) and C++

To define the class dateType, we need three data members to store the month, day
number, and year
...
The following statements define the class dateType:
//************************************************************
// Author: D
...
Malik
//
// class dateType
// This class specifies the members to implement a date
...

//The member variables dMonth, dDay, and dYear are set
//according to the parameters
...

//Postcondition: The value of dDay is returned
...

//Postcondition: The value of dMonth is returned
...

//Postcondition: The value of dYear is returned
...

dateType(int month = 1, int day = 1, int year = 1900);
//Constructor to set the date
//The member variables dMonth, dDay, and dYear are set
//according to the parameters
...
If
//
no values are specified, the default values are used to
//
initialize the member variables
...


dateType
–dMonth: int
–dDay: int
–dYear: int
+setDate(int, int, int): void
+getDay() const: int
+getMonth() const: int
+getYear() const: int
+printDate() const: void
+dateType(int = 1, int = 1, int = 1900)

FIGURE 2-6

UML class diagram of the class dateType

The definitions of the member functions of the class dateType are as follows:
void dateType::setDate(int month, int day, int year)
{
dMonth = month;
dDay = day;
dYear = year;
}

The definition of the function setDate, before storing the date into the data members,
does not check whether the date is valid
...
In Programming Exercise 2 at the end of this chapter, you
are asked to rewrite the definition of the function setDate so that the date is validated
before storing it in the data members
...
In Programming
Exercise 2 at the end of this chapter, when you rewrite the definition of the function
setDate to validate the date, and the constructor uses the function setDate, the date set
by the constructor will also be validated
...
S
...

//************************************************************
class personalInfoType
{
public:
void setPersonalInfo(string first, string last, int month,
int day, int year, int ID);
//Function to set the personal information
...

//Postcondition: firstName = first; lastName = last;
//
dMonth = month; dDay = day; dYear = year;
//
personID = ID;
void printPersonalInfo () const;
//Function to print the personal information
...

//Postcondition: firstName = first; lastName = last;
//
dMonth = month; dDay = day; dYear = year;
//
personID = ID;
//
If no values are specified, the default values are
//
used to initialize the member variables
...


personalInfoType
–name: personType
–bDay: dateType
–personID: int
setPersonalInfo(string, string, int, int,
int, int): void
printPersonalInfo() const: void
personalInfoType(string = "", string = "",
int = 1, int = 1,
int = 1900, int = 0)

FIGURE 2-7

personalInfoType

personType

dateType

UML class diagram of the class personalInfoType and composition (aggregation)

Before we give the definition of the member functions of the class personalInfoType,
let us discuss how the constructors of the objects bDay and name are invoked
...
Suppose that we have the following statement:
personalInfoType student;

When the object student enters its scope, the objects bDay and name, which are
members of student, also enter their scopes; as a result, one of their constructors is
executed
...
Recall that constructors do not have a type and
so cannot be called like other functions
...
The following statements illustrate how to pass arguments to the constructors
of the member objects:
personalInfoType::personalInfoType(string first, string last, int month,
int day, int year, int ID)
: name(first, last), bDay(month, day, year)
{

...


...
Thus, in our case, the object name
is initialized first, then bDay, and, finally, student
...
setName(first,last);
bDay
...
print();
cout << "'s date of birth is ";
bDay
...
In the
case of composition, use the member object name to invoke its own constructor
...
The ability to combine data and operations is called
encapsulation
...
Chapter 1
defined the abstract data type (ADT) and described how classes in C++ implement
ADTs
...
Inheritance, the second principle
of OOD, encourages code reuse
...

First we discuss polymorphism via operator overloading, and then via templates
...

We will simplify function overloading through the use of templates, called function
templates
...
But first let us see why you
would want to overload operators
...
It also showed how you can
use the class clockType to represent the time of day in a program
...

Consider the following statements:
clockType myClock(8,23,34);
clockType yourClock(4,5,30);

The first statement declares myClock to be an object of type clockType and initializes
the data members hr, min, and sec of myClock to 8, 23, and 34, respectively
...

Now consider the following statements:
myClock
...
incrementSeconds();
if (myClock
...


...


The first statement prints the value of myClock in the form hr:min:sec
...
The third statement checks
whether the value of myClock is the same as the value of yourClock
...
However, if we can use the insertion operator << to
output the value of myClock, the increment operator ++ to increment the value of
myClock by one second, and relational operators for comparison, we can enhance the
flexibility of C++ considerably and can improve code readability
...


...


Recall that the only built-in operations on classes are the assignment operator and the
member selection operator
...
However, C++ allows the programmer to extend the definitions of most of the
operators so that operators such as relational operators, arithmetic operators, insertion

2

86 |

Chapter 2: Object-Oriented Design (OOD) and C++

operators for data output, and extraction operators for data input can be applied to classes
...
In addition to operator
overloading, this chapter discusses function overloading
...
If both operands of / are integers, the result
is an integer; otherwise, the result is a floating-point number
...
The
operator << is used as both a stream insertion operator and a left shift operator
...
These
are examples of operator overloading
...
The results of + and – are different
for integer arithmetic, floating-point arithmetic, and pointer arithmetic
...
It does not allow the user to create new operators
...

To overload an operator, you must write functions (that is, the header and body)
...
For example, the name of the function to overload the
operator >= is
operator>=

Operator function: The function that overloads an operator
...

The syntax of the heading for an operator function is as follows:
returnType operator operatorSymbol(arguments)

In C++, operator is a reserved word
...
To overload an operator for a class, you do the following:
1
...

2
...

Certain rules must be followed when you include an operator function in a class
definition
...


Operator Overloading |

87

Overloading an Operator: Some Restrictions
When overloading an operator, keep the following in mind:



You cannot change the precedence of an operator
...
(For example, the associativity of
the arithmetic operator + is from left to right and it cannot be changed
...

• You cannot change the number of arguments that an operator takes
...
Only existing operators can be overloaded
...



...

• Operators can be overloaded either for objects of the user-defined type,
or for a combination of objects of the user-defined type and objects of
the built-in type
...
Sometimes it is necessary for a function member to refer to the object as a
whole, rather than the object’s individual data members
...
In C++, this is a reserved word
...
When an object invokes a member function,
the member function references the pointer this of the object
...
Further suppose that the
definition of funcOne looks like the following:
test test::funcOne()
{

...


...
funcOne();

copies the value of the object x into the object y; that is, the data members of x are copied
into the corresponding data members of y
...


2

88 |

Chapter 2: Object-Oriented Design (OOD) and C++

The following example illustrates how the pointer this works
...
Here we extend the definition of the class personType to individually set a
person’s first name and last name, and then return the entire object
...
S
...

//************************************************************
class personType
{
public:
void print() const;
//Function to output the first name and last name in
//the form firstName lastName
void setName(string first, string last);
//Function to set firstName and lastName according to the
//parameters
...

//Postcondition: firstName = first
//
After setting the first name, a reference to the
//
object, that is, the address of the object, is
//
returned
...

//Postcondition: lastName = last
//
After setting the last name, a reference to the object,
//
that is, the address of the object, is returned
...

//Postcondition: The value of firstName is returned
...

//Postcondition: The value of lastName is returned
...

//Postcondition: firstName = first; lastName = last

Operator Overloading |

89

private:
string firstName; //variable to store the first name
string lastName; //variable to store the last name
};

Notice that in this definition of the class personType, we replace the default constructor and the constructor with parameters by one constructor with default parameters
...
The definitions of the functions
setFirstName and setLastName are as follows:
personType& personType::setLastName(string last)
{
lastName = last;
}

return *this;

personType& personType::setFirstName(string first)
{
firstName = first;
}

return *this;

The following program shows how to use the class personType
...
h
...
S
...
h"

//Line 1
//Line 2
//Line 3

using namespace std;

//Line 4

int main()
{
personType student1("Lisa", "Smith");
personType student2;
personType student3;

//Line
//Line
//Line
//Line
//Line

cout << "Line 10 -- Student 1: ";
student1
...
setFirstName("Shelly")
...
print();
cout << endl;

//Line 14
//Line 15
//Line 16

2

90 |

Chapter 2: Object-Oriented Design (OOD) and C++

student3
...
print();
cout << endl;

//Line 18
//Line 19
//Line 20

student3
...
print();
cout << endl;
}

//Line 17

//Line 22
//Line 23
//Line 24

return 0;

//Line 25
//Line 26

Sample Run:
Line
Line
Line
Line

10
14
18
22

-----

Student
Student
Student
Student

1:
2:
3:
3:

Lisa Smith
Shelly Malik
Cindy
Cindy Tomek

The statements in Lines 7, 8, and 9 declare and initialize the objects student1,
student2, and student3, respectively
...
The statement in Line 11 outputs the value of student1
(see Line 10 in the sample run, which contains the output of Lines 10, 11, and 12)
...
In the statement
student2
...
setLastName("Malik");

first the expression
student2
...
This
expression sets the first name to "Shelly" and returns a reference to the object, which
is student2
...
setLastName("Malik")

which sets the last name of student2 to "Malik"
...
The statement in Line 17 sets the first name of the object student3
to "Cindy", and ignores the value returned
...
Notice the output in Line 18
...
The last name of
student3 is still empty, which was set by the statement in Line 9 when student3 was
declared
...


Operator Overloading |

91

Friend Functions of Classes
A friend function of a class is a nonmember function of the class, but has access to all
the members (public or non-public) of the class
...

The word friend appears only in the function prototype in the class definition, not in
the definition of the friend function
...


...

};

In the definition of the class classIllusFriend, two is declared as a friend of the
class classIllusFriend
...
When you write the definition of the function two, any object of
type classIllusFriend—which is either a local variable of two or a formal parameter of
two—can access its private members within the definition of the function two
...
) Moreover, because a friend function is not a member of a
class, its declaration can be placed within the private, protected, or public part of the
class
...

DEFINITION OF A friend FUNCTION
When writing the definition of a friend function, the name of the class and the scope
resolution operator do not precede the name of the friend function in the function
heading
...
Thus, the definition of the function two in the previous class
classIllusFriend is as follows:
void friendFunc(/*parameters*/)
{

...


...

The next section illustrates the difference between a member function and a nonmember
function (friend function), when we overload some of the operators for a specific class
...


2

92 |

Chapter 2: Object-Oriented Design (OOD) and C++

EXAMPLE 2-4
Consider the following class:
class classIllusFriend
{
friend void friendFunc(classIllusFriend cIFObject);
public:
void print();
void setx(int a);
private:
int x;
};

In the definition of the class classIllusFriend, friendFunc is declared as a
friend function
...
x = 45;

//Line 4

localTwoObject
...
x
endl;

cIFObject
...
print();

}

//Line 7
//Line 8

cout << "Line 9: In friendFunc accessing "
<< "private member variable " << "x = "
<< cIFObject
...
In the statement in Line 4, the
object localTwoObject accesses its private member variable x and sets its value to 45
...
Similarly, in the statement in Line 7, the formal parameter
cIFObject accesses its private member variable x and sets its value to 88
...
The statement in Line 6 outputs the value of
the private member variable x of localTwoObject by directly accessing x
...
The
function friendFunc also prints the value of x by using the function print (see the
statements in Lines 6 and 9)
...
setx(32);
cout << "Line 15: aObject
...
print();
cout << endl;

//Line 15
//Line 16
//Line 17

cout << "*~*~*~* Testing friendFunc *~*~*~*"
<< endl << endl;

//Line 18

friendFunc(aObject);
}

//Line 14

//Line 19

return 0;

//Line 20
//Line 21

Sample Run:
Line 15: aObject
...
The statement in Line 19 calls the
function friendFunc (a friend function of the class classIllusFriend) and passes
the object aObject as an actual parameter
...


2

94 |

Chapter 2: Object-Oriented Design (OOD) and C++

Operator Functions as Member Functions and Nonmember
Functions
Earlier in this chapter we stated that certain rules must be followed when you include an
operator function in the definition of a class
...

Most operator functions can be either member functions or nonmember functions—that
is, friend functions of a class
...
The function that overloads any of the operators (), [], ->, or = for a
class must be declared as a member of the class
...
Suppose that an operator op is overloaded for a class—say, opOverClass
...
)
If the leftmost operand of op is an object of a different type (that is,
not of type opOverClass), the function that overloads the operator
op for opOverClass must be a nonmember—that is, a friend of the
class opOverClass
...
If the operator function that overloads the operator op for the
class opOverClass is a member of the class opOverClass,
then when applying op on objects of type opOverClass, the leftmost operand of op must be of type opOverClass
...


You must follow these rules when including an operator function in a class definition
...

Except for certain operators noted previously, operators can be overloaded either as
member functions or as nonmember functions
...

To facilitate our discussion of operator overloading, we will use the class
rectangleType, defined earlier in this chapter
...

C++ consists of both binary and unary operators
...
The next few sections discuss how to overload various binary and
unary operators
...
This operator can be overloaded as
either a member function of the class or as a friend function
...

OVERLOADING THE BINARY OPERATORS AS MEMBER FUNCTIONS
Suppose that # is overloaded as a member function of the class rectangleType
...
operator#(yourRectangle)

This expression clearly shows that the function operator# has only one parameter,
which is yourRectangle
...
Thus, the first parameter of
operator# is the object that is invoking the function operator#, and the second
parameter is passed as a parameter to this function
...


Function Prototype (to be included in the definition of the class):
returnType operator#(const className&) const;

where # stands for the binary operator, arithmetic or relational, to be overloaded;
returnType is the type of value returned by the function; and className is the name
of the class for which the operator is being overloaded
...


EXAMPLE 2-5
Let us overload +, *, ==, and != for the class rectangleType
...

class rectangleType
{
public:
void setDimension(double l, double w);
double getLength() const;
double getWidth() const;
double area() const;
double perimeter() const;
void print() const;
rectangleType operator+(const rectangleType&) const;
//Overload the operator +
rectangleType operator*(const rectangleType&) const;
//Overload the operator *
bool operator==(const rectangleType&) const;
//Overload the operator ==
bool operator!=(const rectangleType&) const;
//Overload the operator !=
rectangleType();
rectangleType(double l, double w);
private:
double length;
double width;
};

The definition of the function operator+ is as follows:
rectangleType rectangleType::operator+
(const rectangleType& rectangle) const
{
rectangleType tempRect;
tempRect
...
length;
tempRect
...
width;
}

return tempRect;

Operator Overloading |

97

Notice that operator+ adds the corresponding lengths and widths of the two rectangles
...
length = length * rectangle
...
width = width * rectangle
...

Two rectangles are equal if their lengths and widths are equal
...
length &&
width == rectangle
...
Therefore, the definition of the function to overload the operator != is as follows:
bool rectangleType::operator!=
(const rectangleType& rectangle) const
{
return (length != rectangle
...
width);
}

OVERLOADING THE BINARY OPERATORS (ARITHMETIC OR RELATIONAL) AS NONMEMBER
FUNCTIONS
Suppose that # represents the binary operator (arithmetic or relational) that is to be
overloaded as a nonmember function of the class rectangleType
...
This expression also clearly
shows that the function operator# is neither a member of the object myRectangle nor
a member of the object yourRectangle
...

To include the operator function operator# as a nonmember function of the class in the
definition of the class, the reserved word friend must appear before the function
heading
...

GENERAL SYNTAX TO OVERLOAD THE BINARY (ARITHMETIC OR RELATIONAL) OPERATORS AS
NONMEMBER FUNCTIONS
This section describes the general form of the functions that overload binary operators as
nonmember functions of a class
...

Function Definition:
returnType operator#(const className& firstObject,
const className& secondObject)
{
//algorithm to perform the operation
}

return value;

Overloading the Stream Insertion (<<) and Extraction (>>)
Operators
The operator function that overloads the insertion operator, <<, or the extraction operator,
>>, for a class must be a nonmember function of that class for the following reason
...
Because the leftmost operand of << is not an object of
type rectangleType, the operator function that overloads the insertion operator for
rectangleType must be a nonmember function of the class rectangleType
...


Operator Overloading |

99

OVERLOADING THE STREAM INSERTION OPERATOR (<<)
The general syntax to overload the stream insertion operator, <<, for a class is described next
...

//osObject <<
...

return osObject;

In this function definition:



Both parameters are reference parameters
...

• The second parameter is a const reference to a particular class
...

OVERLOADING THE STREAM EXTRACTION OPERATOR (>>)
The general syntax to overload the stream extraction operator, >>, for a class is described next
...

//isObject >>
...

return isObject;

We note the following in this function definition
...

The first parameter—that is, isObject—is a reference to an istream object
...
The data
read will be stored in the object
...

Example 2-6 shows how the stream insertion and extraction operators are overloaded for
the class rectangleType
...

EXAMPLE 2-6
The definition of the class rectangleType and the definitions of the operator functions are as follows:
#include
using namespace std;
class rectangleType
{
//Overload the stream insertion and extraction operators
friend ostream& operator<< (ostream&, const rectangleType &);
friend istream& operator>> (istream&, rectangleType &);
public:
void setDimension(double l, double w);
double getLength() const;
double getWidth() const;
double area() const;
double perimeter() const;
void print() const;
rectangleType operator+(const rectangleType&) const;
//Overload the operator +
rectangleType operator*(const rectangleType&) const;
//Overload the operator *
bool operator==(const rectangleType&) const;
//Overload the operator ==
bool operator!=(const rectangleType&) const;
//Overload the operator !=
rectangleType();
rectangleType(double l, double w);
private:
double length;
double width;
};
//The definitions of the functions operator+, operator*, operator==,
//operator!=, and the constructor are the same as in Example 2-5
...
length
<< "; Width = " << rectangle
...
length >> rectangle
...
(We assume that the definition of the class
rectangleType is in the header file rectangleType
...
)
//****************************************************************
// Author: D
...
Malik
//
// This program shows how to use the modified class rectangleType
...
h"

//Line 2

using namespace std;

//Line 3

int main()
{
rectangleType myRectangle(23, 45);
rectangleType yourRectangle;

//Line
//Line
//Line
//Line

4
5
6
7

cout << "Line 8: myRectangle: " << myRectangle
<< endl;

//Line 8

cout << "Line 9: Enter the length and width "
<< "of a rectangle: ";
cin >> yourRectangle;
cout << endl;

//Line 9
//Line 10
//Line 11

cout << "Line 12: yourRectangle: "
<< yourRectangle << endl;

//Line 12

cout <<
<<
cout <<
<<
}

"Line 13: myRectangle + yourRectangle: "
myRectangle + yourRectangle << endl;
"Line 14: myRectangle * yourRectangle: "
myRectangle * yourRectangle << endl;

return 0;

//Line 13
//Line 14
//Line 15
//Line 16

102 |

Chapter 2: Object-Oriented Design (OOD) and C++

Sample Run: In this sample run, the user input is shaded
...
The statement in Line 8 outputs the value of
myRectangle using cout and the insertion operator
...
The statement in Line 12
outputs the value of yourRectangle using cout and the insertion operator
...
Similarly, the cout statement in Line 14 multiplies the lengths and
widths of myRectangle and yourRectangle and outputs the result
...


OVERLOADING UNARY OPERATORS
The process of overloading unary operators is similar to the process of overloading binary
operators
...
Therefore,
to overload a unary operator for a class we do the following
...

If the operator function is a nonmember—that is, a friend function of
the class—it has one parameter
...
Certain
operators must be overloaded as member functions of the class, and some must be
overloaded as nonmember (friend) functions
...

If you overload + as a member function, the operator + has direct access to the data
members of one of the objects, and you need to pass only one object as a parameter
...
Therefore, overloading + as a nonmember could require additional memory
and computer time to make a local copy of the data
...


Programming Example: Complex Numbers

PROGRAMMING EXAMPLE:

|

103

Complex Numbers

A number of the form a + ib, where i2 ¼ -1, and a and b are real numbers, is called a
complex number
...
Complex
numbers can also be represented as ordered pairs (a, b)
...
In this
example, we will construct a data type, complexType, that can be used to process
complex numbers
...
We will also overload the operators + and * to
perform addition and multiplication of complex numbers
...

#ifndef H_complexNumber
#define H_complexNumber
//*****************************************************************
// Author: D
...
Malik
// class complexType
...

//*****************************************************************
#include
using namespace std;
class complexType
{
//Overload the stream insertion and extraction operators
friend ostream& operator<<(ostream&, const complexType&);
friend istream& operator>>(istream&, complexType&);
public:
void setComplex(const double& real, const double& imag);
//Function to set the complex numbers according to
//the parameters
...

//Postcondition: real = realPart; imag = imaginaryPart;

2

104 |

Chapter 2: Object-Oriented Design (OOD) and C++

complexType(double real = 0, double imag = 0);
//Constructor
//Initializes the complex number according to the parameters
...

The definitions of most of these functions are straightforward
...

To output a complex number in the form:
(a, b)

where a is the real part and b is the imaginary part, clearly the algorithm is as follows:
a
...

c
...

e
...

Output the real part
...

Output the imaginary part
...


Therefore, the definition of the function operator<< is as follows:

Programming Example: Complex Numbers

|

105

ostream& operator<<(ostream& osObject, const complexType& complex)
{
osObject << "(";
//Step a
osObject << complex
...
imaginaryPart; //Step d
osObject << ")";
//Step e
}

return osObject;

//return the ostream object

Next, we discuss the definition of the function to overload the stream extraction
operator, >>
...

Clearly, the algorithm to read this complex number is as follows:
a
...

c
...

e
...

Read and store the real part
...

Read and store the imaginary part
...


Following these steps, the definition of the function operator>> is as follows:
istream& operator>>(istream& isObject, complexType& complex)
{
char ch;
isObject
isObject
isObject
isObject
isObject
}

>>
>>
>>
>>
>>

ch;
complex
...
imaginaryPart;
ch;

return isObject;

//Step
//Step
//Step
//Step
//Step

a
b
c
d
e

//return the istream object

The definitions of the other functions are as follows:
bool complexType::operator==
(const complexType& otherComplex) const
{
return (realPart == otherComplex
...
imaginaryPart);
}

2

106 |

Chapter 2: Object-Oriented Design (OOD) and C++

//Constructor
complexType::complexType(double real, double imag)
{
realPart = real;
imaginaryPart = imag;
}
//Function to set the complex number after the object
//has been declared
...
realPart = realPart + otherComplex
...
imaginaryPart = imaginaryPart
+ otherComplex
...
realPart = (realPart * otherComplex
...
imaginaryPart);
temp
...
imaginaryPart)
+ (imaginaryPart * otherComplex
...
S
...

//**********************************************************
#include
#include "complexType
...

Line 9: Num1 = (23, 34)
Line 10: Num2 = (0, 0)
Line 11: Enter the complex number in the form (a, b): (3, 4)
Line
Line
Line
Line

14:
16:
17:
18:

New value of num2 = (3, 4)
Num3 = (26, 38)
(23, 34) + (3, 4) = (26, 38)
(23, 34) * (3, 4) = (-67, 194)

2

108 |

Chapter 2: Object-Oriented Design (OOD) and C++

Function Overloading
The previous section discussed operator overloading
...
The types of arguments used with an operator determine the action to take
...

Recall that a class can have more than one constructor, but all constructors of a class have the
same name, which is the name of the class
...

Overloading a function refers to the creation of several functions with the same name
...
The types of parameters determine which function to execute
...
Both items
can be integers, floating-point numbers, characters, or strings
...
These functions all
perform similar operations
...
Thus, you can write the previous function prototypes simply as
int larger(int x, int y);
char larger(char first, char second);
double larger(double u, double v);
string larger(string first, string second);

If the call is larger(5,3), for example, the first function executes
...

For function overloading to work, we must give the definition of each function
...


Templates
Templates are very powerful features of C++
...
The syntax we use for templates is as follows:
template
declaration;

Templates |

109

where Type is the type of data, and declaration is either a function declaration or a
class declaration
...
The word class in the heading
refers to any user-defined type or built-in type
...

Just as variables are parameters to functions, types (that is, data types) are parameters to
templates
...
To implement the function larger,
we need to write four function definitions for the data type: one for int, one for char, one
for double, and one for string
...
C++
simplifies the process of overloading functions by providing function templates
...
It is used to specify the
type of parameters to the function and the return type of the function, and to declare
variables within the function
...
In the function
heading, the type of the formal parameters x and y is Type, which will be specified by the
type of the actual parameters when the function is called
...
Because 5 and 6 are of type int, the data type
int is substituted for Type and the compiler generates the appropriate code
...


2

110 |

Chapter 2: Object-Oriented Design (OOD) and C++

The following example illustrates the use of function templates
...

//****************************************************************
// Author: D
...
Malik
//
// This program illustrates how to write and use a template in a
// program
...
6 and 3
...
6, 3
...
6 and 3
...
6
13: Larger of Hello and Happy = Hello

Class Templates
Like function templates, class templates are used to write a single code segment for a set of
related classes
...
If the list element type changes from int to, say, char, double, or
string, we need to write separate classes for each element type
...

Using class templates, we can create a generic class listType, and the compiler can
generate the appropriate source code for a specific implementation
...
For example, if the template parameter type is int, we can
generate a list to process integers; if the parameter type is string, we can generate a list
to process strings
...
To derive a specific list from this list and to add or rewrite
the operations, we declare the array containing the list elements and the length of the list
as protected
...
Suppose that you want to create a list to process integer
data
...
Similarly, the statement
listType stringList;

//Line 2

declares stringList to be a list of 100 components, with each component being of
type string
...
A template instantiation can
be created with either a built-in or user-defined type
...
Thus, when
giving the definitions of function members of a class template, we must follow the
definition of the function template
...


...

}

In the heading of the member function’s definition, elemType specifies the data type of
the list elements
...
The
object code was generated from the implementation file (independently of any client
code) and linked with the client code
...

Passing parameters to a function has an effect at run time, whereas passing a parameter to
a class template has an effect at compile time
...


Quick Review |

113

This problem has several possible solutions
...

Another alternative is to put the class definition and the definitions of the functions in
separate files (as usual), but include a directive to the implementation file at the end of the
header file (that is, the specification file)
...
For illustrative purposes, we will put the class definition and the function definitions in the same header file
...

2
...

4
...

6
...

8
...

10
...

12
...

14
...

16
...

Inheritance is an ‘‘is a’’ relationship
...

In single inheritance, the derived class is derived from only one existing
class, called the base class
...

The private members of a base class are private to the base class
...

The public members of a base class can be inherited either as public,
protected, or private by the derived class
...

A call to a base class’s constructor is specified in the heading of the
definition of the derived class’s constructor
...

Review the inheritance rules given in this chapter
...

In composition, a call to the constructor of the member objects is specified
in the heading of the definition of the class’s constructor
...

An operator that has different meanings with different data types is said to
be overloaded
...

Similarly, >> is used as a stream extraction operator and as a right shift
operator
...


2

114 |

17
...


Chapter 2: Object-Oriented Design (OOD) and C++

The function that overloads an operator is called an operator function
...

20
...

22
...


24
...

26
...

28
...

30
...

32
...

34
...

36
...

38
...


In C++, operator is a reserved word
...

Except for the assignment operator and the member selection operator, to
use an operator on class objects, that operator must be overloaded
...

When an operator is overloaded, its precedence cannot be changed, its
associativity cannot be changed, default arguments cannot be used, the
number of arguments that the operator takes cannot be changed, and
the meaning of how an operator works with built-in data types remains
the same
...
Only existing operators can be
overloaded
...

The operators that cannot be overloaded are
...

The pointer this refers to the object as a whole
...

A friend function is a nonmember of a class
...

In C++, friend is a reserved word
...

The binary operator function as a member of a class has only one parameter; as a nonmember of a class, it has two parameters
...

In C++, a function name can be overloaded
...

In C++, template is a reserved word
...

Using templates, you can write a single code segment for a set of related
classes—called the class template
...


|

115

A syntax of a template is
template
declaration;

41
...

43
...
The word class in the heading refers to any user-defined data type
or built-in data type
...

In a class template, the parameter elemType specifies how a generic class
template is to be customized to form a specific class
...

The heading of the function definition of func is
template
funcType cType::func(formal parameters)

44
...

Suppose cType is a class template, which can take int as a parameter
...


EXERCISES
1
...

a
...


c
...

e
...

g
...

i
...

The constructor of a derived class specifies a call to the constructor of
the base class using the name of the class
...
The constructor of
x specifies a call to the constructor of y by using the object name of
type y
...

In C++, all operators can be overloaded for user-defined data types
...

The function that overloads an operator is called the operator function
...

The precedence of an operator cannot be changed, but its associativity
can be changed
...

k
...

l
...

m
...

n
...

Draw a class hierarchy in which several classes are derived from a single base class
...
Give examples of data
and function members that can be added to the class employeeType
...

Consider the following class definition:
j
...

3
...


5
...


class bClass public aClass
{
public:
void print();
void set(int, int, int);
private:
int z;
};

b
...


117

Consider the following statements:
class yClass
{
public:
void one();
void two(int, int);
yClass();
private:
int a;
int b;
};
class xClass: public yClass
{
public:
void one();
xClass();
private:
int z;
};
yClass y;
xClass x;
a
...


The private members of yClass are public members of xClass
...
If a statement is
invalid, explain why
...


void yClass::one()
{
cout << a + b << endl;
}

ii
...
a = 15;
x
...


void xClass::one()
{
a = 10;
b = 15;
z = 30;
cout << a + b + z << endl;
}

iv
...


|

cout << y
...
b << " " << x
...

a
...


Write the definition of
private data members
Write the definition of
private data members

the default constructor of yClass so that the
of yClass are initialized to 0
...


2

118 |

Chapter 2: Object-Oriented Design (OOD) and C++

Write the definition of the member function two of yClass so that the
private data member a is initialized to the value of the first parameter
of two, and the private data member b is initialized to the value of the
second parameter of two
...


8
...


...

int main()
{
classA aObject;

}
9
...
setX(4);
return 0;

//Line
//Line
//Line
//Line

1
2
3
4

//Line 5
//Line 6
//Line 7

Consider the following code:
class one
{
public:
void print() const;
//Outputs the values of x and y
protected:
void setData(int u, int v);
//Postcondition: x = u; y = v;
private:
int x;
int y;
};
class two: public one
{
public:
void setData(int a, int b, int c);
//Postcondition: x = a; y = b; z = c;
void print() const;
//Outputs the values of x, y, and z
private:
int z;
};
a
...


Write the definition of the function setData of the class two
...


Exercises

10
...
print();
derivedObject
...


What is the output of the following program?
#include
using namespace std;
class baseClass
{
public:
void print()const;
int getX();
baseClass(int a = 0);
protected:
int x;
};
class derivedClass: public baseClass
{
public:
void print()const;
int getResult();
derivedClass(int a = 0, int b = 0);
private:
int y;
};
int main()
{
baseClass baseObject(7);
derivedClass derivedObject(3,8);
baseObject
...
print();

Exercises

|

121

cout << "**** " << baseObject
...
getResult() << endl;
}

return 0;

void baseClass::print() const
{
cout << "In base: x = " << x << endl;
}
baseClass::baseClass(int a)
{
x = a;
}
int baseClass::getX()
{
return x;
}
void derivedClass::print() const
{
cout << "In derived: x = " << x << ", y = " << y
<< ", x + y = " << x + y << endl;
}
int derivedClass::getResult()
{
return x + y;
}
derivedClass::derivedClass(int a, int b)
:baseClass(a)
{
y = b;
}
12
...


14
...


What is a friend function?
Suppose that the operator << is to be overloaded for a user-defined class
mystery
...
How many parameters does the function operator+ have?
Consider the following declaration:
class strange
{

...


...

b
...

c
...

d
...

Assume the declaration of Exercise 15
...


16
...

b
...

c
...

Find the error(s) in the following code:
a
...


class mystery
{

...

};
bool mystery::<=(mystery rightObj)
{

...


//Line 1

//Line 2

//Line 3

Find the error(s) in the following code:
class mystery
{

...

};

19
...

friend operator+ (mystery);
//Overload the binary operator +

...


//Line 1

//Line 1

//Line 2

How many parameters are required to overload the preincrement operator
for a class as a member function?

Exercises

21
...


23
...


How many parameters are required to overload the preincrement operator
for a class as a friend function?
How many parameters are required to overload the postincrement operator
for a class as a member function?
How many parameters are required to overload the postincrement operator
for a class as a friend function?
Find the error(s) in the following code:
template
class strange
{

...


//Line 1
//Line 2

//Line 3
//Line 4

Consider the following declaration:
template
class strange
{

...

b
...

c
...
Write the definition of the function
operator== for the class strange, which is overloaded as a member
function
...


26
...


cout << surprise(5, 7) << endl;

b
...


Chapter 2: Object-Oriented Design (OOD) and C++

Consider the definition of the following function template:
Template
Type funcExp(Type list[], int size)
{
Type x = list[0];
Type y = list[size - 1];
for (int j = 1; j < (size - 1)/2; j++)
{
if (x < list[j])
x = list[j];
if (y > list[size - 1 -j])
y = list[size - 1 -j];
}
}

return x + y;

Further suppose that you have the following declarations:
int list[10] = {5,3,2,10,4,19,45,13,61,11};
string strList[] = {"One", "Hello", "Four", "Three", "How", "Six"};

What is the output of the following statements?
a
...

28
...


PROGRAMMING EXERCISES
1
...


In Chapter 1, the class clockType was designed to implement the time
of day in a program
...
Derive the class
extClockType from the class clockType by adding a data member
to store the time zone
...
Also, write the definitions of the
member functions and the constructors
...

In this chapter, the class dateType was designed to implement the date
in a program, but the member function setDate and the constructor do
not check whether the date is valid before storing the date in the data
members
...
Add a function member,
isLeapYear, to check whether a year is a leap year
...


Programming Exercises

3
...


5
...


A point in the x-y plane is represented by its x-coordinate and y-coordinate
...
You should then perform operations on the point, such as showing
the point, setting the coordinates of the point, printing the coordinates of the
point, returning the x-coordinate, and returning the y-coordinate
...

Every circle has a center and a radius
...
Given the center, we can determine its
position in the x-y plane
...

Design a class, circleType, that can store the radius and center of the
circle
...
You
should be able to perform the usual operations on a circle, such as setting
the radius, printing the radius, calculating and printing the area and circumference, and carrying out the usual operations on the center
...
Design a
class, cylinderType, that can capture the properties of a cylinder and
perform the usual operations on a cylinder
...
Some of the
operations that can be performed on a cylinder are as follows: Calculate
and print the volume, calculate and print the surface area, set the height, set
the radius of the base, and set the center of the base
...
Redefine
the class dateType so that it can perform the following operations on a
date in addition to the operations already defined:
a
...

c
...

e
...

g
...


i
...

Set the day
...

Return the month
...

Return the year
...

Return the number of days in the month
...

Return the number of days passed in the year
...
Note that the
number of days returned also includes the current day
...
For example, if the
date is 3-18-2011, the number of days remaining in the year is 288
...
Calculate the new date by adding a fixed number of days to the date
...

Write the definitions of the functions to implement the operations defined
for the class dateType in Programming Exercise 6
...
Some applications might require the date to be printed in
another form, such as March 24, 2003
...

j
...


8
...
Add a function member to output the month in the
string format followed by the year—for example, in the form March 2003
...

9
...
To print
a monthly calendar, you must know the first day of the month and the number of
days in that month
...
Clearly, the
month and the year can be stored in an object of the form extDateType by
setting the day component of the date to 1, and the month and year as specified
by the user
...

Design the class calendarType so that the program can print a calendar
for any month starting January 1, 1500
...
To calculate the first day of a month, you can add
the appropriate days to Monday of January 1, 1500
...


b
...

d
...

f
...


Determine the first day of the month for which the calendar will be
printed
...

Set the month
...

Return the month
...

Print the calendar for the particular month
...


Programming Exercises

10
...


b
...

Write a test program to print the calendar for either a particular month
or a particular year
...


12
...
This chapter discussed how to
overload various operators
...
Also write a test program to test various
operations of the class clockType
...
Extend the definition of the class complexType so that it performs
the subtraction and division operations
...

If (a, b) and (c, d) are complex numbers,
(a, b) À (c, d) ¼ (a À c, b À d),
If (c, d) is nonzero,
(a, b) / (c, d) ¼ ((ac + bd) / (c2 + d2), (Àad + bc) / (c2 + d2))
b
...


13
...


b
...


Write the definitions of the functions to overload the operators – and /
as defined in part a
...
Format your answer with two decimal places
...

Write the definitions of the member functions of the class
complexType as designed in part a
...
Format your answer with
two decimal places
...


15
...


17
...


Chapter 2: Object-Oriented Design (OOD) and C++

Let a + ib be a complex number
...
Extend the definition of the class
complexType of the Programming Example, Complex Numbers by overloading the operators $ and ! as member functions so that $ returns the
conjugate of a complex number and ! returns the absolute value
...

Redo Programming Exercise 13 so that the operators $ and ! are overloaded as nonmember functions
...
So an integer larger than this
cannot be stored and processed as an integer
...

One way to store and manipulate large integers is to store each individual digit
of the number in an array
...
Overload the
operators + and – to add and subtract, respectively, the values of two objects
of this class
...
) Overload the assignment operator to copy the
value of a large integer into another large integer
...
Your program
must contain appropriate constructors to initialize objects of the class
largeIntegers
...
Add instance variables to store the number of
digits and the sign of the number
...
If b2 À 4ac ¼ 0,
the equation has a single (repeated) root
...
If b2 À 4ac < 0, the equation has two complex roots
...
Overload the operators + and – to add and subtract, respectively, the corresponding coefficients
of two quadratic equations
...
Add appropriate constructors to initialize objects
...
Also, include function members to
determine and output the type and the roots of the equation
...

Programming Exercise 6 in Chapter 1 describes how to design the class
lineType to implement a line
...

b
...

d
...


f
...


h
...


Overloads the stream insertion operator, <<, for easy output
...
(The line
ax + by ¼ c is input as (a, b, c)
...

Overloads the unary operator + as a member function, so that it returns
true if a line is vertical; false otherwise
...

Overloads the operator == as a member function, so that it returns true
if two lines are equal; false otherwise
...

Overloads the operator && as a member function, so that it returns
true if two lines are perpendicular; false otherwise
...

Rational fractions are of the form a / b, where a and b are integers and b 6¼ 0
...
Suppose a / b
and c / d are fractions
...

Fractions are compared as follows: a / b op c / d if ad op bc, where op is any of
the relational operations
...

Design a class—say, fractionType—that performs the arithmetic and
relational operations on fractions
...
Also, overload the stream insertion and stream extraction operators for easy input and output
...


b
...

Among other things, test the following: Suppose x, y, and z are objects
of type fractionType
...
The statement
cout << x + y << endl;

|

129

2

130 |

Chapter 2: Object-Oriented Design (OOD) and C++

should output the value of x + y in fraction form
...


a
...
Your answer
need not be in the lowest terms
...
In that
exercise, we also implemented a function, romanToDecimal, to convert a Roman numeral into its equivalent decimal number
...
Use the class string to manipulate
the strings
...
The stream insertion
operator outputs the Roman numeral in the Roman format
...
Write the definition of the member
function decimalToRoman
...


c
...


For simplicity, we assume that only the letter I can appear in front of
another letter and that it appears only in front of the letters V and X
...
Also, 40 will be represented
as XXXX, 190 will be represented as CLXXXX, and so on
...
In the class extRomanType, overload the arithmetic
operators +, -, *, and / so that arithmetic operations can be performed
on Roman numerals
...

To add (subtract, multiply, or divide) Roman numerals, add (subtract,
multiply, or divide, respectively) their decimal representations and then
convert the result to the Roman numeral format
...
Similarly, for division, the
numerator must be larger than the denominator
...

Write the definitions of the functions to overload the operators
described in part b
...


3

CHAPTER

P OINTERS AND
A RRAY -B ASED L ISTS
I N T H I S C H A P T E R , YO U W I L L :


...


Explore how to declare and manipulate pointer variables


...


Discover dynamic variables


...


Learn about pointer arithmetic


...


Become aware of the shallow and deep copies of data


...


Explore how dynamic arrays are used to process lists


...


Become aware of abstract classes

132 |

Chapter 3: Pointers and Array-Based Lists

The data types in C++ are classified into three categories: simple, structured, and pointers
...
This chapter discusses
the third data type: the pointer data type
...
Later, you use
these concepts when you study dynamic arrays and linked lists
...


The Pointer Data Type and Pointer Variables
The values belonging to pointer data types are the memory addresses of your computer
...
Because the
domain, (that is, the values of a pointer data type), consists of addresses (memory
locations), a pointer variable is a variable whose content is an address, that is, a memory
location
...


Declaring Pointer Variables
The value of a pointer variable is an address
...
The data is typically stored in this memory space
...

In C++, you declare a pointer variable by using the asterisk symbol ( *) between the
data type and the variable name
...
The content of p (when properly
assigned) points to a memory location of type int, and the content of ch points to a
memory location of type char
...

Before discussing how pointers work, let us make the following observations
...

Now consider the following statement:
int* p, q;

In this statement, only p is a pointer variable, not q
...
To avoid
confusion, we prefer to attach the character * to the variable name
...

Now that you know how to declare pointers, next we discuss how to make a pointer
point to a memory space and how to manipulate the data stored in these memory
locations
...
For example, if p is a pointer of type int, p can
store the address of any memory space of type int
...
The
next two sections describe these operators
...
For example, given the statements
int x;
int *p;

the statement
p = &x;

assigns the address of x to p
...


Dereferencing Operator (*)
The previous chapters used the asterisk character, *, as the binary multiplication operator
...
When *, commonly referred to as the dereferencing operator or indirection operator, is used as a unary operator, * refers to the

3

134 |

Chapter 3: Pointers and Array-Based Lists

object to which the operand of the * (that is, the pointer) points
...

Also, the statement
*p = 55;

stores 55 in the memory location to which p points—that is, 55 is stored in x
...


EXAMPLE 3-1
Let us consider the following statements:
int *p;
int num;

In these statements, p is a pointer variable of type int and num is a variable of type int
...
(See Figure 3-1
...



...
num = 78;
2
...
*p = 24;


...

After
statement

Values of the variables

1


...


78

1200
p

...


78

24

1200
p

1800
num

The statement num = 78; stores 78 into

num
...
1800
...


1800
num

1200
p

2

Explanation

3


...


The statement p = # stores the
address of num, which is 1800, into p
...
Because
the value of p is 1800, statement 3 stores
24 into memory location 1800
...


Let us summarize the preceding discussion
...
A declaration such as int *p; allocates memory for p only, not for *p
...

2
...

3
...

4
...

5
...

6
...
Note
that the value of *p is 78 after the statement p = # executes; the
value of *p is 24 after the statement *p = 24; executes
...

EXAMPLE 3-2
//***********************************************************
// Author: D
...
Malik
//
// This program illustrates how a pointer variable works
...
Let us look at some of these
statements
...
The statement in
Line 9 outputs the value of &num1, the address of num1, and the value of p
...
When you execute this program on your
computer, you are likely to get different values of &num1 and p
...
Because p points to the memory location of num1,
*p outputs the value of this memory location, that is, of num1
...
Because p points to the memory location num1, the value
of num1 is also changed
...

The statement in Line 13 stores the address of num2 into p
...
So, any change that *p makes immediately changes the value

The Pointer Data Type and Pointer Variables |

137

of num2
...
This
statement in Line 16 multiplies the value of *p, which is the value of num2, by 2 and
stores the new value into *p
...
The
statement in Line 17 outputs the value of num2 and *p
...
The second
statement allocates memory of type string and stores the address of the allocated memory
in str
...
Now suppose that you want to use the string function length to find the length of
the string "Sunny Day"
...
length() returns the length of the string
...
The expression (*str)
...
In C++, the dot operator,
...
Let us elaborate on this a little more
...
length(), the operator * evaluates first, so the expression *str
evaluates first
...
Therefore, in the expression (*str)
...
Now consider the expression *str
...

Let us see how this expression gets evaluated
...
has a higher precedence than *, the
expression str
...
The expression str
...

As you can see, in the expression (*str)
...
However, typos are unavoidable
...
The operator -> consists of two consecutive
symbols: a hyphen and the ‘‘greater than’’ symbol
...
length()

is equivalent to the expression
str->length()

3

138 |

Chapter 3: Pointers and Array-Based Lists

Accessing class (struct) components via pointers using the operator -> thus eliminates
the use both of the parentheses and of the dereferencing operator
...


Initializing Pointer Variables
Because C++ does not automatically initialize variables, pointer variables must be
initialized if you do not want them to point to anything
...
Thus, the statement p = 0; stores the
null pointer in p; that is, p points to nothing
...
The following two statements are equivalent:
p = NULL;
p = 0;

The number 0 is the only number that can be directly assigned to a pointer variable
...
However, you learned how to use pointers to manipulate
data only into memory spaces that were created using other variables
...
So what is the benefit to using
pointers? You can access these memory spaces by working with the variables that were
used to create them
...
In
particular, you learn how to allocate and deallocate memory during program execution
using pointers
...
With
the help of pointers, C++ creates dynamic variables
...
When a program
requires a new variable, the operator new is used
...

In C++, new and delete are reserved words
...
The syntax to use the operator new is as follows:
new dataType;
new dataType[intExp];

//to allocate a single variable
//to allocate an array of variables

where intExp is any expression evaluating to a positive integer
...
Moreover, the allocated
memory is uninitialized
...
However, no new memory is allocated
...
The allocated memory is accessed via
pointer dereferencing—namely, *p
...

Because a dynamic variable is unnamed, it cannot be accessed directly
...
The following statements illustrate this concept:
int *p;

//p is a pointer of type int

p = new int;
*p = 28;

//allocates memory of type int and stores the address
//of the allocated memory in p
//stores 28 in the allocated memory

The operator new allocates memory space of a specific type and returns the (starting)
address of the allocated memory space
...


Operator delete
Suppose you have the following declaration:
int *p;

This statement declares p to be a pointer variable of type int
...
The statement in Line 1 allocates memory space
of type int and stores the address of the allocated memory space into p
...
Then, the value of p after the execution of
this statement is 1500
...
)

1500
p 1500

FIGURE 3-2

p after the execution of p = new int;

In Figure 3-2, the number 1500 on top of the box indicates the address of the memory
space
...
In other words, after execution of the statement in Line 2, the value stored into
memory space at location 1500 is 54
...
)

p 1500

FIGURE 3-3

1500
54

p and *p after the execution of *p = 54;

Next, the statement in Line 3 executes, which allocates a memory space of type int and
stores the address of the allocated memory space into p
...
It follows that the value of p is now 1800
...
)

1500
54
1800
p 1800

FIGURE 3-4

p after the execution of p = new int;

The statement in Line 4 stores 73 into the memory space to which p points, which is
1800
...
(See Figure 3-5
...
The previous
memory space at location 1500 is now inaccessible
...
In other words, it cannot be reallocated
...
That is, there is an unused memory space that cannot be allocated
...
There will be a good amount of memory leak
...

The question at hand is how to avoid memory leak
...
The C++ operator
delete is used to destroy dynamic variables, so that its memory space can be allocated
again when needed
...

Suppose p is a pointer variable, as declared previously
...
Depending
on a particular system, after these statements execute, these pointer variables might still contain
the addresses of the deallocated memory spaces
...
Therefore, if you later access the memory spaces via these pointers without properly
initializing them, depending on a particular system, either the program will access a wrong
memory space, which might result in corrupting data, or the program will terminate with an error
message
...

The program in the following example illustrates how to allocate dynamic memory and
how to manipulate data into that dynamic memory
...
S
...

//***********************************************************
#include

//Line 1

using namespace std;

//Line 2

int main()
{
int *p;
int *q;

//Line
//Line
//Line
//Line

p = new int;
*p = 34;
cout << "Line 9: p = " << p
<< ", *p = " << *p << endl;
q = p;
cout << "Line 11: q = " << q
<< ", *q = " << *q << endl;
*q = 45;
cout << "Line
<< ", *p
cout << "Line
<< ", *q
p = new int;
*p = 18;
cout << "Line
<< ", *p
cout << "Line
<< ", *q
delete q;
q = NULL;
q = new int;
*q = 62;
cout << "Line
<< ", *p
cout << "Line
<< ", *q
}

return 0;

3
4
5
6

//Line 7
//Line 8
//Line 9
//Line 10
//Line 11
//Line 12

13:
= "
14:
= "

p = "
<< *p
q = "
<< *q

<<
<<
<<
<<

p
endl;
q
endl;

//Line 13
//Line 14
//Line 15
//Line 16

17:
= "
18:
= "

p = "
<< *p
q = "
<< *q

<<
<<
<<
<<

p
endl;
q
endl;

//Line 17
//Line 18
//Line
//Line
//Line
//Line

23:
= "
24:
= "

p = "
<< *p
q = "
<< *q

<<
<<
<<
<<

p
endl;
q
endl;

19
20
21
22

//Line 23
//Line 24
//Line 25
//Line 26

The Pointer Data Type and Pointer Variables |

143

Sample Run:
Line
Line
Line
Line
Line
Line
Line
Line

9: p = 00355620, *p = 34
11: q = 00355620, *q = 34
13: p = 00355620, *p = 45
14: q = 00355620, *q = 45
17: p = 003556C8, *p = 18
18: q = 00355620, *q = 45
23: p = 003556C8, *p = 18
24: q = 00355620, *q = 62

3

The statements in Lines 5 and 6 declare p and q to be pointer variables of type int
...
(See Figure 3-6
...
The statement in Line 8 stores
34 into the memory location to which p points
...
)

p

FIGURE 3-7

34

Pointer p and the value of the memory location to which p points

The statement in Line 9 outputs the value of p and *p
...
When you execute this program, you
are likely to get different values of p and q
...
(See Figure 3-8
...
So any changes made into that memory location by q immediately change the
value of *p
...
The statement in
Line 12 stores 45 into the memory location to which q points
...
)

p
45
q

FIGURE 3-9 Pointers p and q and the memory space to which they point after the execution
of the statement in Line 12

The statements in Lines 13 and 14 output the values of p, *p, q, and *q
...
(See Figure 3-10
...

(See Figure 3-11
...

The statement in Line 19 deallocates the memory space to which q points and the
statement in Line 20 sets the value of q to NULL
...
(See Figure 3-12
...
The statement in Line 22 stores 62 in the memory space to
which q points
...
)

p

18

q

62

Pointers p and q and the memory space to which they point after the execution
of the statement in Line 22

FIGURE 3-13

The statements in Lines 23 and 24 output the values of p, *p, q, and *q
...


Operations on Pointer Variables
The operations that are allowed on pointer variables are the assignment and relational
operations and some limited arithmetic operations
...
Two pointer variables of the
same type can be compared for equality, and so on
...
The value of one pointer variable can be subtracted
from another pointer variable
...
After this statement executes, both p and q point to the same
memory location
...


3

146 |

Chapter 3: Pointers and Array-Based Lists

The expression
p == q

evaluates to true if p and q have the same value—that is, if they point to the same
memory location
...

The arithmetic operations that are allowed differ from the arithmetic operations on
numbers
...

The statement
p++; or p = p + 1;

increments the value of p by 4 bytes because p is a pointer of type int
...

The increment operator increments the value of a pointer variable by the size of the
memory to which it is pointing
...

Moreover, the statement
p = p + 2;

increments the value of p by 8 bytes
...

Similarly, when an integer is subtracted from a pointer variable, the value of the pointer variable
is decremented by the integer times the size of the memory to which the pointer is pointing
...
Using pointer arithmetic, the program can
accidentally access the memory locations of other variables and change their content
without warning
...
If a
pointer variable tries to access either the memory spaces of other variables or an illegal
memory space, some systems might terminate the program with an appropriate error
message
...


The Pointer Data Type and Pointer Variables |

147

Dynamic Arrays
The arrays used earlier are called static arrays because their size was fixed at compile time
...
One way to handle this limitation is to declare an array that is large enough to
process a variety of data sets
...
On the other hand, it would be helpful if, during
program execution, you could prompt the user to enter the size of the array and then create an
array of the appropriate size
...
In this section, you learn how to create arrays during program execution and how to
process such arrays
...
To create
a dynamic array, we use the second form of the new operator
...
The statement
p = new int[10];

allocates 10 contiguous memory locations, each of type int, and stores the address of the
first memory location into p
...
Thus, the statement
*p = 25;

stores 25 into the first memory location, and the statements
p++;
*p = 35;

//p points to the next array component

store 35 into the second memory location
...
Of course, after performing a few
increment operations, it is possible to lose track of the first array component
...
For example, the statements
p[0] = 25;
p[1] = 35;

store 25 and 35 into the first and second array components, respectively
...

In general, p[i] refers to the (i + 1)th array component
...

The following for loop initializes each array component to 0:
for (int j = 0; j < 10; j++)
p[j] = 0;

3

148 |

Chapter 3: Pointers and Array-Based Lists

When the array notation is used to process the array to which p points, p stays fixed at
the first memory location
...

EXAMPLE 3-4
The following program segment illustrates how to obtain a user’s response to get the array size
and create a dynamic array during program execution
...
The statement in Line 3 prompts the
user to enter the size of the array, and the statement in Line 4 inputs the array size into the
variable arraySize
...
From this point on,
you can treat intList just like any other array
...


Array Name: A Constant Pointer
The statement
int list[5];

declares list to be an array of five components
...
Suppose the address of the first array component is 1000
...


list

1000
1000
list[0]

FIGURE 3-14

list and array list

1004
1008
list[1] list[2]

1012
list[3]

1016
list[4]

The Pointer Data Type and Pointer Variables |

149

Because the value of list, which is 1000, is a memory address, list is a pointer variable
...

That is, the value of list is constant
...
In fact, any attempt to use the increment or decrement operations
on list results in a compile-time error
...
However,
the data in the array list can be manipulated as usual
...
Similarly, the statement
list[2] = 78; stores 78 into the third component of list
...
)

list 1000

25
1000
list[0]

FIGURE 3-15

78
1004
1008
list[1] list[2]

1012
list[3]

1016
list[4]

Array list after the execution of the statements list[0] = 25; and list[2] = 78;

If p is a pointer variable of type int, then the statement
p = list;

copies the value of list, which is 1000, the base address of the array, into p
...

An array name is a constant pointer
...
To declare a pointer as a value parameter in a function heading, you use the
same mechanism as you use to declare a variable
...
Therefore, to declare a formal parameter as a reference parameter, you must
use &
...
The obvious question is:
In what order should & and * appear between the data type name and the identifier to
declare a pointer as a reference parameter? In C++, to make a pointer a reference
parameter in a function heading, * appears before the & between the data type name
and the identifier
...


...

}

3

150 |

Chapter 3: Pointers and Array-Based Lists

In this example, both p and q are pointers
...


Pointers and Function Return Values
In C++, the return type of a function can be a pointer
...
)
{

...


...


Dynamic Two-Dimensional Arrays
The beginning of this section discussed how to create dynamic one-dimensional arrays
...
In this section, we discuss how to create
dynamic two-dimensional arrays
...

There are various ways you can create dynamic two-dimensional arrays
...
Consider the statement:
int *board[4];

This statement declares board to be an array of four pointers wherein each pointer is of
type int
...
Suppose that each row of board
has six columns
...

for (int row = 0; row < 4; row++)
board[row] = new int[6];

Note that the expression new int[6] creates an array of six components of type int and
returns the base address of the array
...
It follows that after the execution of the previous for loop,
board is a two-dimensional array of 4 rows and 6 columns
...
In other words, the number of
columns of board can be specified during execution
...
So in reality, board is not a true dynamic two-dimensional array
...
In other words, board and
*board are pointers
...


The Pointer Data Type and Pointer Variables |

151

Suppose that you want board to be an array of 10 rows and 15 columns
...
The following statement accomplishes this:
board = new int* [10];

Next we create the columns of board
...
For
example, see the next example
...
The program in Example 3-5 further
explains how to create two-dimensional arrays
...
S
...

//*************************************************************
#include
#include

//Line 1
//Line 2

using namespace std;

//Line 3

void fill(int **p, int rowSize, int columnSize);
void print(int **p, int rowSize, int columnSize);

//Line 4
//Line 5

int main()
{
int **board;

//Line 6
//Line 7
//Line 8

int rows;
int columns;

//Line 9
//Line 10

cout << "Line 11: Enter the number of rows "
<<"and columns: ";
cin >> rows >> columns;
cout << endl;

//Line 11
//Line 12
//Line 13

//Create the rows of board
board = new int* [rows];

//Line 14

//Create the columns of board
for (int row = 0; row < rows; row++)
board[row] = new int[columns];

//Line 15
//Line 16

152 |

Chapter 3: Pointers and Array-Based Lists

//Insert elements into board
fill(board, rows, columns);
cout << "Line 18: Board:" << endl;

//Line 18

//Output the elements of board
print(board, rows, columns);
}

//Line 17

//Line 19

return 0;

//Line 20
//Line 21

void fill(int **p, int rowSize, int columnSize)
{
for (int row = 0; row < rowSize; row++)
{
cout << "Enter " << columnSize << " number(s) for row "
<< "number " << row << ": ";
for (int col = 0; col < columnSize; col++)
cin >> p[row][col];
cout << endl;
}
}
void print(int **p, int rowSize, int columnSize)
{
for (int row = 0; row < rowSize; row++)
{
for (int col = 0; col < columnSize; col++)
cout << setw(5) << p[row][col];
cout << endl;
}
}

Sample Run: In this sample run, the user input is shaded
...
The function fill
prompts the user to enter the elements of a two-dimensional array of type int
...


The Pointer Data Type and Pointer Variables |

153

For the most part, the preceding output is self-explanatory
...
The statement in Line 8 declares board to be a pointer to a pointer
of type int
...

The statement in Line 11 prompts the user to input the number of rows and number of
columns
...
The statement in Line 14 creates the
rows of board and the for loop in Lines 15 and 16 creates the columns of board
...


Shallow Vs
...
This
event might result in unsuspected or erroneous results
...
To facilitate the discussion, we will use diagrams to show pointers and
their related memory
...


first

FIGURE 3-16

10

36

89

29

47

64

28

92

37

73

Pointer first and its array

Next, consider the following statement:
second = first;

//Line A

This statement copies the value of first into second
...


first

10

36

89

29

47

64

28

92

37

73

second

FIGURE 3-17

first and second after the statement second = first; executes

3

154 |

Chapter 3: Pointers and Array-Based Lists

The statement first[4] = 10; not only changes the value of first[4], it also changes
the value of second[4] because they point to the same array
...
This action
results in Figure 3-18
...
Therefore, if the program later tries to access the memory to which first
pointed, either the program will access the wrong memory or it will terminate in an
error
...
More formally, in a shallow copy, two
or more pointers of the same type point to the same memory; that is, they point to the
same data
...
The second statement copies the array to which first
points into the array to which second points
...
)

first

36

89

29

47

64

28

92

37

73

second

FIGURE 3-19

10
10

36

89

29

47

64

28

92

37

73

first and second both pointing to their own data

Classes and Pointers: Some Peculiarities

|

155

Both first and second now point to their own data
...
This case is an example of a deep copy
...

From the preceding discussion, it follows that you must know when to use a shallow
copy and when to use a deep copy
...
To facilitate the discussion, we use the following class:
class pointerDataClass
{
public:

...


...
(See Figure 3-20
...
Suppose that during program
execution the pointer p creates a dynamic array
...
However, p created a dynamic array,
and dynamic memory must be deallocated using the operator delete
...
How do we ensure that when p is destroyed, the dynamic memory created by p
is also destroyed? Suppose that objectOne is as shown in Figure 3-21
...


Object objectOne and its data

Recall that if a class has a destructor, the destructor automatically executes whenever a
class object goes out of scope (see Chapter 1)
...
For example, the definition of the destructor for the
class pointerDataClass is as follows:
pointerDataClass::~pointerDataClass()
{
delete [] p;
}

Of course, you must include the destructor as a member of the class in its definition
...

Moreover, the remainder of this section assumes that the definition of the destructor is as
given previously—that is, the destructor deallocates the memory space pointed to by p
...


...

private:
int x;
int lenP;
int *p;
};
For the destructor to work properly, the pointer p must have a valid value
...
For this reason, you should exercise extra caution while working
with pointers
...
Suppose that objectOne and objectTwo are as shown in Figure 3-22
...


Objects objectOne and objectTwo

Recall that one of the built-in operations on classes is the assignment operator
...
That is, the value of
objectOne
...
x, and the value of objectOne
...
p
...
That is, both objectTwo
...
p
would point to the same memory space, as shown in Figure 3-23
...


Objects objectOne and objectTwo after the statement objectTwo =
objectOne; executes

FIGURE 3-23

Now, if objectTwo
...
p would
become invalid
...
It suggests that there must be a way to avoid
this pitfall
...
This process is called overloading the assignment operator
...
Once the
assignment operator is properly overloaded, both objectOne and objectTwo have their
own data, as shown in Figure 3-24
...


5

36 24 15


...

General Syntax to Overload the Assignment Operator = for a Class
Function Prototype (to be included in the definition of the class):
const className& operator=(const className&);

Function Definition:
const className& className::operator=(const className& rightObject)
{
//local declaration, if any
if (this != &rightObject) //avoids self-assignment
{
//algorithm to copy rightObject into this object
}
//returns the object assigned
return *this;

}

In the definition of the function operator=:




There is only one formal parameter
...

The return type of the function is a reference to a particular class
...
We must prevent such statements because they waste computer time
...
Let us see how
...
operator=(x);

Because the function operator= is invoked by the object x, the pointer this in the
body of the function operator= refers to the object x
...
Therefore, in the expression
this != &rightObject
this means the address of x, and &rightObject also means the address of x
...


Notice that the return type of the function to overload the assignment operator is a
reference
...

In the section ‘‘Array-Based Lists,’’ later in this chapter, we explicitly illustrate how to
overload the assignment operator
...
For example, consider the following statement:
pointerDataClass objectThree(objectOne);

The object objectThree is being declared and is also being initialized by using the value of
objectOne
...
This initialization is called the default
memberwise initialization
...
Just as in the case of the assignment
operator, because the class pointerDataClass has pointer member variables, this default
initialization would lead to a shallow copying of the data, as shown in Figure 3-25
...
)

objectOne

x 8
lenP 50

objectThree

p
5

FIGURE 3-25

x 8
lenP 50
p

36 24 15
...
The solution to both these problems
is the same
...
Remember that the class pointerDataClass has the destructor, which deallocates
the memory space pointed to by p
...


objectOne

x 8
lenP 50
p
5

FIGURE 3-26

36 24 15


...
Now
consider the following statement:
destroyList(objectOne);

In this statement, objectOne is passed as a parameter to the function destroyList
...
Just
as in the previous case, paramObject
...
p would point to the same
memory space, as shown in Figure 3-27
...


FIGURE 3-27 Pointer member variables of objects objectOne and paramObject pointing
to the same array

Because objectOne is passed by value, the member variables of paramObject should
have their own copy of the data
...
p should have its own
memory space
...

• If, as a parameter, an object is passed by value and the default memberwise copying of data is allowed, it leads to a shallow copying of the data
...
This is usually done by putting a statement that
includes the copy constructor in the definition of the class, and then writing the definition
of the copy constructor
...

Therefore, for the class pointerDataClass, we can overcome this shallow copying
problem by including the copy constructor in the class pointerDataClass
...
p and objectThree
...

Similarly, objectOne
...
p will have their own copies of the data, as
shown in Figure 3-28
...


5

36 24 15


...
p
...


3

162 |

Chapter 3: Pointers and Array-Based Lists

The general syntax to include the copy constructor in the definition of a class is as
follows:
className(const className& otherObject);

Notice that the formal parameter of the copy constructor is a constant reference
parameter
...

For classes with pointer member variables, three things are normally done:
1
...

2
...

3
...


Inheritance, Pointers, and Virtual Functions
Recall that, as a parameter, a class object can be passed either by value or by reference
...

However, in the case of classes, C++ allows the user to pass an object of a derived class to a
formal parameter of the base class type
...
To be specific, let us consider the following classes:
class baseClass
{
public:
void print();
baseClass(int u = 0);
private:
int x;
};
class derivedClass: public baseClass
{
public:
void print();
derivedClass(int u = 0, int v = 0);
private:
int a;
};

The class baseClass has three members
...
Both classes have a member function
print
...
print();
}

The function callPrint has a formal reference parameter p of type baseClass
...
Moreover, the body of the function callPrint calls the
member function print
...
print();
two
...
Let us look at the statements
in Lines 8 and 9
...
The statement in Line
9 also calls the function callPrint, but passes the object two as the parameter; it generates
the sixth line of the output
...
(Because in Line 9 object two is passed as a parameter to the function
callPrint, one would expect that the output generated by the statement in Line 9 should
be similar to the output in the second and third lines of the output
...
This is due to the fact that the binding of the member function
print, in the body of the function callPrint, occurred at compile time
...
print();, the compiler associates the function print of the class baseClass
...
(Compile-time binding is also known as static binding
...
Thus, when
the body of the function callPrint executes, logically the print function of object two
should execute, which is not the case
...
The binding of virtual
functions occurs at program execution time, not at compile time
...
More formally, in run-time binding, the compiler does not
generate the code to call a specific function
...

Run-time binding is also known as dynamic binding
...
Let us redefine
the previous classes using this feature:
class baseClass
{
public:
virtual void print();
baseClass(int u = 0);
private:
int x;
};

//virtual function

Inheritance, Pointers, and Virtual Functions |

165

class derivedClass: public baseClass
{
public:
void print();
derivedClass(int u = 0, int v = 0);
private:
int a;
};

3

Note that we need to declare a virtual function only in the base class
...
If we execute the
previous program with these modifications, the output is as follows
...

The previous discussion also applies when a formal parameter is a pointer to a class, and a
pointer of the derived class is passed as an actual parameter
...
(We assume that the definition of the class baseClass is in
the header file baseClass
...
h
...
S
...

//******************************************************
#include

//Line 1

#include "derivedClass
...

However, if p is a value parameter, then this mechanism of passing a derived class object as
an actual parameter to p does not work, even if p uses a virtual function
...
Therefore, if a formal parameter is of a class type, the member
variables of the actual object are copied into the corresponding member variables of the
formal parameter
...

Consider the following function definition:
void callPrint(baseClass p)
{
p
...
The member variable x is inherited
from the base class
...
However, because p is an
object of type baseClass, it has only one member variable
...
Also, the
statement:
p
...

The output of the following program further illustrates this concept
...
h,
and the definition of the class derivedClass is in the header file derivedClass
...
)
//*******************************************************
// Author: D
...
Malik
//
// This program illustrates how virtual functions and a
// pointer variable of base class as a formal parameter
// work
...
h"

//Line 2

using namespace std;

//Line 3

void callPrint(baseClass p);

//Line 4

int main()
{
baseClass one(5);
derivedClass two(3, 15);

//Line
//Line
//Line
//Line

5
6
7
8

one
...
print();
cout << "*** Calling the function "
<< "callPrint ***" << endl;

//Line 11

callPrint(one);
callPrint(two);
}

//Line 9
//Line 10

//Line 12
//Line 13

return 0;

//Line 14
//Line 15

3

168 |

Chapter 3: Pointers and Array-Based Lists

void callPrint(baseClass p)
{
p
...
In Line 13, because the formal parameter p is a value parameter, the member
variables of two are copied into the corresponding member variables of p
...
Consequently, only the member variable x of two is copied into the member variable x of p
...
print(); in the function callPrint executes the function
print of the class baseClass, not the class derivedClass
...

An object of the base class type cannot be passed to a formal parameter of the derived
class type
...
The destructor is automatically executed when the class
object goes out of scope
...
If a derived class object is passed to a
formal parameter of the base class type, the destructor of the base class executes regardless
of whether the derived class object is passed by reference or by value
...

To correct this problem, the destructor of the base class must be virtual
...

When a derived class object is passed to a formal parameter of the base class type, then
when the object goes out of scope, the destructor of the derived class executes
...

Therefore, when the derived class object is destroyed, the base class part (that is, the
members inherited from the base class) of the derived class object is also destroyed
...


Abstract Classes and Pure Virtual Functions |

169

Abstract Classes and Pure Virtual Functions
The preceding section discussed virtual functions
...

Chapter 2 described the second principal of OOD—inheritance
...
The derived classes, in
addition to inheriting the existing members of the base class, can add their own members
and also redefine or override public and protected member functions of the base class
...
There are many scenarios when a class is desired to be served as a base class for a
number of derived classes, however, the base class may contain certain functions that may
not have meaningful definitions in the base class
...
As noted in that chapter, from the
class shape you can derive other classes such as rectangle, circle, ellipse, and
so on
...
Among others, we can include
these in the class shape
...


};

virtual void move(double x, double y);
//Function to move the shape at the position (x, y)
...


...


Because the definitions of the functions draw and move are specific to a particular shape,
each derived class can provide an appropriate definition of these functions
...

The way the definition of the class shape is written when you write the definition of
the functions of the class shape, you must also write the definitions of the functions
draw and move
...
Therefore,
these function bodies have no code
...
This solution would work, but it has another drawback
...
Because there is no shape to work with, we would like to prevent the user from
creating objects of the class shape
...


3

170 |

Chapter 3: Pointers and Array-Based Lists

Because we do not want to include the definitions of the functions draw and move of the
class shape, we must convert these functions to pure virtual functions
...
Once you make these functions pure
virtual functions in the class shape, you no longer need to provide the definitions of
these functions for the class shape
...
Thus, the abstract definition of the class shape is similar to the following:
class shape
{
public:
virtual void draw() = 0;
//Function to draw the shape
...


};

virtual void move(double x, double y) = 0;
//Function to move the shape at the position (x, y)
...


...


...

Now suppose that we derive the class rectangle from the class shape
...

Note that in addition to the pure virtual functions, an abstract class can contain instance
variables, constructors, and functions that are not pure virtual
...


Array-Based Lists
Everyone is familiar with the term list
...
One thing common to all lists is that all
the elements of a list are of the same type
...

The length of a list is the number of elements in the list
...

2
...

4
...

6
...

8
...

10
...


Create the list
...

Determine whether the list is empty
...

Find the size of the list
...

Determine whether an item is the same as a given list element
...

Remove an item from the list at the specified location
...

Retrieve an item from the list from the specified location
...


Before discussing how to implement these operations, we must first decide how to store the
list in the computer’s memory
...
Initially, the size of the
array holding the list elements is usually larger than the number of elements in the list so that,
at a later stage, the list can grow
...
C++ allows the programmer to
create dynamic arrays
...
The
size of the array can be specified when a list object is declared
...
Then
length and maxSize are nonnegative integers and, therefore, we can declare them to be
of type int
...
If we
have a list of names, the array elements are of type string
...
As
you can see, there are various types of lists
...
To insert an item
at the end of a list of any type would require you to add the element after the current last
element and then increment the length by one
...
We do not want to spend time and efforts to

3

172 |

Chapter 3: Pointers and Array-Based Lists

develop separate code for each type of list we encounter
...
In other words,
while designing the algorithms, we do not want to be concerned whether we are
processing a list of numbers, a list of names, or a list of students’ data
...
To develop
generic algorithms to implement list operations, we make use of class templates
...
The following class, arrayListType, defines the list as an ADT:
//***********************************************************
// Author: D
...
Malik
//
// This class specifies the members to implement the basic
// properties of array-based lists
...

bool isFull() const;
//Function to determine whether the list is full
...

int listSize() const;
//Function to determine the number of elements in the list
//Postcondition: Returns the value of length
...

//Postcondition: Returns the value of maxSize
...

bool isItemAtEqual(int location, const elemType& item) const;
//Function to determine whether the item is the same
//as the item in the list at the position specified by
//Postcondition: Returns true if list[location]
//
is the same as the item; otherwise,
//
returns false
...
The item to be inserted
//is passed as a parameter to the function
...
If the list is full or location is
//
out of range, an appropriate message is displayed
...

//The parameter insertItem specifies the item to be inserted
...

void removeAt(int location);
//Function to remove the item from the list at the
//position specified by location
//Postcondition: The list element at list[location] is removed
//
and length is decremented by 1
...

void retrieveAt(int location, elemType& retItem) const;
//Function to retrieve the element from the list at the
//position specified by location
...

void replaceAt(int location, const elemType& repItem);
//Function to replace the elements in the list at the
//position specified by location
...

//Postcondition: list[location] = repItem
//
If location is out of range, an appropriate message is
//
displayed
...

//After this operation, the size of the list is zero
...

//Postcondition: If the item is found, returns the location
//
in the array where the item is found; otherwise,
//
returns -1
...
However, first the
//list is searched to see whether the item to be inserted
//is already in the list
...

void remove(const elemType& removeItem);
//Function to remove an item from the list
...

//Postcondition: If removeItem is found in the list,
//
it is removed from the list and length is
//
decremented by one
...
The default array size is 100
...

protected:
elemType *list;
int length;
int maxSize;
};

//array to hold the list elements
//to store the length of the list
//to store the maximum size of the list

Figure 3-29 shows the UML class diagram of the class arrayListType
...

This is because we want to derive classes from this class to implement special lists such as
an ordered list
...

The list is empty if length is zero; it is full if length is equal to maxSize
...

Similarly, because the size of the array holding the list elements is stored in the data
member maxSize, maxSize specifies the maximum size of the list
...
It
follows that each of these functions is of O(1)
...
We assume that the output
is sent to the standard output device
...
The number of times
the for loop executes depends on the number of elements of the list
...
In general, suppose that the number of
elements in the list is n
...

The definition of the function isItemAtEqual is straightforward
...
It is easy to see that this function is of O(1)
...
The item to be
inserted and the insert location in the array are passed as parameters to this function
...
That is, we need to move certain elements right one array slot
...
(Note that
this figure does not show the data members length and maxSize
...


Array list

The number of elements currently in the list is 6, so length is 6
...
If the item is to be inserted at, say location 6, we
can easily accomplish this by copying the item into list[6]
...
Thus, we
must first copy list[5] into list[6], list[4] into list[5], and list[3] into
list[4], in this order
...

Of course, special cases such as trying to insert in a full list must be handled separately
...

The definition of the function insertAt is as follows:
template
void arrayListType::insertAt
(int location, const elemType& insertItem)
{
if (location < 0 || location >= maxSize)
cerr << "The position of the item to be inserted "
<< "is out of range" << endl;

Array-Based Lists

|

177

else
if (length >= maxSize) //list is full
cerr << "Cannot insert in a full list" << endl;
else
{
for (int i = length; i > location; i--)
list[i] = list[i - 1];
//move the elements down
list[location] = insertItem;

length++;
}
} //end insertAt

//insert the item at the
//specified position

//increment the length

The function insertAt uses a for loop to shift the elements of the list
...
If the
item is to be inserted at the first position, all the elements of the list are shifted
...

The function insertEnd can be implemented by using the function insertAt
...
Therefore, we
give its definition directly
...
Therefore, this function is of O(1)
...
The function
removeAt removes an item from a specific location in the list
...
After removing the item
from the list, the length of the list is reduced by 1
...
Suppose that the data member list of an arrayListType object
is as shown in Figure 3-31
...
)

3

178 |

Chapter 3: Pointers and Array-Based Lists

list

FIGURE 3-31

[0] [1] [2] [3] [4] [5] [6] [7] [8]
35 24 45 17 26 78


...
Thus, after removing an
element, the length of the list is 5
...
Clearly, we must move list[4] into list[3] and list[5] into list[4],
in this order
...

The definition of the function retrieveAt is straightforward
...
Similarly, the definition of the function replaceAt is straightforward
...
" << endl;
else
retItem = list[location];
} //end retrieveAt
template
void arrayListType::replaceAt
(int location, const elemType& repItem)

Array-Based Lists

|

179

{
if (location < 0 || location >= length)
cerr << "The location of the item to be replaced is "
<< "out of range
...
Because
the data member length indicates the number of elements in the list, the elements are
removed by simply setting length to 0
...
The constructor
creates an array of the size specified by the user, and initializes the length of the list to 0
and the maxSize to the size of the array specified by the user
...
The default array size is 100
...
The definition of
the constructor and the destructor are as follows:
template
arrayListType::arrayListType(int size)
{
if (size < 0)
{
cerr << "The array size must be positive
...
" << endl;
maxSize = 100;
}
else
maxSize = size;
length = 0;

}

list = new elemType[maxSize];
assert(list != NULL);

template
arrayListType::~arrayListType()
{
delete [] list;
}

3

180 |

Chapter 3: Pointers and Array-Based Lists

As before, it is easy to see that each of the functions retrieveAt, replaceAt,
clearList, as well as the constructor and destructor is of O(1)
...
It copies the data members of the actual object into the
corresponding data members of the formal parameter and the object being created
...
maxSize;
length = otherList
...
list[j];
} //end copy constructor

Overloading the Assignment Operator
Next, because we are overloading the assignment operator for the class arrayListType,
we give the definition of the function template to overload the assignment operator
...
maxSize;
length = otherList
...
list[i];

return *this;

Array-Based Lists

|

181

Similar to the function print, it is easy to see that both the copy constructor and the
function to overload the assignment operator are of O(n)
...


3

Consider the list of seven elements shown in Figure 3-32
...


List of seven elements

Suppose that you want to determine whether 27 is in the list
...

Because list[0] 6¼ 27, you then compare 27 with list[1] (that is, with 12, the second
item in the list)
...
Because list[2] = 27, the search stops
...

Let us now search for 10
...
This time the search item, which is 10, is compared with every item
in the list
...

This is an unsuccessful search
...
’’ (In this case, you usually also tell the
location in the list where the search item was found
...
’’
Suppose that the name of the array containing the list elements is list
...
Recall that the function insert
inserts a new item at the end of the list if this item does not exist in the list and
the list is not full
...

Chapter 9 explicitly shows that the function seqSearch is of O(n)
...
Because duplicates are not allowed,
this function first searches the list to determine whether the item to be inserted is already
in the list
...
If the item to be
inserted is not in the list, the new item is inserted at the end of the list and the length of
the list is increased by 1
...
The definition of this function is as follows:
template
void arrayListType::insert(const elemType& insertItem)
{
int loc;
if (length == 0)
//list is empty
list[length++] = insertItem;
//insert the item and
//increment the length
else if (length == maxSize)
cerr << "Cannot insert in a full list
...
No duplicates are allowed
...
Because the function seqSearch is of O(n), it follows
that the function insert is of O(n)
...
The item to be deleted is passed as a
parameter to this function
...
If the item to be
deleted is found in the list, the item is removed from the list and the length of the list is
decremented by 1
...
We can now use the index
returned by the function seqSearch, and use the function removeAt to remove the item
from the list
...
" << endl;
else
{
loc = seqSearch(removeItem);
if (loc != -1)
removeAt(loc);
else
cout << "The item to be deleted is not in the list
...
Because each of these functions is of O(n) and because they are called in
sequence, it follows that the function remove is of O(n)
...

TABLE 3-1

Time complexity of list operations

Function

Time-complexity

isEmpty

O (1)

isFull

O (1)

listSize

O (1)

maxListSize

O (1)

3

184 |

Chapter 3: Pointers and Array-Based Lists

TABLE 3-1

Time complexity of list operations (continued)

Function

Time-complexity

print

O (n)

isItemAtEqual

O (1)

insertAt

O (n)

insertEnd

O (1)

removeAt

O (n)

retrieveAt

O (1)

replaceAt

O (n)

clearList

O (1)

constructor

O (1)

destructor

O (1)

copy constructor

O (n)

overloading the assignment
operator

O (n)

seqSearch

O (n)

insert

O (n)

remove

O (n)

The following program tests the various operations on array-based lists
...
S
...

//****************************************************************
#include

//Line 1

#include
#include "arrayListType
...
insertAt(counter, number);
}

//Line
//Line
//Line
//Line
//Line

11
12
13
14
15

cout << endl;
cout << "List 19: The list you entered is: ";
intList
...
remove(number);
cout << "Line 23: After removing " << number
<< ", the list is:" << endl;
intList
...
insertAt(counter, str);
}

//Line
//Line
//Line
//Line
//Line

//Line 23
//Line 24
//Line 25

28
29
30
31
32

cout << endl;
//Line 33
cout << "Line 34: The list you entered is: " << endl; //Line 34
stringList
...
remove(str);
cout << "Line 40: After removing " << str
<< ", the list is:" << endl;

//Line 37
//Line 38
//Line 39
//Line 40

3

186 |

Chapter 3: Pointers and Array-Based Lists

stringList
...

List 10: Enter 5 integers: 23 78 56 12 79
List 19: The list you entered is: 23 78 56 12 79
Line 20: Enter the item to be deleted: 56
Line 23: After removing 56, the list is:
23 78 12 79
Line 27: Enter 5 strings: hello sunny warm winter summer
Line 34: The list you entered is:
hello sunny warm winter summer
Line 37: Enter the string to be deleted: hello
Line 40: After removing hello, the list is:
sunny warm winter summer

The preceding program works as follows
...
The data member list of intList is an array of
100 components and the component type is int
...
The data member list of
stringList is an array of 100 components (the default size) and the component type
is string
...
The
statement in Line 13 gets the next number from the input stream
...

The statement in Line 18 uses the member function print of intList to output the
elements of intList
...
The statement in Line 22 uses the member function remove of
intList to remove the number from intList
...
These statements process a list of strings
...
þ anÀ1 xnÀ1 þ an xn ;
where ai are real (or complex) numbers and n is a nonnegative integer
...
If p(x) is a nonzero constant polynomial,
the degree of p(x) is defined to be 0
...
If p(x) is not constant and an 6¼ 0, n is
called the degree of p(x); that is, the degree of a nonconstant polynomial is defined
to be the exponent of the highest power of x
...
)
The basic operations performed on polynomials are add, subtract, multiply, divide,
and evaluate a polynomial at any given point
...
Moreover,
pð2Þ ¼ 1 þ 2 Á 2 þ 3 Á 22 ¼ 17
pðxÞ þ qðxÞ ¼ 5 þ 3x þ 3x2
pðxÞ À qðxÞ ¼ À3 þ x þ 3x2
pðxÞÃ qðxÞ ¼ 4 þ 9x þ 14x2 þ 3x3
The purpose of this programming example is to design and implement the class
polynomialType to perform the various polynomial operations in a program
...

2
...

4
...

Add polynomials
...

Multiply polynomials
...
You
will be asked in Programming Exercise 8 to generalize it so that the coefficients can
also be complex numbers
...
Let list be an array of size n + 1
...
See Figure 3-33
...


[n-1]

ai

[n]

an–1


...
Suppose that p(x) = 1 + 8x – 3x2 + 5x4 + 7x8
...


[0] [1] [2] [3] [4] [5] [6] [7] [8]
p(x)

FIGURE 3-34

1

–3

8

0

5

0

0

0

7

Polynomial p (x) of degree 8 and its coefficients

Similarly, if q(x) = –5x2 + 16x5, the array storing the coefficient of q(x) is given in
Figure 3-35
...
Suppose that
pðxÞ ¼ a0 þ a1 x þ
...
þ bmÀ1 xmÀ1 þ am xm :
Let t = max(n, m)
...
þ ctÀ1 xtÀ1 þ ct xt ;
where for i = 0, 1, 2,
...
It follows that the
degree of the polynomials is max(n, m)
...
þ dnþm xnþm ;
The coefficient dk, for k ¼ 0, 1, 2,
...
þ ak à b0 ;
where if either ai or bi does not exist, it is assumed to be zero
...

dnþm ¼ an bm
In Chapter 2, you learned how to overload various operators
...
Moreover, we also overload the function call operator, (), to evaluate a
polynomial at a given value
...

Because the coefficients of a polynomial are stored in a dynamic array, we use the
class arrayListType to store and manipulate the coefficients of a polynomial
...

The following class defines polynomials as an ADT:
//***********************************************************
// Author: D
...
Malik
//
// This class specifies the members to implement the basic
// polynomial operations
...

If p(x) is a polynomial of degree 3, we can create an object, say p, of type polynomialType
and set the size of the array list to 4
...

Next we discuss the definitions of the functions
...

polynomialType::polynomialType(int size)
: arrayListType(size)
{
length = size;

}

for (int i = 0; i < size; i++)
list[i] = 0;

The definition of the function to overload the operator () is quite straightforward
and is given next
...
0;
for (int i = 0; i < length; i++)
{
if (list[i] != 0
...

If n ¼ m, the operator + adds the corresponding coefficients of p(x) and q(x)
...
The
remaining coefficients of p(x) are copied into the polynomial containing the sum of p(x)
and q(x)
...
The remaining coefficients of q(x) are copied into the polynomial
containing the sum
...
The definitions of these two operator functions are as follows:
polynomialType polynomialType::operator+
(const polynomialType& right)
{
int size = max(length, right
...
length); i++)
temp
...
list[i];
if (size == length)
for (int i = min(length, right
...
list[i] = list[i];
else
for (int i = min(length, right
...
length;
i++)
temp
...
list[i];
}

return temp;

polynomialType polynomialType::operator(const polynomialType& right)
{
int size = max(length, right
...
length); i++)
temp
...
list[i];
if (size == length)
for (int i = min(length, right
...
list[i] = list[i];
else
for (int i = min(length, right
...
length;
i++)
temp
...
list[i];
}

return temp;

The definition of the function to overload the operator * to multiply two polynomials is left as an exercise for you
...
The definitions of the remaining functions of the class polynomialType
are as follows:
int polynomialType::min(int x, int y) const
{
if (x <= y)
return x;
else
return y;
}
int polynomialType::max(int x, int y) const
{
if (x >= y)
return x;
else
return y;
}
ostream& operator<<(ostream& os, const polynomialType& p)
{
int indexFirstNonzeroCoeff = 0;
for (int i = 0; i < p
...
list[i] != 0
...
length)
{
if (indexFirstNonzeroCoeff == 0)
os << p
...
list[indexFirstNonzeroCoeff] << "x^"
<< indexFirstNonzeroCoeff << " ";
for (int i = indexFirstNonzeroCoeff + 1; i < p
...
list[i] != 0
...
list[i] >= 0
...
list[i] << "x^" << i << " ";
else
os << "- " << -p
...
length - 1 << endl;
for (int i = 0; i < p
...
list[i];
}
}

MAIN
PROGRAM

return is;

//****************************************************************
// Author: D
...
Malik
//
// This program illustrates how to use the class polynomialType
...
h"

//Line 2

using namespace std;

//Line 3

int main()
{
polynomialType p(8);
polynomialType q(4);
polynomialType t;

//Line
//Line
//Line
//Line
//Line

4
5
6
7
8

3

194 |

Chapter 3: Pointers and Array-Based Lists

cin >> p;
cout << endl << "Line 10: p(x): " << p << endl;

//Line 9
//Line 10

cout << "Line 11: p(5): " << p(5) << endl << endl; //Line 11
cin >> q;
cout << endl << "Line 13: q(x): " << q << endl
<< endl;

//Line 12
//Line 13

t = p + q;

//Line 14

cout << "Line 15: p(x) + q(x): " << t << endl;

//Line 15

cout << "Line 16: p(x) - q(x): " << p - q << endl; //Line 16
}

return 0;

//Line 17
//Line 18

Sample Run: In this sample run, the user input is shaded
...

2
...


Pointer variables contain the addresses of other variables as their values
...

A pointer variable is declared using an asterisk, *, between the data type and
the variable
...

5
...

The address of operator returns the address of its operand
...

7
...

When used as a unary operator, * is called the dereferencing operator
...
For example, if p is a pointer variable
of type int, the statement
*p = 25;

8
...

10
...


12
...

14
...

16
...

18
...

20
...

You can use the member access operator arrow, ->, to access the component of an object pointed to by a pointer
...

The only integer value that can be directly assigned to a pointer variable is 0
...

Pointer arithmetic is different from ordinary arithmetic
...

Similarly, when an integer is subtracted from a pointer, the value subtracted
from the value of the pointer variable is the integer times the size of the
object to which the pointer is pointing
...
(It makes
sense to compare pointers of the same type
...

A variable created during program execution is called a dynamic variable
...

The operator delete is used to deallocate the memory occupied by a
dynamic variable
...

The operator new has two forms: one to create a single dynamic variable,
and another to create an array of dynamic variables
...


3

196 |

21
...

23
...


Chapter 3: Pointers and Array-Based Lists

The operator delete has two forms: one to deallocate the memory
occupied by a single dynamic variable, and another to deallocate the
memory occupied by an array of dynamic variables
...

The array name is a constant pointer
...

To create a dynamic array, the form of the new operator that creates an
array of dynamic variables is used
...


26
...


creates an array of 10 components of type int
...
We call p a dynamic array
...
For
example, suppose p is a dynamic array of 10 components
...
In particular, p[i] refers to the (i + 1)th component of the array
...

If p is a dynamic array, then the statement
delete [] p;

28
...

30
...

32
...

34
...

36
...

In a shallow copy, two or more pointers of the same type point to the same
memory space; that is, they point to the same data
...

If a class has a destructor, the destructor automatically executes whenever a
class object goes out of scope
...

A copy constructor executes when an object is declared and initialized by
using the value of another object, and when an object is passed by value as a
parameter
...

The binding of virtual functions occurs at execution time, not at compile
time, and is called dynamic or run-time binding
...

A class is called an abstract class if it contains one or more pure virtual
functions
...


38
...

40
...

In addition to the pure virtual functions, an abstract class can contain instance
variables, constructors, and functions that are not pure virtual
...

A list is a collection of elements of the same type
...


EXERCISES
1
...

a
...

c
...


e
...

In C++, pointer variables are declared using the reserved word pointer
...

The statement delete p; deallocates the dynamic variable to which p
points
...


is valid in C++
...

h
...

The address of operator returns the address and value of its operand
...


|

197

3

198 |

2
...
If a statement is invalid,
explain why
...

b
...


p = x;

d
...


q = &x;

f
...


p = q;

*p = q;

What is the output of the following C++ code?
int x;
int y;
int *p = &x;
int *q = &y;
*p = 35;
*q = 98;
*p = *q;
cout << x << " " << y << endl;
cout << *p << " " << *q << endl;

4
...


Given the declaration
int num = 6;
int *p = #

which of the following statement(s) increment the value of num?
a
...


(*p)++;

c
...


(*num)++;

Exercises

6
...


What is wrong with the following code?
int *p;
int *q;

//Line 1
//Line 2

p = new int;
*p = 43;

//Line 3
//Line 4

q = p;
*q = 52;

//Line 5
//Line 6

delete q;

//Line 7

cout << *p << " " << *q << endl;
9
...


|

//Line 8

What is the output of the following code?
int x;
int *p;
int *q;
p = new int[10] ;
q = p;
*p = 4;
for(int j = 0; j < 10; j++)
{
x = *p;
p++;
*p = x + j;
}

200 |

Chapter 3: Pointers and Array-Based Lists

for (int k = 0; k < 10; k++)
{
cout << *q << " ";
q++;
}
cout << endl;
10
...

12
...

What is wrong with the following code?
int *p;
int *q;
p = new int [5];
*p = 2;

//Line 3
//Line 4

for (int i = 1; i < 5; i++)
p[i] = p[i-1] + i;

//Line 5
//Line 6

q = p;

//Line 7

delete [] p;

//Line 8

for (int j = 0; j < 5; j++)
cout << q[j] << " ";

//Line 9
//Line 10

cout << endl;
13
...


What is the output of the following code?
int **p;
p = new int* [5];
for (int i = 0; i < 5; i++)
p[i] = new int[3];
for (int i = 1; i < 5; i++)
for (int j = 0; j < 3; j++)
p[i][j] = 2 * i + j;
for (int i = 1; i < 5; i++)
{
for (int j = 0; j < 3; j++)
cout << p[i][j] << " ";
cout << endl;
}

15
...

17
...


What is the purpose of the copy constructor?
Name three situations when a copy constructor executes
...

Suppose that you have the following definition of a class
...

private:
int listLength;
int *list;
double salary;
string name;
}
a
...


Write the function prototype to overload the assignment operator for
the class dummyClass
...


3

202 |

Chapter 3: Pointers and Array-Based Lists

Write the function prototype to include the copy constructor for the
class dummyClass
...
Write the definition of the copy constructor for the class dummyClass
...


19
...


return 0;

What is the output of the function main of Exercise 19, if the definition of
classA is replaced by the following definition?
class classA
{
public:
virtual void print() const;
virtual void doubleNum();
classA(int a = 0);
private:
int x;
};

21
...


Chapter 3: Pointers and Array-Based Lists

Consider the following definition of the class student
...


Rewrite the definition of the class student so that the functions print
and calculateGPA are pure virtual functions
...


arrayListType stringList(1000);

c
...


arrayListType intList(100);

b
...
Also show the
inheritance hierarchy
...


2
...
However, if the element to be
removed is at the beginning of the list and the list is fairly large, it could take a
lot of computer time
...
Rewrite the
definition of the function removeAt using this technique
...
Add the function removeAll to the class
arrayListType that would remove all occurrences of a given element
...


Programming Exercises

3
...


5
...


7
...


9
...
Also, write the definition of the function min and a
program to test this function
...
Also, write the definition of the function max and a
program to test this function
...
Redo Programming Example Polynomial Operations so
that these operators are overloaded as nonmember functions
...

Write the definition of the function to overload the operator * (as a member
function) for the class polynomialType to multiply two polynomials
...

Let p(x) = a0 + a1x +
...
The
derivative of p(x), written p'(x), is defined to be p' (x) = a1 + 2a2x2 +
...
If p(x) is constant, then p' (x) ¼ 0
...

The class polynomialType as given in the Programming Example Polynomial Operations processes polynomials with coefficients that are real
numbers
...
Your class must overload
the operators +, -, * to perform addition, subtraction, and multiplication;
and the operator () to evaluate a polynomial at a given complex number
...

Using classes, design an online address book to keep track of the names,
addresses, phone numbers, and dates of birth of family members, close friends,
and certain business associates
...

a
...


c
...
Use the appropriate functions to print and store the address
...

Define a class extPersonType using the class personType (as defined
in Example 1-12, Chapter 1), the class dateType (as designed in Programming Exercise 2 of Chapter 2), and the class addressType
...
Also, add a data member to store the phone number
...
Use constructors to automatically initialize the data members
...
An object of type addressBookType
should be able to process a maximum of 500 entries
...

ii
...

iii
...

iv
...

v
...

vi
...

(Safe Arrays) In C++, there is no check to determine whether the array
index is out of bounds
...
Also, recall that in C++ the array index
starts at 0
...


10
...
Every object of type safeArray should be an array of
type int
...
For example,
safeArray list(2,13);
safeArray yourList(-5,9);

11
...


In this example, list is an array of 11 components, the component type is
int, and the components are list[2], list[3],
...
Also,
yourList is an array of 15 components, the component type is int, and the
components are yourList[-5], yourlist[-4],
...
,
yourList[8]
...
Redesign the class
safeArray using class templates so that the class can be used in any
application that requires arrays to process data
...
A matrix is a set of
numbers arranged in rows and columns
...
If A is a matrix of 5
rows and 6 columns, we say that matrix A is of the size 5 Â 6 and
sometimes denote it as A5Â6
...
Two matrices can be added
and subtracted if they have the same size
...
The sum
and difference of A and B is given by
A þ B ¼ ½aij þ bij Š;

A À B ¼ ½aij À bij Š

The multiplication of A and B (A * B) is defined only if the number
of columns of A are the same as the number of rows of B
...
þ ain bnk

13
...
Overload the operators +, -, and * to perform the addition, subtraction, and multiplication operations, respectively, and overload the operator
<< to output a matrix
...

The class largeIntegers in Programming Exercise 16, in Chapter 2, is
designed to process large integers of at most 100 digits
...
Also overload the multiplication operator to multiply large
integers
...


Learn about the Standard Template Library (STL)


...


Explore how vector and deque containers are used
to manipulate data in a program


...
With the help of class templates, we
developed (and used) a generic code to process lists
...
In Chapters 5,
7, and 8, we will study the three most important data structures: linked lists, stacks,
and queues
...
In addition, using the second principle, inheritance, of objectoriented programming (OOP), we will develop a generic code to process ordered lists
...
Along the way, you will see that a template is a powerful
tool that promotes code reuse
...
Among other things, the STL
provides class templates to process lists (contiguous or linked), stacks, and queues
...
Chapter 13 describes the features of the STL not
described in this chapter
...


Components of the STL
The main objective of a program is to manipulate data and generate results
...

For example, if all the data items are of the same type and we have some idea of the
number of data items, we could use an array to store the data
...
Using a loop and the array index,
we can step through the elements of the array
...

On the other hand, if we do not want to worry about the size of the data, we can use a
linked list, as is described in Chapter 5, to process it
...
Similarly, if the
data needs to be processed in a First In First Out (FIFO) manner, we can use a queue
(Chapter 8)
...
More formally,
the STL has three main components:




Containers
Iterators
Algorithms

Components of the STL

|

211

Containers and iterators are class templates
...
Algorithms are used to manipulate data
...
Algorithms are discussed in Chapter 13
...
The STL containers are classified
into three categories:




Sequence containers (also called sequential containers)
Associative containers
Container adapters

Associative containers are described in Chapter 13, and container adapters are described
in Chapters 7 and 8
...
The three predefined
sequence containers are as follows:




vector
deque
list

Before discussing container types in general, let us first briefly describe the sequence
container vector
...
Also, with the help of vector containers,
we can describe several properties that are common to all containers
...
Of course, there are operations that are
specific to a container
...

This chapter discusses vector and deque containers
...


Sequence Container: vector
A vector container stores and manages its objects in a dynamic array
...
Item
insertion in the middle or beginning of an array is time consuming, especially if the array
is large
...

The name of the class that implements the vector container is vector
...
) The name of the header file containing the class vector
is vector
...
For example, the statement
vector intList;

declares intList to be a vector and the component type to be int
...

DECLARING VECTOR OBJECTS
The class vector contains several constructors, including the default constructor
...

Table 4-1 describes how a vector container of a specific type can be declared and
initialized
...
(The default
constructor is invoked
...

vecList and otherVecList
are of the same type
...

vecList is initialized using the default
constructor
...

vecList is initialized using n copies of
the element elem
...
vecList is
initialized to the elements in the range
[begin, end), that is, all elements in
the range begin
...


Components of the STL

|

213

EXAMPLE 4-1
a
...

vector intList;

b
...
The elements of intList
are initialized to 0
...
The following statement declares intList to be a vector container
of size 5 and the element type is int
...

int intArray[5] = {2,4,6,8,10};
vector intList(intArray, intArray + 5);

The container intList is initialized using the elements of the array
intArray
...

Now that we know how to declare a vector sequence container, let us now discuss how
to manipulate data stored in a vector container
...

TABLE 4-2

Operations to access the elements of a vector container

Expression

Effect

vecList
...


vecList[index]

Returns the element at the position specified by index
...
front()

Returns the first element
...
)

vecList
...
(Does not check whether the
container is empty
...
(Recall that in C++, arrays start at location 0
...
)
EXAMPLE 4-2
Consider the following statement, which declares intList to be a vector container of
size 5 and the element type is int
...

The class vector provides various operations to process the elements of a vector
container
...
Item insertion and
deletion in vecList can be accomplished using the operations given in Table 4-3
...
Table 4-3 also shows how these operations are used
...
clear()

Deletes all elements from the
container
...
erase(position)

Deletes the element at the position
specified by position
...
erase(beg, end)

Deletes all elements starting at beg
until end-1
...
insert(position, elem)

A copy of elem is inserted at the
position specified by position
...


vecList
...


vecList
...


Components of the STL

TABLE 4-3

|

215

Various operations on a vector container (continued)

Expression

Effect

vecList
...


vecList
...


vecList
...
resize(num, elem)

Changes the number of elements to
num
...

Changes the number of elements to
num
...


In Table 4-3, the argument position in STL terminology is called an iterator
...
In general, iterators are used to step through the
elements of a container
...
In the next
section, we describe how to declare an iterator in a vector container and how to
manipulate the data stored in a container
...


The function push_back is quite useful
...
The container intList of size 5 was declared in
Example 4-2
...
However, this is not the case
...
You cannot use the array subscripting operator, as in
Example 4-2, to add elements past the position 4 unless you increase the size of the
container
...
In this case, you can use the function push_back, as shown in Examples
4-3 and 4-5, to add elements into a vector container
...

vector intList;

To add elements into intList, we can use the function push_back as follows:
intList
...
push_back(55);

After these statements execute, the size of intList is 2 and intList = {34, 55}
...
However, at times, the push_back function is
more convenient because it does not need to know the size of the container; it simply
adds elements at the end
...
(Recall that an iterator is just like a pointer
...
Because
the element is to be inserted at a specific position, this would require shifting the elements of
the container (unless the element is added at the end)
...
To make element(s) insertion convenient, the class vector
provides the function insert to insert the elements at a specific position in a vector
container
...
Similarly, the function erase, to remove an element, also
requires the use of an iterator
...

The class vector contains a typedef iterator, which is declared as a public
member
...

For example, the statement
vector::iterator intVecIter;

declares intVecIter to be an iterator into a vector container of type int
...

Suppose that the iterator intVecIter points to an element of a vector container whose
elements are of type int
...
The expression
*intVecIter

returns the element at the current iterator position
...

Recall that when used as a unary operator, * is called the dereferencing operator
...
Suppose that we have the following statements:
vector intList;
vector::iterator intVecIter;

//Line 1
//Line 2

The statement in Line 1 declares intList to be a vector container and the element type
to be int
...


Containers and the Functions begin and end
Every container contains the member functions begin and end
...
These functions have no parameters
...
begin();

the iterator intVecIter points to the first element into the container intList
...
begin(); intVecIter != intList
...
Now
consider the following statements:
intVecIter = vecList
...
insert(intVecIter, 22};

//Line 4
//Line 5
//Line 6

4

218 |

Chapter 4: Standard Template Library (STL) I

The statement in Line 4 initializes the iterator intVecIter to the first element of
vecList
...
The statement in Line 6 inserts 22 at the position specified by intVecIter
...
Notice
that the size of the container also increases
...
Table 4-4 describes some of these operations
...
)
TABLE 4-4

Functions to determine the size of a vector container

Expression

Effect

vecCont
...


vecCont
...


vecCont
...


vecCont
...


Example 4-5 illustrates how to use a vector container in a program and how to process
the elements into a vector container
...
S
...

//***********************************************************
#include
#include

//Line 1
//Line 2

using namespace std;

//Line 3

Components of the STL

int main()
{
vector

219

//Line 4
//Line 5
//Line 6

intList;

intList
...
push_back(75);
intList
...
push_back(35);

//Line
//Line
//Line
//Line

7
8
9
10

cout << "Line 11: List Elements: ";
for (int i = 0; i < 4; i++)
cout << intList[i] << " ";
cout << endl;

//Line
//Line
//Line
//Line

11
12
13
14

for (int i = 0; i < 4; i++)
intList[i] *= 2;

//Line 15
//Line 16

cout << "Line 17: List Elements: ";
for (int i = 0; i < 4; i++)
cout << intList[i] << " ";
cout << endl;

//Line
//Line
//Line
//Line

vector::iterator

//Line 21

listIt;

17
18
19
20

cout << "Line 22: List Elements: ";
//Line 22
for (listIt = intList
...
end(); ++listIt) //Line 23
cout << *listIt << " ";
//Line 24
cout << endl;
//Line 25
listIt = intList
...
insert(listIt, 88);

//Line
//Line
//Line
//Line

26
27
28
29

cout << "Line 30: List Elements: ";
//Line 30
for (listIt = intList
...
end(); ++listIt) //Line 31
cout << *listIt << " ";
//Line 32
cout << endl;
//Line 33
}

|

return 0;

//Line 34
//Line 35

Sample Run:
Line
Line
Line
Line

11:
17:
22:
30:

List
List
List
List

Elements:
Elements:
Elements:
Elements:

13
26
26
26

75 28 35
150 56 70
150 56 70
150 88 56 70

4

220 |

Chapter 4: Standard Template Library (STL) I

The statement in Line 6 declares a vector container (or vector for short), intList, of
type int
...
The statements in Lines 12 and 13
use the for loop and the array subscripting operator [] to output the elements of
intList
...
The statements in Lines 15 and 16 use a for loop to
double the value of each element of intList; the statements in Lines 18 and 19 output
the elements of intList
...

The statement in Line 21 declares listIt to be a vector iterator that processes any vector
container whose elements are of type int
...
After the statement in Line 26 executes,
listIt points to the first element of intList
...
The statement in Line 29 inserts 88 into intList at the position
specified by the iterator listIt
...
The statements in Lines 31 and 32 output the
modified intList
...
We now look at operations that are
common to all containers
...

Recall that a class encapsulates data, and operations on that data, into a single unit
...
Also, recall that the operations to
manipulate the data are implemented with the help of functions and are called member
functions of the class
...

Suppose that ct, ct1, and ct2 are containers of the same type
...


Components of the STL

TABLE 4-5

|

221

Member functions common to all containers

Member function

Effect

Default constructor

Initializes the object to an empty state
...
We
describe these constructors when we discuss a
specific container
...


Destructor

Executes when the object goes out of scope
...
empty()

Returns true if container ct is empty and
false otherwise
...
size()

Returns the number of elements currently in
container ct
...
max_size()

Returns the maximum number of elements that
can be inserted into container ct
...
swap(ct2)

Swaps the elements of containers ct1 and ct2
...
begin()

Returns an iterator to the first element into
container ct
...
end()

Returns an iterator to the last element into
container ct
...
rbegin()

Reverse begin
...
This function is used
to process the elements of ct in reverse
...
rend()

Reverse end
...


ct
...
Note
that here position is an iterator
...
erase(begin, end)

Deletes all elements between begin
...


4

222 |

Chapter 4: Standard Template Library (STL) I

TABLE 4-5

Member functions common to all containers (continued)

Member function

Effect

ct
...
After a
call to this function, container ct is empty
...
After this
operation, the elements in both containers are
the same
...


ct1 != ct2

Returns true if containers ct1 and ct2 are
not equal and false otherwise
...


ct1 <= ct2

Returns true if container ct1 is less than or
equal to container ct2 and false otherwise
...


ct1 >= ct2

Returns true if container ct1 is greater than
or equal to container ct2 and false
otherwise
...


Member Functions Common to Sequence Containers
The previous section described the member functions that are common to all containers
...
(Suppose that seqCont is a sequence container
...
insert(position, elem)

A copy of elem is inserted at the
position specified by position
...


seqCont
...


seqCont
...


seqCont
...


seqCont
...


seqCont
...


seqCont
...


seqCont
...


seqCont
...
If size() grows, the
new elements are created by their
default constructor
...
resize(num, elem)

Changes the number of elements to
num
...


The copy Algorithm
Example 4-5 used a for loop to output the elements of a vector container
...
The function copy is provided as a part of the generic algorithms of the STL
and can be used with any container type as well as arrays
...


4

224 |

Chapter 4: Standard Template Library (STL) I

Like the function copy, the STL contains many functions as part of generic algorithms,
which are described in Chapter 13
...
In general, it
allows us to copy the elements from one place to another
...
The prototype of the function template copy is as follows:
template
outputItr copy(inputIterator first1, inputIterator last,
outputIterator first2);

The parameter first1 specifies the position from which to begin copying the elements;
the parameter last specifies the end position
...
Therefore, the parameters first1 and last specify the source, and
the parameter first2 specifies the destination
...
last-1 are copied
...

Thus, to use the function copy, the program must include the statement
#include

The function copy works as follows
...

The statement in Line 2 creates an empty container of nine components of type vector
and the element type int
...
Therefore, intArray points to the first component of the array,
intArray + 1 points to the second component of the array, and so on
...
begin());

//Line 3

This statement copies the elements starting at the location intArray, which is the first
component of the array intArray, until intArray + 9 - 1 (that is, intArray + 8),
which is the last element of the array intArray, into the container vecList
...
begin()
...
Also, first2 is intArray;
that is, first2 points to the location of the first element of the array intArray
...
After the statement in Line 5
executes,
intArray[] = {6, 8, 3, 40, 36, 98, 29, 75, 75}

//Line 6

Notice that the elements of the array intArray are shifted to the left by one position
...
Consider the statement
copy(vecList
...
rend(),
vecList
...
Therefore,
vecList
...
Similarly, the function rend (reverse end) returns a pointer to the first element
of a container
...
After the statement in Line 7 executes, the container vecList
is as follows:
vecList = {5, 6, 5, 6, 8, 3, 40, 36, 98}

Example 4-6 shows the effect of the preceding statements using a C++ program
...


ostream Iterator and Function copy
One way to output the contents of a container is to use a for loop and the function
begin to initialize the for loop control variable, and to use the function end to set the
limit
...

In this case, an iterator of type ostream specifies the destination (ostream iterators are
discussed in detail later in this chapter)
...

The following statement illustrates how to create an ostream iterator of type int:
ostream_iterator screen(cout, " "); //Line A

4

226 |

Chapter 4: Standard Template Library (STL) I

This statement creates screen to be an ostream iterator with the element type int
...
Thus, the iterator
screen is initialized using the object cout, and when this iterator outputs the elements
they are separated by a space
...

Similarly, the statement
copy(vecList
...
end(), screen);

outputs the elements of the container vecList on the screen
...
Also, until we discuss ostream iterators in detail, we will use
statements similar to the statement in Line A to create an ostream iterator
...
For
example, the statement (shown previously)
copy(vecList
...
end(), screen);

is equivalent to the statement
copy(vecList
...
end(), ostream_iterator(cout, " "));

Finally, the statement
copy(vecList
...
end(),
ostream_iterator(cout, ", "));

outputs the elements of vecList with a comma and space between them
...

EXAMPLE 4-6
//***********************************************************
// Author: D
...
Malik
//
// This program illustrates how to use the function copy and
// an ostream iterator in a program
...
begin());

//Line 14

cout << "Line 15: vecList: ";
copy(vecList
...
end(), screen);
cout << endl;

//Line 15
//Line 16
//Line 17

copy(intArray + 1, intArray + 9, intArray);
cout << "Line 19: After shifting the elements one "
<< "position to the left, intArray: " << endl;
copy(intArray, intArray + 9, screen);
cout << endl;

//Line 18
//Line 19
//Line 20
//Line 21

copy(vecList
...
rend(),
vecList
...
begin(), vecList
...
The term deque stands for
double-ended queue
...
Thus, a deque can expand in either

4

228 |

Chapter 4: Standard Template Library (STL) I

direction
...
Inserting elements at the beginning
or at the end is fast; inserting elements in the middle, however, is time consuming
because the elements in the queue need to be shifted
...
The definition of the
class deque, and the functions to implement the various operations on a deque object,
are also contained in the header file deque
...
Thus, a deque object can be initialized
in various ways when it is declared, as described in Table 4-7
...
(The default constructor is
invoked
...


deque
deq(size);

Creates a deque container, deq, of size
size
...


deque
deq(n, elem);

Creates a deque container, deq, of size n
...


deque
deq(begin, end);

Creates a deque container, deq
...
end-1
...
The name of the function implementing the operations is shown in bold
...
Suppose that deq is a deque
container
...
assign(n,elem)

Assigns n copies of elem
...
assign(beg,end)

Assigns all the elements in the range beg
...


deq
...


deq
...


deq
...


deq[index]

Returns the element at the position specified by
index
...
front()

Returns the first element
...
)

deq
...
(Does not check whether
the container is empty
...

EXAMPLE 4-7
//***********************************************************
// Author: D
...
Malik
//
// This program illustrates how to use a deque container in a
// program
...
push_back(13);
intDeq
...
push_back(28);
intDeq
...
begin(), intDeq
...
push_front(0);
intDeq
...
begin(), intDeq
...
pop_front();
intDeq
...
begin(), intDeq
...
pop_back();
intDeq
...
begin(), intDeq
...
begin();
//Line 33
++deqIt;
//deqIt points to the second element
//Line 34
intDeq
...
begin(), intDeq
...
The statement in Line 9 declares screen to be
an ostream iterator initialized to the standard output device
...
The statement in Line 15 outputs the elements of intDeq
...

The statement in Line 17 inserts 0 at the beginning of intDeq
...
The statement in Line 20 outputs the
modified intDeq
...
The statements in Lines 27 and 28 use the operation pop_back to remove
the last two elements of intDeq; the statement in Line 30 outputs the modified
intDeq
...
After the statement in Line 33
executes, deqIt points to the first element of intDeq
...
The statement in Line 35 inserts 444
into intDeq at the position specified by deqIt
...


Iterators
Examples 4-5 through 4-7 further clarify that iterators are quite important to efficiently
process the elements of a container
...

Iterators work just like pointers
...
Thus, with the help of iterators, we can successively
access each element of a container
...
Suppose that cntItr is an iterator into a container
...
The statement
*cntItr;

returns the value of the element of the container pointed to by cntItr
...
In the next few sections, we describe
these iterators
...
These iterators are provided for reading data from an input stream
...
Table 4-9 describes the operations on
inputIterator
...


inputIterator->member

Gives access to the member of the element
...


inputIterator++

Moves forward, returns the old position (postincrement)
...


inputIt1 != inputIt2

Returns true if the two iterators are not the same
and false otherwise
...


Output Iterators
Output iterators, with write access, step forward element-by-element
...

Suppose outputIterator is an output iterator
...


Iterators |

TABLE 4-10

233

Operations on an output iterator

Expression

Effect

*outputIterator = value;

Writes the value at the position specified
by the outputIterator
...


outputIterator++

Moves forward, returns the old position
(postincrement)
...


Output iterators cannot be used to iterate over a range twice
...


Forward Iterators
Forward iterators combine all of the functionality of input iterators and almost all of the
functionality of output iterators
...
Table
4-11 describes the operations on forwardIterator
...


forwardIterator->member

Gives access to the member of the element
...


forwardIterator++

Moves forward, returns the old position
(postincrement)
...


forwardIt1 != forwardIt2

Returns true if the two iterators are not
the same and false otherwise
...


4

234 |

Chapter 4: Standard Template Library (STL) I

A forward iterator can refer to the same element in the same collection and process the
same element more than once
...
Suppose biDirectionalIterator is a bidirectional iterator
...
To step backward, the decrement operations are also defined for
biDirectionalIterator
...

TABLE 4-12

Additional operations on a bidirectional iterator

Expression

Effect

--biDirectionalIterator

Moves backward, returns the new position
(predecrement)
...


Bidirectional iterators can be used only with containers of type vector, deque, list,
set, multiset, map, and multimap
...
These iterators can be used with containers of type vector, deque, string,
and arrays
...
Table 4-13 describes the additional
operations that are defined for random access iterators
...
)

Iterators |

TABLE 4-13

235

Additional operations on a random access iterator

Expression

Effect

rAccessIterator[n]

Accesses the nth element
...


rAccessIterator -= n

Moves rAccessIterator backward
n elements if n >= 0 and forward
if n < 0
...


n + rAccessIterator

Returns the iterator of the next nth element
...


rAccessIt1 - rAccessIt2

Returns the distance between the iterators
rAccessIt1 and rAccessIt2
...


rAccessIt1 <= rAccessIt2

Returns true if rAccessIt1 is before or
equal to rAccessIt2 and false
otherwise
...


rAccessIt1 >= rAccessIt2

Returns true if rAccessIt1 is after
or equal to rAccessIt2 and false
otherwise
...


Input iterators

Output iterators

Forward iterators

Bidirectional iterators

Random access iterators

FIGURE 4-1

Iterator hierarchy

Now that you know the different types of iterators, next we describe how to declare an
iterator to a container
...
Thus, an iterator into a container is declared using the typedef iterator
...
Moreover,
the iterator intVecIter can be used on any vector, but not on any other
container, such as vector, vector, and deque
...

typedef const_iterator Because an iterator works like a pointer, with the help
of an iterator into a container and the dereferencing operator, *, we can modify the
elements of the container
...

To handle this situation, every container contains another typedef const_iterator
...
The iterator intConstVecIt is used to process the elements of those vector
containers that are declared as constant vector containers of type vector
...


Iterators |

237

typedef reverse_iterator Every container also contains the typedef
reverse_iterator
...

typedef const_reverse_iterator An iterator of this type is a read-only iterator
and is used to iterate through the elements of a container in reverse
...


In addition to the previous four typedefs, several other typedefs are common to all
containers
...

TABLE 4-14

Various typedefs common to all containers

typedef

Effect

difference_type

The type of result from subtracting two iterators
referring to the same container
...


reference

A reference to the type of elements stored in the
container
...
A constant reference is read-only
...
This
type is also used to index through sequence containers,
except list containers
...


Stream Iterators
Another useful set of iterators is stream iterators—istream iterators and ostream
iterators
...

istream_iterator The istream iterator is used to input data into a program from
an input stream
...
The general syntax to use an istream iterator is as follows:
istream_iterator isIdentifier(istream&);

4

238 |

Chapter 4: Standard Template Library (STL) I

where Type is either a built-in type or a user-defined class type, for which an input
iterator is defined
...

ostream_iterator The ostream iterators are used to output data from a program into
an output stream
...
We review them
here for the sake of completeness
...
The
general syntax to use an ostream iterator is as follows:
ostream_iterator osIdentifier(ostream&);

or
ostream_iterator osIdentifier(ostream&, char* deLimit);

where Type is either a built-in type or a user-defined class type, for which an output
iterator is defined
...
In the second form used for declaring an ostream
iterator, by using the second argument (deLimit) of the initializing constructor, we can
specify a character separating the output
...
The registrar’s office
wants to prepare the grade reports as soon as the students’ grades are recorded
...

If a student has paid the tuition, the grades are shown on the grade report together
with the grade point average (GPA)
...
For these students, the grade report contains a message indicating that
the grades have been held for nonpayment of the tuition
...

The registrar’s office and the business office want your help in writing a program that
can analyze the students’ data and print the appropriate grade reports
...

studentName studentID isTuitionPaid
courseName courseNumber creditHours
courseName courseNumber creditHours

...
The students’ data is given
thereafter
...


The first line indicates that the tuition rate is $345 per credit hour
...
The course number for the mathematics class she is taking is
MTH345, the course has 4 credit hours, her midsemester grade is A, and so on
...
54

This output shows that the courses must be ordered according to the course number
...

Input

A file containing the data in the form given previously
...
txt
...
Let us assume
that the name of the output file is stDataOut
...


240 |

Chapter 4: Standard Template Library (STL) I

PROBLEM
ANALYSIS
AND
ALGORITHM
DESIGN

We must first identify the main components of the program
...
Thus, the two main components are the
student and the course
...
Although the grade a student receives is not really a characteristic of
a course, to simplify the program this component also includes the student’s grade
...


Some of the basic operations that need to be performed on an object of the course
type are as follows:
1
...

3
...

5
...

Print the course information
...

Show the course number
...


The following class defines the course as an ADT:
class courseType
{
public:
void setCourseInfo(string cName, string cNo,
char grade, int credits);
//Function to set the course information
//The course information is set according to the
//incoming parameters
...

int getCredits();
//Function to return the credit hours
//The value of the private data member courseCredits
//is returned
...


Programming Example: Grade Report |

bool
bool
bool
bool
bool
bool

241

operator==(const courseType&) const;
operator!=(const courseType&) const;
operator<=(const courseType&) const;
operator<(const courseType&) const;
operator>=(const courseType&) const;
operator>(const courseType&) const;

courseType(string cName = "", string cNo = "",
char grade = '*', int credits = 0);
//Constructor
//The object is initialized according to the parameters
...


courseType
–courseName: string
–courseNo: string
–courseGrade: char
–courseCredits: int
+setCourseInfo(string, string, char, int): void
+print(ostream&, bool): void
+getCredits(): int
+getCourseNumber(string&): void
+getGrade(): char
+operator==(const courseType&) const: bool
+operator!=(const courseType&) const: bool
+operator<=(const courseType&) const: bool
+operator<(const courseType&) const: bool
+operator>=(const courseType&) const: bool
+operator>(const courseType&) const: bool
+courseType(string = "", string = "", char = '*', int = 0)

FIGURE 4-2

UML class diagram of the class courseType

Next, we discuss the definition of the functions to implement the operations of the
class courseType
...


The function setCourseInfo sets the values of the private data members according to the values of the parameters
...
If the bool parameter isGrade
is true, the grade is printed on the screen; otherwise, three stars are shown in place
of the grade
...
Thus, we need to set the left manipulator
...
The
following steps describe this function:
1
...

3
...

5
...


Set the left manipulator
...

Print the course name
...

Print the credit hours
...


The definition of the function print is as follows:
void courseType::print(ostream& outp, bool isGrade)
{
outp << left;
//Step
outp << setw(8) << courseNo << "
";
//Step
outp << setw(15) << courseName;
//Step
outp
...
If no values are specified when a
courseType object is declared, the constructor uses the default to initialize the object
...
Otherwise, the values specified in the object declaration are used to initialize the
object
...

int courseType::getCredits()
{
return courseCredits;
}
char courseType::getGrade()
{
return courseGrade;
}
void courseType::getCourseNumber(string& cNo)
{
cNo = courseNo;
}
bool courseType::operator==(const courseType& right) const
{
return (courseNo == right
...
courseNo);
}
bool courseType::operator<=(const courseType& right) const
{
return (courseNo <= right
...
courseNo);
}
bool courseType::operator>=(const courseType& right) const
{
return (courseNo >= right
...
courseNo);
}

4

244 |

Chapter 4: Standard Template Library (STL) I

Next we discuss the component student
...

Because every student has to pay tuition, we also include a member to indicate
whether the student has paid the tuition
...
We have already designed a
class personType to process a person’s first name and last name
...
Thus, we see that we can
derive the class studentType to keep track of a student’s information from the
class personType, and one member of this class is of type courseType
...

The basic operations to be performed on an object of type studentType are as follows:
1
...

3
...

5
...


Set the student information
...

Calculate the number of credit hours taken
...

Calculate the billing amount
...


The following class defines studentType as an ADT
...

void print(ostream& out, double tuitionRate);
//Function to print the student's grade report
//The output is stored in a file specified by the
//parameter out
...


Programming Example: Grade Report |

245

int getHoursEnrolled();
//Function to return the credit hours a student
//is enrolled in
...

double getGpa();
//Function to return the grade point average
...

double billingAmount(double tuitionRate);
//Function to return the tuition fees
//Postcondition: The tuition fees due is calculated
//
and returned
...


studentType
–sId: int
–numberOfCourses: int
–isTuitionPaid: bool
–coursesEnrolled: vector
+setInfo(string, string, int, bool,
vector): void
+print(ostream& out, double tuitionRate): void
+getHoursEnrolled(): int
+getGpa(): double
+billingAmount(double): double
+studentType()

FIGURE 4-3

personType

studentType

UML class diagram of the class studentType and the inheritance hierarchy

Next, we discuss the definitions of the functions to implement the operations of the
class studentType
...
The class studentType is derived from the class
personType, and the variables to store the first name and last name are private data

4

246 |

Chapter 4: Standard Template Library (STL) I

members of that class
...
To sort the array coursesEnrolled we use the algorithm sort provided by
the STL
...

When we declare the vector coursesEnrolled, we did not specify its size
...

Therefore, coursesEnrolled
...
end() specifies the position
of the last element
...
The following statement sorts the
vector coursesEnrolled
...
begin(), coursesEnrolled
...
size();
coursesEnrolled = courses;
}

sort(coursesEnrolled
...
end());

The default constructor initializes the private data members to the default values
...

studentType::studentType()
{
numberOfCourses = 0;
sId = 0;
isTuitionPaid = false;
}

Programming Example: Grade Report |

247

The function print prints the grade report
...
Otherwise, three stars are printed in place of each grade,
the GPA is not shown, a message indicates that the grades are being held for nonpayment
of the tuition, and the amount due is shown
...

2
...

4
...

6
...


Output the student’s name
...

Output the number of courses in which enrolled
...

Print the total credit hours
...
Also,
set the precision to two decimal places
...
if isTuitionPaid is true
Output the GPA
else

Output the billing amount and a message about withholding the
grades
...
unsetf(ios::left);
for (int i = 0; i < numberOfCourses; i++)
coursesEnrolled[i]
...
***" << endl;
outp << "Amount Due: $" << billingAmount(tuitionRate)
<< endl;
}

}

outp << "-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
<< "-*-*-*-*-" << endl << endl;

The function getHoursEnrolled calculates and returns the total credit hours that a
student is taking
...
The total credit hours are calculated by adding the credit hours of
each course in which the student is enrolled
...
Therefore, we use the
member function getCredits of the class courseType to retrieve the credit
hours
...
getCredits();
return totalCredits;

If a student has not paid the tuition, the function billingAmount calculates and
returns the amount due, based on the number of credit hours enrolled
...
This function calculates a student’s GPA
...
The definition of this
function is as follows:
double studentType::getGpa()
{
double sum = 0
...
getGrade())
{
case 'A':
sum += coursesEnrolled[i]
...
getCredits() * 3;
break;
case 'C':
sum += coursesEnrolled[i]
...
getCredits() * 1;
break;
case 'F':
break;

}

}

MAIN
PROGRAM

default:
cout << "Invalid Course Grade" << endl;
}

if (getHoursEnrolled() != 0)
return sum / getHoursEnrolled();
else
return 0;

Now that we have designed the classes courseType and studentType, we will use
these classes to complete the program
...
In fact, all that is left
for the main program is to declare the objects to hold the students’ data, load the data
into these objects, and then print the grade reports
...
Essentially, the main algorithm for the program is as follows:
1
...

3
...


Declare the variables
...

If the input file does not exist, exit the program
...


4

250 |

Chapter 4: Standard Template Library (STL) I

5
...

6
...

7
...

Variables

To store students’ data, we use the vector container, studentList, whose
elements are of type studentType
...
Because the
data will be read from a file, and because the output is sent to a file, we need two stream
variables to access the input and output files
...
The next two sections describe these functions
...
In pseudocode, the definition of this
function is as follows:
For each student in the university,
1
...

2
...
Get the number of courses the student is taking
...
For each course
a
...

b
...

c
...

5
...

6
...

We need to declare several local variables to read and store the data
...
clear();
for (int i = 0; i < noOfCourses; i++)
//Step 4
{
infile >> cName >> cNo >> credits >> grade; //Step 4
...
setCourseInfo(cName, cNo,
grade, credits);
//Step 4
...
push_back(cTemp);
//Step 4
...
setInfo(fName, lName, ID, isTuitionPaid,
courses);
studentList
...
For each student, it calls the function print of
the class studentType to print the grade report
...
size(); count++)
studentList[count]
...
S
...

//****************************************************************
#include
#include
#include
#include
#include
#include








#include "studentType
...
open("stData
...
"
<< "Program terminates
...
open("stDataOut
...
54
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*Student Name: Bill Wilton
Student ID: 798324
Number of courses enrolled: 5
Course No
BIO234
CHM256
ENG378
MTH346
PHL534

Course Name
Biology
Chemistry
English
Mathematics
Philosophy

Credits Grade
4
***
4
***
3
***
3
***
3
***

Total number of credit hours: 17
*** Grades are being held for not paying the tuition
...
00
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

4

254 |

Chapter 4: Standard Template Library (STL) I

Student Name: Dandy Goat
Student ID: 746333
Number of courses enrolled: 6
Course No
BUS128
CHM348
CSC201
ENG328
HIS101
MTH137

Course Name
Business
Chemistry
ComputerSci
English
History
Mathematics

Credits Grade
3
C
4
B
3
B
3
B
3
A
3
A

Total number of credit hours: 19
Midsemester GPA: 3
...

2
...

4
...

The three main components of the STL are containers, iterators, and
algorithms
...

Iterators are used to step through the elements of a container
...

6
...

8
...

10
...

12
...

14
...

16
...


18
...

20
...

22
...

24
...

The main categories of containers are sequence containers, associative
containers, and container adapters
...

A vector container stores and manages its objects in a dynamic array
...

The name of the class that implements the vector container is vector
...

Item deletion in a vector container is accomplished by using the operations
pop_back, erase, and clear
...

Member functions common to all containers are the default constructor,
constructors with parameters, the copy constructor, the destructor, empty,
size, max_size, swap, begin, end, rbegin, rend, insert, erase,
clear, and the relational operator functions
...

The member function end returns an iterator to the last element into the
container
...

The copy algorithm is used to copy the elements in a given range to
another place
...

When we create an iterator of the type ostream, we also specify the type
of element that the iterator will output
...

A deque can expand in either direction
...

In addition to the operations that are common to all containers, the other
operations that can be used to manipulate the elements of a deque are
assign, push_front, pop_front, at, array subscripting operator [],
front, and back
...

26
...

28
...

30
...

32
...

Input iterators are used to input data from an input stream
...

A forward iterator can refer to the same element in the same collection and
process the same element more than once
...

Bidirectional iterators can be used with containers of type list, set,
multiset, map, and multimap
...

Random access iterators can be used with containers of type vector,
deque, string, and arrays
...

2
...

4
...

Write a statement that declares and stores the elements of the following
array into a vector object:
char vowels[5] = {'a', 'e', 'i', 'o', 'u'};

5
...


Write a statement to declare screen to be an ostream_iterator initialized
to the standard output device that outputs the elements of an int vector object
...
Moreover, suppose that
screen is an ostream_iterator initialized to the standard output device
to output the elements of an int vector object
...
begin(), vecList
...


What is the output of the following program segment?
vector vecList(5);
for (int j = 0; j < 5; j++)
vecList[j] = 2 * j;
for (int j = 0; j < 5; j++)
cout << vecList[j] << " ";
cout << endl;

Exercises

8
...
)
int list[5] = {2,4,6,8,10};
vector vecList(5);
copy(list, list + 5, vecList
...
begin(), vecList
...


What is the output of the following program segment? (Assume that
screen is an ostream_iterator initialized to the standard output device
to output elements of type int
...
push_back(3);
vecList
...
push_back(7);
vecIt = vecList
...
erase(vecIt);
vecList
...
begin(), vecList
...


What is the output of the following program segment? (Assume that
screen is an ostream_iterator initialized to the standard output device
to output elements of type int
...
begin());
vecList
...
begin(), vecList
...


What is the output of the following program segment? (Assume that
screen is an ostream_iterator initialized to the standard output device
to output elements of type double
...
00;
sales[1] = 75
...
00;

4

258 |

Chapter 4: Standard Template Library (STL) I

sales
...
00;
sales[4] = 95
...
begin(), sales
...


What is the output of the following program segment? (Assume that
screen is an ostream_iterator initialized to the standard output device
that outputs elements of type int
...
push_back(15);
intVector
...
push_back(10);
intVector
...
begin();
vecIt++;
intVector
...
pop_back();
copy(intVector
...
end(), screen);

13
...
begin() + 2, vecList
...
begin());
14
...
rbegin() + 3, vecList
...
rbegin());
15
...
push_back(5);
intDeq
...
push_front(45);
intDeq
...
push_front(0);
intDeq
...
push_front(34);
deqIt = intDeq
...
insert(deqIt,76);
intDeq
...
begin();
++deqIt;
++deqIt;
intDeq
...
push_front(2 * intDeq
...
push_back(3 * intDeq
...
begin(), intDeq
...


Write a program that allows the user to enter the last names of five candidates in
a local election and the votes received by each candidate
...
Your program should
also output the winner of the election
...
91
Miller
4000
20
...
09
Robinson
2500
12
...
33
Total
19300
The Winner of the Election is Duffy
...


Write a program that allows the user to input the students’ names followed
by their test scores and outputs the following:
Class average
b
...
Highest test score and the names of all students having the highest score
Write a program that uses a vector object to store a set of real numbers
...
When declaring the vector object, do not specify its size
...

Write the definition of the function template reverseVector to reverse the
elements of a vector object
...


3
...


template
void reverseVector(vector &list);
//Reverses the elements of the vector list
...

//
After a call to this function, list = {5, 2, 8, 4}
...


Chapter 4: Standard Template Library (STL) I

Also, write a program to test the function reverseVector
...
Use the function push_back to
insert elements in the vector object
...

template
int seqSearch(const vector &list, const elemType& item);
//If item is found in the list, returns the
//position of the item in the list; otherwise, returns -1
...


Also, write a program to test the function seqSearch
...

Write a program to find the mean and standard deviation of numbers
...
, xn is x ¼ (x1 + x2 +
...

The standard deviation of these numbers is as follows:
sffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffi
ðx1 À xÞ2 þ ðx2 À xÞ2 þ Á Á Á þ ðxi À xÞ2 þ Á Á Á þ ðxn À xÞ2

n

7
...

a
...
Design the class bookType that
defines the book as an ADT
...

c
...
To keep track of the number of authors,
add another data member
...
For example, the typical operations that can be performed on the title are to show the title, set the title, and check whether a title
is the same as the actual title of the book
...
Add similar
operations for the publisher, ISBN, book price, and authors
...

Write the definitions of the member functions of the class bookType
...
Declare a vector container of
type bookType
...


Programming Exercises

8
...


|

261

In the first part of this exercise, you will design a class memberType
...

ii
...
Similarly, update, modify, and show the
number of books bought and the amount spent
...

Add the appropriate constructors and a destructor (if one is needed)
...

Write the definitions of the member functions of memberType
...
Using the classes designed in Programming Exercise 7 and part (8a),
write a program to simulate a bookstore
...
Each member has to
pay a $10 yearly membership fee and receives a 5% discount on each
book bought
...
For every eleventh book that a
member buys, the bookstore takes the average of the total amount of
the last 10 books bought, applies this amount as a discount, and then
resets the total amount spent to 0
...

Redo Programming Exercise 9 of Chapter 3 so that the address book is
stored in a vector object
...
The company invests only in the stock market
...
We assume that the company invests in, say, 10 different stocks
...

The input data is stored in a file in the following format:
i
...


10
...
50 115
...
50 111
...
50 6723823
CBA 67
...
50 78
...
50 65
...


...


4

262 |

Chapter 4: Standard Template Library (STL) I

The first line indicates that the stock symbol is MSMT, today’s opening price
was 112
...
75, today’s high price was 116
...
75, yesterday’s closing price was 113
...

The listing sorted by stock symbols must be in the following form:
********* First Investor's Heaven **********
*********
Financial Report
**********
Stock
Today
Previous Percent
Symbol Open
Close High
Low Close Gain Volume
------ -------- ------ ----- ---- -----ABC
123
...
95 132
...
00 120
...
67% 10000
AOLK
80
...
00 82
...
00 83
...
64% 5000
CSCO 100
...
00 105
...
00 101
...
99% 25000
IBD
68
...
00 72
...
00 75
...
33% 15000
MSET 120
...
00 145
...
00 115
...
74% 30920
Closing Assets: $9628300
...
In the first step (part a),
design and implement a stock object
...

a
...
Call the class
that captures the various characteristics of a stock object stockType
...
Moreover, we need to output the opening price, high
price, low price, previous price, and the percent gain/loss for the day
...
Therefore, the stock
object should store all this information
...

ii
...

iv
...

a
...


a
...

a
...


Set the stock information
...

Show the different prices
...

Show the number of shares
...
Overload the
relational operators to compare two stock objects by their symbols
...

Because data is stored in a file, overload the stream extraction operator,
>>, for easy input
...
Further suppose that
myStock is a stock object
...


|

263

reads data from the input file and stores it in the object myStock
...
)
Now that you have designed and implemented the class stockType
to implement a stock object in a program, it is time to create a list of
stock objects
...
To store the list of stocks, you need to declare a
vector
...

Because the company also requires you to produce the list ordered by
the percent gain/loss, you need to sort the stock list by this component
...

To do so, add a data member, a vector, to hold the indices of the stock list
ordered by the component percent gain/loss
...

When printing the list ordered by the component percent gain/loss, use
the array indexByGain to print the list
...

In skeleton form, the definition of the class stockListType is as follows:
class stockListType
{
public:
void insert(const stockType& item));
//Function to insert a stock in the list
...

private:
vector indexByGain;
vector list; //vector to store the list //of stocks
};

c
...


4

This page intentionally left blank

5

CHAPTER

L INKED L ISTS
I N T H I S C H A P T E R , YO U W I L L :


...


Become aware of the basic properties of linked lists


...


Discover how to build and manipulate a linked list


...


Discover how to use the STL container list


...


Become aware of circular linked lists

266 |

Chapter 5: Linked Lists

You have already seen how data is organized and processed sequentially using an array, called a
sequential list
...
You also found that if data is not sorted, searching for an item in the list
can be very time consuming, especially with large lists
...
However, in this case, insertion and deletion
become time consuming, especially with large lists because these operations require data
movement
...
Thus, there are limitations when you organize data in an array
...
Chapter 3 showed how
memory (variables) can be dynamically allocated and deallocated using pointers
...
Recall that when data is
stored in an array, memory for the components of the array is contiguous—that is, the blocks
are allocated one after the other
...


Linked Lists
A linked list is a collection of components, called nodes
...
Thus, every node in a linked list has two components:
one to store the relevant information (that is, data) and one to store the address, called the
link, of the next node in the list
...
Figure 5-1 is a pictorial representation of a node
...

The list in Figure 5-2 is an example of a linked list
...
The down arrow in the last node indicates that this link field is NULL
...


1200
head

FIGURE 5-3

1200

45

1575
1575

65

Linked list and values of the links

The value of the head is 1200, the data part of the first node is 45, and the link component
of the first node contains 1575, the address of the second node
...

For simplicity and for the ease of understanding and clarity, Figures 5-3 through 5-5 use
decimal integers as the values of memory addresses
...

Because each node of a linked list has two components, we need to declare each node as a
class or struct
...
However, the link component of each node is a
pointer
...
For the previous linked
list, the definition of the node is as follows
...
)
struct nodeType
{
int info;
nodeType *link;
};

The variable declaration is as follows:
nodeType *head;

Linked Lists: Some Properties
To better understand the concept of a linked list and a node, some important properties
of linked lists are described next
...


FIGURE 5-4

2000
17 2800

2800
92 1500

1500
63 3600

3600
45

0

info

head
2000

info

info

info

link

link

Linked list with four nodes

link

link

5

268 |

Chapter 5: Linked Lists

This linked list has four nodes
...

Each node has two components: info, to store the info, and link, to store the address of
the next node
...

Suppose that the first node is at location 2000, the second node is at location 2800, the
third node is at location 1500, and the fourth node is at location 3600
...

TABLE 5-1

Values of head and some of the nodes of the linked list in Figure 5-4
Value

head

2000

head->info

17

head->link

2800

head->link->info

92

Explanation

Because head is 2000 and the info of the
node at location 2000 is 17

Because head->link is 2800 and the
info of the node at location 2800 is 92

Suppose that current is a pointer of the same type as the pointer head
...
Now consider the following statement:
current = current->link;

This statement copies the value of current->link, which is 2800, into current
...

(When working with linked lists, we typically use these types of statements to advance a
pointer to the next node in the list
...


head
2000

2000
17 2800
info link

2800
92 1500
info link

1500
63 3600
info link

3600
45
info

current 2800

FIGURE 5-5

List after the statement current = current->link; executes

0
link

Linked Lists

|

269

Table 5-2 shows the values of current, head, and some other nodes in Figure 5-5
...

TRAVERSING A LINKED LIST
The basic operations of a linked list are as follows: Search the list to determine whether a
particular item is in the list, insert an item in the list, and delete an item from the list
...
That is, given a pointer to the first node
of the list, we must step through the nodes of the list
...
We cannot use the pointer head to traverse the list because if we use the
head to traverse the list, we would lose the nodes of the list
...
The pointer head contains the address of the first
node, the first node contains the address of the second node, the second node contains the
address of the third node, and so on
...
If we keep advancing head to the next
node, we will lose all the nodes of the list (unless we save a pointer to each node before
advancing head, which is impractical because it would require additional computer time
and memory space to maintain the list)
...
It now follows that we must
traverse the list using another pointer of the same type
...
The following code traverses the list:

5

270 |

Chapter 5: Linked Lists

current = head;
while (current != NULL)
{
//Process current
current = current->link;
}

For example, suppose that head points to a linked list of numbers
...

Consider the following definition of a node
...
The next section, which discusses linked lists as an abstract data type (ADT)
using templates, uses the generic definition of a node
...


head

45

65
p

FIGURE 5-6

Linked list before item insertion

34

76

Linked Lists

|

271

Suppose that p points to the node with info 65, and a new node with info 50 is to be
created and inserted after p
...

TABLE 5-3

Inserting a node in a linked list

Statement

Effect
head

45

65

34

65

34

5

76

76

p

newNode = new nodeType;

newNode

head

45
p

newNode->info = 50;

newNode

head

45

50

65

76

p

newNode->link = p->link;

newNode

head

p->link = newNode;

34

45

50
34

65

76

p
newNode

50

Note that the sequence of statements to insert the node, that is,
newNode->link = p->link;
p->link = newNode;

is very important because to insert newNode in the list we use only one pointer, p, to
adjust the links of the nodes of the linked list
...


head

45

34

65

76

p
newNode

50

FIGURE 5-7 List after the execution of the statement p->link = newNode; followed by the
execution of the statement newNode->link = p->link;

From Figure 5-7, it is clear that newNode points back to itself and the remainder of the
list is lost
...
Suppose q points to
the node with info 34
...
)

head

45

65
p

newNode

FIGURE 5-8

34

76

q
50

List with pointers p and q

The following statements insert newNode between p and q:
newNode->link = q;
p->link = newNode;

The order in which these statements execute does not matter
...

TABLE 5-4

Inserting a node in a linked list using two pointers

Statement

Effect
head

45

34

65
p

p->link = newNode;

newNode
head

45

50

65

34

p

newNode->link = q;

76

q

newNode

50

76

q

DELETION
Consider the linked list shown in Figure 5-9
...
The following
statement removes the node from the list:
p->link = p->link->link;

Figure 5-10 shows the resulting list after the preceding statement executes
...

However, the memory is still occupied by this node and this memory is inaccessible; that

5

274 |

Chapter 5: Linked Lists

is, this node is dangling
...
The
following statements delete the node from the list and deallocate the memory occupied
by this node:
q = p->link;
p->link = q->link;
delete q;

Table 5-5 shows the effect of these statements
...

First, we consider a linked list in general
...
Such a list can be built in two ways: forward and backward
...
In the backward manner, a new
node is always inserted at the beginning of the list
...

BUILDING A LINKED LIST FORWARD
Suppose that the nodes are in the usual info-link form and info is of type int
...
Consider the following variable declaration:
nodeType *first, *last, *newNode;
int num;

Linked Lists

|

275

Suppose that first points to the first node in the list
...
Thus, we must have the statements
first = NULL;
last = NULL;

to initialize first and last to NULL
...
Initially, both first and last are NULL
...


first
last

FIGURE 5-11

Empty list

After statement 1 executes, num is 2
...
Statement 3 stores 2 in the info field of newNode, and statement 4
stores NULL in the link field of newNode
...
)

newNode

FIGURE 5-12

newNode with info 2

2

5

276 |

Chapter 5: Linked Lists

Because first is NULL, we execute statements 5a and 5b
...


first
last

2

newNode

FIGURE 5-13

List after inserting newNode in it

We now repeat statements 1 through 6b
...

Statement 2 creates a node and stores the address of this node in newNode
...
(See Figure 5-14
...
Figure 5-15 shows the
resulting list
...
Figure 5-16 shows the resulting list
...
We can, in fact, write a C++ function to build a linked list
...
The following function,
buildListForward, builds a linked list (in a forward manner) and returns the pointer
of the built list:
nodeType* buildListForward()
{
nodeType *first, *newNode, *last;
int num;
cout << "Enter a list of integers ending with -999
...
For the previously given
data—2, 15, 8, 24, and 34—the linked list is as shown in Figure 5-17
...
Also, after inserting the new node at the
beginning, the new node becomes the first node in the list
...
We see, then, that we need
only two pointers to build the linked list: one to point to the list and one to create the new
node
...
The
following C++ function builds the linked list backward and returns the pointer of the built list:
nodeType* buildListBackward()
{
nodeType *first, *newNode;
int num;
cout << "Enter a list of integers ending with -999
...
Because a linked list is a very important data structure, rather than
discuss specific lists such as a list of integers or a list of strings, this section discusses linked lists
as an abstract data type (ADT)
...
The programming
example at the end of this chapter also uses this generic definition of linked lists
...

2
...

4
...


Initialize the list
...

Print the list
...

Destroy the list
...

7
...

9
...

11
...

Retrieve the info contained in the last node
...

Insert an item in the list
...

Make a copy of the linked list
...
The algorithms to
implement the operations search, insert, and remove slightly differ for sorted and unsorted lists
...
Using the principal of inheritance, we derive two classes—
unorderedLinkedList and orderedLinkedList—from the class linkedListType
...
On the other hand, objects of the class
orderedLinkedList would arrange elements according to some comparison criteria, usually
less than or equal to
...
Moreover, after inserting an
element into or removing an element from an ordered list, the resulting list will be ordered
...

Furthermore, you can build such a list in either a forward manner or a backward manner
...
To accommodate both
operations, we will write two functions: insertFirst to insert the new item at the
beginning of the list and insertLast to insert the new item at the end of the list
...


Structure of Linked List Nodes
Recall that each node of a linked list must store the data as well as the address for the next
node in the list (except the last node of the list)
...
To simplify operations such as insert and delete, we define the class to implement
the node of a linked list as a struct
...
Programming
Exercise 8, at the end of this chapter, asks you to redefine the class to implement the nodes
of a linked list so that the instance variables of the class nodeType are private
...
The pointer first
points to the first node in the list, and last points to the last node in the list
...
Therefore, the class linkedListType
has three instance variables, as follows:
protected:
int count; //variable to store the number of elements in the list
nodeType *first; //pointer to the first node of the list
nodeType *last; //pointer to the last node of the list

Linked List Iterators
One of the basic operations performed on a list is to process each node of the list
...
Moreover, a specific application
requires each node to be processed in a very specific way
...
So what is an iterator? An iterator is an object
that produces each element of a container, such as a linked list, one element at a time
...
The increment operator advances the iterator to the next
node in the list while the dereferencing operator returns the info of the current node
...
So we need to define a class, which we will call
linkedListIterator, to create iterators to objects of the class linkedListType
...

//***********************************************************
// Author: D
...
Malik
//
// This class specifies the members to implement an iterator
// to a linked list
...

//Postcondition: current = ptr;
Type operator*();
//Function to overload the dereferencing operator *
...

linkedListIterator operator++();
//Overload the preincrement operator
...


Linked List as an ADT

|

281

bool operator==(const linkedListIterator& right) const;
//Overload the equality operator
...

bool operator!=(const linkedListIterator& right) const;
//Overload the not equal to operator
...

private:
nodeType *current; //pointer to point to the current
//node in the linked list
};

Figure 5-18 shows the UML class diagram of the class linkedListIterator
...
current);
}
template
bool linkedListIterator::operator!=
(const linkedListIterator& right) const
{
return (current != right
...

Now that we have defined the classes to implement the node of a linked list and an
iterator to a linked list, next, we describe the class linkedListType to implement the
basic porperties of a linked list
...
S
...
This is an abstract class
...

//***********************************************************
template
class linkedListType
{
public:
const linkedListType& operator=
(const linkedListType&);
//Overload the assignment operator
...

//Postcondition: first = NULL, last = NULL, count = 0;

Linked List as an ADT

|

283

bool isEmptyList() const;
//Function to determine whether the list is empty
...

void print() const;
//Function to output the data contained in each node
...

//Postcondition: The value of count is returned
...

//Postcondition: first = NULL, last = NULL, count = 0;
Type front() const;
//Function to return the first element of the list
...

//Postcondition: If the list is empty, the program terminates;
//
otherwise, the first element of the list is returned
...

//Precondition: The list must exist and must not be empty
...

virtual bool search(const Type& searchItem) const = 0;
//Function to determine whether searchItem is in the list
...

virtual void insertFirst(const Type& newItem) = 0;
//Function to insert newItem at the beginning of the list
...

virtual void insertLast(const Type& newItem) = 0;
//Function to insert newItem at the end of the list
...

virtual void deleteNode(const Type& deleteItem) = 0;
//Function to delete deleteItem from the list
...
first points to the first node,
//
last points to the last node of the updated list, and
//
count is decremented by 1
...

//Postcondition: Returns an iterator such that current is set
//
to first
...

//Postcondition: Returns an iterator such that current is set
//
to NULL
...

//Postcondition: first = NULL, last = NULL, count = 0;
linkedListType(const linkedListType& otherList);
//copy constructor
~linkedListType();
//destructor
//Deletes all the nodes from the list
...

protected:
int count; //variable to store the number of list elements
//
nodeType *first; //pointer to the first node of the list
nodeType *last; //pointer to the last node of the list
private:
void copyList(const linkedListType& otherList);
//Function to make a copy of otherList
...

};

Linked List as an ADT

|

285

Figure 5-19 shows the UML class diagram of the class linkedListType
...

The instance variables first and last, as defined earlier, of the class linkedListType
are protected, not private, because as noted previously, we will derive the classes
unorderedLinkedList and orderedLinkedList from the class linkedListType
...

The definition of the class linkedListType includes a member function to overload
the assignment operator
...
For the same reason, the
definition of the class also includes a copy constructor
...
This is because this function is used
only to implement the copy constructor and overload the assignment operator
...

The list is empty if first is NULL
...
It simply initializes
the list to an empty state
...

template
linkedListType::linkedListType() //default constructor
{
first = NULL;
last = NULL;
count = 0;
}

From the definitions of the functions isEmptyList and the default constructor, it
follows that each of these functions is of O(1)
...
We traverse
the list starting from the first node and deallocate the memory by calling the operator
delete
...
Once the entire list is
destroyed, we must set the pointers first and last to NULL and count to 0
...
From this, it follows that the
function destroyList is of O(n)
...
Note that the default
constructor or the copy constructor has already initialized the list when the list object was
declared
...
This task can be accomplished by using the destroyList
operation, which also resets the pointers first and last to NULL and sets count to 0
...

Therefore, the function initializeList is of O(n)
...
To print the data
contained in each node, we must traverse the list starting at the first node
...
(If we use first to traverse the list, the entire list will be lost
...


Length of a List
The length of a linked list (that is, how many nodes are in the list) is stored in the variable
count
...

template
int linkedListType::length() const
{
return count;
}

5

288 |

Chapter 5: Linked Lists

Retrieve the Data of the First Node
The function front returns the info contained in the first node, and its definition is
straightforward
...
Therefore,
before calling this function check, you have to check to see whether the list is nonempty
...
Its definition is as follows:
template
Type linkedListType::back() const
{
assert(last != NULL);
return last->info; //return the info of the last node
}//end back

Notice that if the list is empty, the assert statement terminates the program
...

From the definitions of the functions length, front, and back, it follows easily that
each of these functions are of O(1)
...
Their definitions are as follows:
template
linkedListIterator linkedListType::begin()
{
linkedListIterator temp(first);
}

return temp;

template
linkedListIterator linkedListType::end()
{
linkedListIterator temp(NULL);
}

return temp;

Linked List as an ADT

|

289

From the definitions of the functions length, front, back, begin, and end, it follows
easily that each of these functions are of O(1)
...
Therefore, we traverse
the list to be copied starting at the first node
...
Create a node and call it newNode
...
Copy the info of the node (in the original list) into newNode
...
Insert newNode at the end of the list being created
...
first == NULL) //otherList is empty
{
first = NULL;
last = NULL;
count = 0;
}
else
{
current = otherList
...
count;
//copy the first node
first = new nodeType; //create the node
first->info = current->info; //copy the info
first->link = NULL; //set the link field of the node to NULL
last = first;
//make last point to the first node
current = current->link; //make current point to the next
// node
//copy the remaining list
while (current != NULL)
{
newNode = new nodeType; //create a node
newNode->info = current->info; //copy the info
newNode->link = NULL; //set the link of newNode to NULL

5

290 |

Chapter 5: Linked Lists

last->link = newNode; //attach newNode after last
last = newNode; //make last point to the actual last
//node
current = current->link; //make current point to the
//next node
}//end while
}//end else
}//end copyList

The function copyList contains a while loop
...
If the list contains n items, the while
loop executes n times
...


Destructor
The destructor deallocates the memory occupied by the nodes of a list when the class
object goes out of scope
...
We
must traverse the list, starting at the first node, and delete each node in the list
...
Therefore, the definition of the
destructor is as follows:
template
linkedListType::~linkedListType() //destructor
{
destroyList();
}

Copy Constructor
Because the class linkedListType contains pointer data members, the definition of
this class contains the copy constructor
...
The copy constructor also executes when an object is declared and initialized using
another object
...
This can be done by
calling the function copyList
...

The definition of the copy constructor is as follows:
template
linkedListType::linkedListType
(const linkedListType& otherList)
{
first = NULL;
copyList(otherList);
}//end copy constructor

Linked List as an ADT

|

291

Overloading the Assignment Operator
The definition of the function to overload the assignment operator for the class
linkedListType is similar to the definition of the copy constructor
...

//overload the assignment operator
template
const linkedListType& linkedListType::operator=
(const linkedListType& otherList)
{
if (this != &otherList) //avoid self-copy
{
copyList(otherList);
}//end else
}

return *this;

The destructor uses the function destroyList, which is of O(n)
...
Therefore, each of these functions are of O(n)
...

The following class defines an unordered linked list as an ADT:
//***********************************************************
// Author: D
...
Malik
//
// This class specifies the members to implement the basic
// properties of an unordered linked list
...

//***********************************************************
template
class unorderedLinkedList: public linkedListType
{
public:
bool search(const Type& searchItem) const;
//Function to determine whether searchItem is in the list
...

void insertFirst(const Type& newItem);
//Function to insert newItem at the beginning of the list
...

//
void insertLast(const Type& newItem);
//Function to insert newItem at the end of the list
...

void deleteNode(const Type& deleteItem);
//Function to delete deleteItem from the list
...
first points to the first
node, last points to the last node of the updated
list, and count is decremented by 1
...


unorderedLinkedList
linkedListType
+search(const Type&) const: bool
+insertFirst(const Type&): void
+insertLast(const Type&): void
+deleteNode(const Type&): void

5
unorderedLinkedList

FIGURE 5-20 UML class diagram of the class unorderedLinkedList and the
inheritance hierarchy

Next we give the definitions of the member functions of the class unorderedLinkedList
...
If the item is found, it
returns true; otherwise, it returns false
...

This function has the following steps:
1
...
If the info of
the current node is the same as the search item, stop the search; otherwise,
make the next node the current node
...
Repeat Step 1 until either the item is found or no more data is left in the
list to compare with the search item
...
Suppose the list has n items
...
On the other hand, if the search item is the first
item, the while loop executes 1 time
...
From these observations, we can show that the function
search is of O(n)
...


Insert the First Node
The function insertFirst inserts the new item at the beginning of the list—that is, before
the node pointed to by first
...

2
...

4
...


Create a new node
...

Store the new item in the new node
...

Increment count by 1
...
Here, we insert the new node after last
...

DELETE A NODE
Next, we discuss the implementation of the member function deleteNode, which
deletes a node from the list with a given info
...

The node is nonempty and the node to be deleted is the first node
...

• The node to be deleted is not in the list
...
If list is
not empty, we search the list for the node with the given info and, if such a node is
found, we delete this node
...
In
pseudocode, the algorithm is as follows:
if list is empty
Output(cannot delete from an empty list);
else
{
if the first node is the node with the given info
adjust the head pointer, that is, first, and deallocate
the memory;
else
{
search the list for the node with the given info
if such a node is found, delete it and adjust the
values of last (if necessary) and count
...
If the list is empty, output an error message as shown in the
pseudocode
...
This case has
two scenarios: list has only one node, and list has more than one node
...
Therefore, after
deletion, both first and last are set to NULL and count is set to 0
...

Then after deleting this node, the second node becomes the first node
...

Case 3: The node to be deleted is not the first node, but is somewhere in the list
...
Let us illustrate the first cases
...

Consider the list shown in Figure 5-21
...
After deleting this node, the resulting list
is as shown in Figure 5-22
...
The link field of the previous node—that is,
17—changes
...
)

list
first
last
count 4

FIGURE 5-22

list after deleting 37

28

17

24

54

Unordered Linked Lists

|

297

Case 3b: The node to be deleted is the last node
...
It contains the address of the node just
before the node to be deleted
...
After deleting 54, last contains the
address of the node with info 24
...

Case 4: The node to be deleted is not in the list
...
We
simply output an error message, indicating that the item to be deleted is not in the list
...
Because a linked list is not a random access data structure, we must sequentially search
the list
...
We
sequentially search the list starting at the second node
...
Thus, we need a pointer to the previous node
...
If the node to be deleted is the last
node, we must adjust the pointer last
...

cout << "Cannot delete from an empty list
...
" << endl;
}//end else
}//end else
}//end deleteNode

From the definition of the function deleteNode, it can be shown that this function is of O(n)
...

TABLE 5-7

Time-complexity of the operations of the class unorderedLinkedList

Function

Time-complexity

search

O (n)

insertFirst

O (1)

insertLast

O (1)

deleteNode

O (n)

Header File of the Unordered Linked List
For the sake of completeness, we show how to create the header file that defines the
class unorderedListType and the operations on such lists
...
h
...
S
...
This class is
// derived from the class linkedListType
...
h"
using namespace std;
template
class unorderedLinkedList: public linkedListType
{
public:
bool search(const Type& searchItem) const;
//Function to determine whether searchItem is in the list
...

void insertFirst(const Type& newItem);
//Function to insert newItem at the beginning of the list
...

void insertLast(const Type& newItem);
//Function to insert newItem at the end of the list
...


};

void deleteNode(const Type& deleteItem);
//Function to delete deleteItem from the list
...
first points to the first
//
node, last points to the last node of the updated list,
//
and count is decremented by 1
...


...


...


5

300 |

Chapter 5: Linked Lists

Ordered Linked Lists
The preceding section described the operations on an unordered linked list
...
As noted earlier, we derive the class orderedLinkedList
from the class linkedListType and provide the definitions of the abstract functions
insertFirst, insertLast, search, and deleteNode to take advantage of the fact that
the elements of an ordered linked list are arranged using some ordering criteria
...

Because the elements of an ordered linked list are in order, we include the function
insert to insert an element in an ordered list at the proper place
...
S
...
This class is
// derived from the class linkedListType
...

//Postcondition: Returns true if searchItem is in the list,
//
otherwise the value false is returned
...

//Postcondition: first points to the new list, newItem
//
is inserted at the proper place in the list, and
//
count is incremented by 1
...

//Postcondition: first points to the new list, newItem is
//
inserted at the beginning of the list, last points to the
//
last node in the list, and count is incremented by 1
...

//Postcondition: first points to the new list, newItem is
//
inserted at the end of the list, last points to the
//
last node in the list, and count is incremented by 1
...

//Postcondition: If found, the node containing deleteItem is

Ordered Linked Lists

};

//
//
//
//

|

301

deleted from the list; first points to the first node
of the new list, and count is decremented by 1
...


Figure 5-23 shows a UML class diagram of the class orderedLinkedList and the
inheritance hierarchy
...


Search the List
First, we discuss the search operation
...
Here, because the list is
sorted, we can improve the search algorithm somewhat
...
We stop the search as soon as we find a node in the list with info
greater than or equal to the search item, or we have searched the entire list
...
Compare the search item with the current node in the list
...

2
...

Note that the loop does not explicitly check whether the search item is equal to an item
in the list
...

template
bool orderedLinkedList::
search(const Type& searchItem) const

302 |

Chapter 5: Linked Lists

{
bool found = false;
nodeType *current; //pointer to traverse the list
current = first;

//start the search at the first node

while (current != NULL && !found)
if (current->info >= searchItem)
found = true;
else
current = current->link;
if (found)
found = (current->info == searchItem); //test for equality
return found;
}//end search

As in the case of the search function of the class unorderedLinkedList, the search
function of the class orderedLinkedList is also of O(n)
...
To find the place for the new item in the
list, as before, we search the list
...
The pointer current points to the node whose info is being compared with
the item to be inserted, and trailCurrent points to the node just before current
...
The following cases arise:
Case 1: The list is initially empty
...

Case 2: The new item is smaller than the smallest item in the list
...
In this case, we need to adjust the list’s head pointer—
that is, first
...

Case 3: The item is to be inserted somewhere in the list
...
In this case, the new
item is inserted at the end of the list
...
Also, count is incremented by 1
...
In this
case, the new item is inserted between trailCurrent and current
...

The following statements can accomplish both cases 3a and 3b
...

trailCurrent->link = newNode;
newNode->link = current;

Ordered Linked Lists

|

303

Let us next illustrate Case 3
...
As indicated previously, this case has two scenarios
...
Consider the list shown in Figure 5-24
...
After inserting 65, the resulting list is as
shown in Figure 5-25
...
Again
consider the list shown in Figure 5-24
...
Clearly, 25 goes between 17 and 27, which would require the
link of the node with info 17 to be changed
...


list
first

17

25

27

38

last
count 5

FIGURE 5-26

list after inserting 25 in the list in Figure 5-24

54

304 |

Chapter 5: Linked Lists

From case 3, it follows that we must first traverse the list to find the place where the new
item is to be inserted
...
The pointer current is used to traverse the list and
compare the info of the node in the list with the item to be inserted
...
For example, in case 3b, when
the search stops, trailCurrent points to node 17 and current points to node 27
...
In case 3a, after searching the list to find the place
for 65, trailCurrent points to node 54 and current is NULL
...
It can be
shown that the function insert is of O(n)
...
In Programming Exercise 7 at the end of this chapter
you are asked to revise the definition of the function insert so that before inserting the
item it checks whether the item to be inserted is already in the list
...
In other
words, duplicates are not allowed
...
However,
because the resulting list must be sorted, the new item must be inserted at the proper
place
...

We, therefore, use the function insert to insert the new item at its proper place
...
However, you
must provide its definition as these functions are declared as abstract in the parent class
...
It follows that these functions are of O(n)
...
The function to implement this operation is the same
as the delete operation on general linked lists
...

As in the case of insertNode, we search the list with two pointers, current and
trailCurrent
...
We have an error
...

Case 2: The item to be deleted is contained in the first node of the list
...

Case 3: The item to be deleted is somewhere in the list
...

Case 4: The list is not empty, but the item to be deleted is not in the list
...
The definition of the function
deleteNode is as follows:
template
void orderedLinkedList::deleteNode(const Type& deleteItem)
{
nodeType *current; //pointer to traverse the list
nodeType *trailCurrent; //pointer just before current
bool found;
if (first == NULL) //Case 1
cout << "Cannot delete from an empty list
...
"
<< endl;
else
if (current->info == deleteItem) //the item to be
//deleted is in the list

Ordered Linked Lists

|

307

{
if (first == current)
{
first = first->link;

//Case 2

if (first == NULL)
last = NULL;
delete current;
}
else
//Case 3
{
trailCurrent->link = current->link;
if (current == last)
last = trailCurrent;
delete current;
}
count--;

}
else
//Case 4
cout << "The item to be deleted is not in the "
<< "list
...

Table 5-8 gives the time-complexity of the operations of the class orderedLinkedList
...
(We assume that the defini-

5

308 |

Chapter 5: Linked Lists

tion of the class linkedListType and the definitions of the functions to implement
the operations are in the header file linkedList
...
)
#ifndef H_orderedListType
#define H_orderedListType
//***********************************************************
// Author: D
...
Malik
//
// This class specifies the members to implement the basic
// properties of an ordered linked list
...

//***********************************************************
#include "linkedList
...

//Postcondition: Returns true if searchItem is in the list,
//
otherwise the value false is returned
...

//Postcondition: first points to the new list, newItem is
//
inserted at the proper place in the list, and count
//
is incremented by 1
...

//Postcondition: first points to the new list, newItem is
//
inserted at the beginning of the list, last points to the
//
last node in the list, and count is incremented by 1
...

//Postcondition: first points to the new list, newItem is
//
inserted at the end of the list, last points to the
//
last node in the list, and count is incremented by 1
...

//Postcondition: If found, the node containing deleteItem is
//
deleted from the list; first points to the first node of
//
the new list, and count is decremented by 1
...


Ordered Linked Lists

|

309

//Place the definitions of the functions search, insert,
//insertfirst, insertLast, and deleteNode here
...


...

#endif

The following program tests various operations on an ordered linked list:
//**********************************************************
// Author: D
...
Malik
//
// This program tests the various operations on an ordered
// linked list
...
h"

//Line 1
//Line 2

using namespace std;

//Line 3

int main()
{
orderedLinkedList list1, list2;
int num;

//Line 4
//Line 5
//Line 6

cout << "Line 7: Enter numbers ending "
<< "with -999
...
insert(num);
cin >> num;
}

//Line
//Line
//Line
//Line
//Line

cout << endl;

//Line 14

cout << "Line 15: list1: ";
list1
...
print();
cout << endl;

//Line 19
//Line 20
//Line 21

cout << "Line 22: Enter the number to be "
<< "deleted: ";
cin >> num;
cout << endl;

//Line 22
//Line 23
//Line 24

5

310 |

Chapter 5: Linked Lists

list2
...
print();
cout << endl;
}

//Line 25

//Line 26
//Line 27
//Line 28

return 0;

//Line 29
//Line 30

Sample Run: In this sample run, the user input is shaded:
Line 7: Enter numbers ending with -999
...
The details are left as an exercise for you
...
Programming Exercise 7 at
the end of this chapter asks you to revise the definition of the function insert so that
before inserting the item it checks whether it is already in the list
...
In
other words, duplicates are not allowed
...
In other words, every node contains the address of the next node (except the last
node), and every node contains the address of the previous node (except the first node)
...
)

first
last

FIGURE 5-27

Doubly linked list

Doubly Linked Lists

|

311

A doubly linked list can be traversed in either direction
...

As before, the typical operations on a doubly linked list are as follows: Initialize the list,
destroy the list, determine whether the list is empty, search the list for a given item, insert
an item, delete an item, and so on
...
S
...

//***********************************************************
//Definition of the node
template
struct nodeType
{
Type info;
nodeType *next;
nodeType *back;
};
template
class doublyLinkedList
{
public:
const doublyLinkedList& operator=
(const doublyLinkedList &);
//Overload the assignment operator
...

//Postcondition: first = NULL; last = NULL; count = 0;
bool isEmptyList() const;
//Function to determine whether the list is empty
...

void destroy();
//Function to delete all the nodes from the list
...

void reversePrint() const;
//Function to output the info contained in each node
//in reverse order
...

//Postcondition: The value of count is returned
...

//Precondition: The list must exist and must not be empty
...

Type back() const;
//Function to return the last element of the list
...

//Postcondition: If the list is empty, the program terminates;
//
otherwise, the last element of the list is returned
...

//Postcondition: Returns true if searchItem is found in the
//
list, otherwise returns false
...

//Precondition: If the list is nonempty, it must be in order
...

void deleteNode(const Type& deleteItem);
//Function to delete deleteItem from the list
...

doublyLinkedList();
//default constructor
//Initializes the list to an empty state
...

protected:
int count;
nodeType *first; //pointer to the first node
nodeType *last; //pointer to the last node

Doubly Linked Lists

|

313

private:
void copyList(const doublyLinkedList& otherList);
//Function to make a copy of otherList
...

};

We leave the UML class diagram of the class doublyLinkedList as an exercise for
you, see Exercise 11 at the end of this chapter
...
Here, because every node has two pointers, back and next, some of the
operations require the adjustment of two pointers in each node
...
Let us call this pointer current
...
We give the definition of each function here, with four exceptions
...
(See Programming Exercise 10 at the end
of this chapter
...


Default Constructor
The default constructor initializes the doubly linked list to an empty state
...

template
doublyLinkedList::doublyLinkedList()
{
first= NULL;
last = NULL;
count = 0;
}

isEmptyList
This operation returns true if the list is empty; otherwise, it returns false
...

template
bool doublyLinkedList::isEmptyList() const
{
return (first == NULL);
}

Destroy the List
This operation deletes all the nodes in the list, leaving the list in an empty state
...
Furthermore, count is set to 0
...
This task can be done by
using the operation destroy
...
Therefore, this function returns the value of this variable
...
We traverse the list
starting from the first node
...
We traverse the
list in reverse order starting from the last node
...
The search algorithm is exactly the same as the search algorithm for an ordered linked list
...
If the list is empty, both functions terminate the program
...
As before, we find the
place where the new item is supposed to be inserted, create the node, store the new
item, and adjust the link fields of the new node and other particular nodes in the list
...
Cases 3 and 4 are
similar
...
Next, we show case 4
...


first

8

15

last
count 4

FIGURE 5-28

Doubly linked list before inserting 20

24

40

Doubly Linked Lists

|

317

Suppose that 20 is to be inserted in the list
...


first

8

15

24

40

20
last
count 5

FIGURE 5-29

Doubly linked list after inserting 20

From Figure 5-29, it follows that the next pointer of node 15, the back pointer of node 24,
and both the next and back pointers of node 20 need to be adjusted
...
As before, we
first search the list to see whether the item to be deleted is in the list
...
Similar to the insert operation, this operation (if the
item to be deleted is in the list) requires the adjustment of two pointers in certain nodes
...

Case 2: The item to be deleted is in the first node of the list, which would require us to
change the value of the pointer first
...

Case 4: The item to be deleted is not in the list
...
Let us demonstrate case 3
...


first

5

17

44

52

last
count 4

FIGURE 5-30

Doubly linked list before deleting 17

Suppose that the item to be deleted is 17
...
(See Figure 5-31
...
(See Figure 5-32
...
" << endl;
else if (first->info == deleteItem) //node to be deleted is
//the first node
{
current = first;
first = first->next;
if (first != NULL)
first->back = NULL;
else
last = NULL;
count--;
delete current;
}
else
{
found = false;
current = first;
while (current != NULL && !found) //search the list
if (current->info >= deleteItem)
found = true;
else
current = current->next;
if (current == NULL)
cout << "The item to be deleted is not in "
<< "the list
...
" endl;
}//end else
}//end deleteNode

STL Sequence Container: list |

321

STL Sequence Container: list
Chapter 4 listed three types of sequence containers—vector, deque, and list
...
This section describes
the STL sequence container list
...

Thus, every element in a list points to its immediate predecessor and to its immediate
successor (except the first and last elements)
...
Therefore, to access, for example, the fifth element
in the list, we must first traverse the first four elements
...
The
definition of the class list, and the definitions of the functions to implement the
various operations on a list, are contained in the header file list
...
Thus, a
list object can be initialized in several ways when it is declared, as described in
Table 5-9
...
(The default constructor is
invoked
...
listCont and
otherList are of the same type
...
listCont is initialized
using the default constructor
...
listCont is initialized using
n copies of the element elem
...

listCont is initialized to the elements in
the range [beg, end), that is, all the
elements in the range beg
...

Both beg and end are iterators
...
In addition to these
common operations, Table 5-10 describes the operations that are specific to a list
container
...

(Suppose that listCont is a container of type list
...
assign(n, elem)

Assigns n copies of elem
...
assign(beg, end)

Assigns all the elements in the range
beg
...
Both beg and end are
iterators
...
push_front(elem)

Inserts elem at the beginning of listCont
...
pop_front()

Removes the first element from listCont
...
front()

Returns the first element
...
)

listCont
...
(Does not check
whether the container is empty
...
remove(elem)

Removes all the elements that are equal
to elem
...
remove_if(oper)

Removes all the elements for which oper
is true
...
unique()

If the consecutive elements in listCont
have the same value, removes the
duplicates
...
unique(oper)

If the consecutive elements in listCont
have the same value, removes the
duplicates, for which oper is true
...
splice(pos, listCont2)

All the elements of listCont2 are
moved to listCont1 before the position
specified by the iterator pos
...


STL Sequence Container: list |

TABLE 5-10

323

Operations specific to a list container (continued)

Expression

Description

listCont1
...


listCont1
...
end-1
of listCont2 are moved to listCont1
before the position specified by the iterator
pos
...


listCont
...

The sort criterion is <
...
sort(oper)

The elements of listCont are sorted
...


listCont1
...
This
operation moves all the elements of
listCont2 into listCont1
...
Moreover, after this operation,
listCont2 is empty
...
merge(listCont2, oper)

Suppose that the elements of listCont1
and listCont2 are sorted according to the
sort criteria oper
...
After this operation, the
elements in listCont1 are sorted
according to the sort criteria oper
...
reverse()

The elements of listCont are reversed
...

EXAMPLE 5-1
//****************************************************************
// Author: D
...
Malik
//
// This program illustrates how to use a list container in a
// program
...
push_back(23);
intList1
...
push_back(58);
intList1
...
push_back(15);
intList1
...
push_back(58);

//Line
//Line
//Line
//Line
//Line
//Line
//Line

cout << "Line 17: intList1: ";
copy(intList1
...
end(), screen);
cout << endl;

//Line 17
//Line 18
//Line 19

intList2 = intList1;

//Line 20

cout << "Line 21: intList2: ";
copy(intList2
...
end(), screen);
cout << endl;

//Line 21
//Line 22
//Line 23

intList1
...
begin(), intList1
...
sort();

//Line 28

cout << "Line 29: After sorting, intList2: ";
copy(intList2
...
end(), screen);
cout << endl;
}

//Line 9

//Line 29
//Line 30
//Line 31

return 0;

//Line 32
//Line 33

Sample Run:
Line 17: intList1: 23 58 58 36 15 98 58
Line 21: intList2: 23 58 58 36 15 98 58
Line 25: After removing the consecutive duplicates,
intList1: 23 58 36 15 98 58
Line 29: After sorting, intList2: 15 23 36 58 58 58 98

10
11
12
13
14
15
16

Linked Lists with Header and Trailer Nodes

|

325

For the most part, the output of the preceding program is straightforward
...
The statement in Line 20 copies the elements of intList1 into
intList2
...
The
statement in Line 24 removes any consecutive occurrences of the same elements
...
The operation unique removes
two occurrences of 58
...
The statement in Line 28 sorts intList2
...
These cases needed to be handled separately
...
One way to
simplify these algorithms is to never insert an item before the first or last item and to never
delete the first node
...

Suppose the nodes of a list are in order; that is, they are arranged with respect to a given key
...
In this case, we can set up a node, called the header, at the beginning of the list containing
a value smaller than the smallest value in the data set
...

These two nodes, header and trailer, serve merely to simplify the insertion and deletion
algorithms and are not part of the actual list
...

For example, suppose the data are ordered according to the last name
...
The smallest last name is larger than
the string "A" and the largest last name is smaller than the string "zzzzzzzz"
...

Figure 5-33 shows an empty and a nonempty linked list with header and trailer nodes
...

We leave it as an exercise for you to design a class to implement a linked list with header
and trailer nodes
...
)

Circular Linked Lists
A linked list in which the last node points to the first node is called a circular linked list
...


first
(a) Empty circular list

first
(b) Circular linked list with one node

first
(c) Circular linked list with more than one node

FIGURE 5-34

Circular linked lists

In a circular linked list with more than one node, as in Figure 5-34(c), it is convenient to
make the pointer first point to the last node of the list
...
For example, first points to the last node
and first->link points to the first node
...

We leave it as exercise for you to design a class to implement a sorted circular linked list
...
)

Programming Example: Video Store

PROGRAMMING EXAMPLE:

|

327

Video Store

During holidays or on weekends, a family or an individual typically rents a movie either
from a local store or online
...

2
...

4
...

6
...

8
...

Return, or check in, a video
...

Show the details of a particular video
...

Check whether a particular video is in the store
...

Print a list of all the videos rented by each customer
...
This example further illustrates the objectoriented design methodology and, in particular, inheritance and overloading
...
We will describe these two components in detail
...
In part 1, we design, implement, and test
the video component
...
That is, after
completing parts 1 and 2, we can perform all the operations listed previously
...
The common
things associated with a video are as follows:







Name of the movie
Names of the stars
Name of the producer
Name of the director
Name of the production company
Number of copies in the store

5

328 |

Chapter 5: Linked Lists

From this list, we see that some of the operations to be performed on the video object
are as follows:
1
...

3
...

5
...


Set the video information—that is, the title, stars, production
company, and so on
...

Check the number of copies in the store
...
In other words, if the number of
copies is greater than zero, decrement the number of copies by one
...
To check in a video, first we
must check whether the store owns such a video and, if it does,
increment the number of copies by one
...


The deletion of a video from the video list requires that the video list be searched
for the video to be deleted
...
For simplicity, we assume that two
videos are the same if they have the same title
...
S
...

//************************************************************
#include
#include
using namespace std;
class videoType
{
friend ostream& operator<< (ostream&, const videoType&);
public:
void setVideoInfo(string title, string star1,
string star2, string producer,
string director, string productionCo,
int setInStock);
//Function to set the details of a video
...


Programming Example: Video Store

|

329

//Postcondition: videoTitle = title; movieStar1 = star1;
//
movieStar2 = star2; movieProducer = producer;
//
movieDirector = director;
//
movieProductionCo = productionCo;
//
copiesInStock = setInStock;
int getNoOfCopiesInStock() const;
//Function to check the number of copies in stock
...

void checkOut();
//Function to rent a video
...

void checkIn();
//Function to check in a video
...

void printTitle() const;
//Function to print the title of a movie
...

//Postcondition: The title of the movie, stars, director,
//
and so on are displayed on the screen
...

//Postcondition: Returns the value true if the title is the
//
same as the title of the video; false otherwise
...

//Postcondition: copiesInStock = copiesInStock + num;
void setCopiesInStock(int num);
//Function to set the number of copies in stock
...

//Postcondition: The title of the video is returned
...
If no values are specified, the
//default values are assigned
...

bool operator==(const videoType&) const;
bool operator!=(const videoType&) const;
private:
string
string
string
string

};

videoTitle; //variable to store the name of the movie
movieStar1; //variable to store the name of the star
movieStar2; //variable to store the name of the star
movieProducer; //variable to store the name of the
//producer
string movieDirector; //variable to store the name of the
//director
string movieProductionCo; //variable to store the name
//of the production company
int copiesInStock; //variable to store the number of
//copies in stock

We leave the UML diagram of the class videoType as an exercise for you, see
Exercise 15 at the end of this chapter
...

Next, we write the definitions of each function in the class videoType
...
videoTitle);
}
bool videoType::operator!=(const videoType& other) const
{
return (videoTitle != other
...
videoTitle << endl;
osObject << "Stars: " << video
...
movieStar2 << endl;
osObject << "Producer: " << video
...
movieDirector << endl;
osObject << "Production Company: "
<< video
...
copiesInStock
<< endl;
osObject << "_____________________________________" << endl;
}

Video List

return osObject;

This program requires us to maintain a list of all the videos in the store, and we should
be able to add a new video to our list
...
Therefore, we will use a linked list to create a list of videos
...
We also defined the basic operations such as insertion and deletion

Programming Example: Video Store

|

333

of a video in the list
...
These operations are not available in the class unorderedLinkedList
...

The definition of the class videoListType is as follows:
//*****************************************************************
// Author: D
...
Malik
//
// class videoListType
// This class specifies the members to implement a list of videos
...
h"
#include "videoType
...

//Postcondition: Returns true if the title is found, and
//
false otherwise
...

//Postcondition: Returns true if at least one copy of the
//
video specified by title is in the store, and false
//
otherwise
...

//Postcondition: copiesInStock is decremented by one
...

//Postcondition: copiesInStock is incremented by one
...

//Postcondition: Returns true if the video’s title is the
//
same as title, and false otherwise
...
The
//parameter title specifies the name of the video for
//which the number of copies is to be updated
...

//The parameter title specifies the name of the video
//for which the number of copies is to be reset, and the
//parameter num specifies the number of copies
...

private:
void searchVideoList(string title, bool& found,
nodeType* ¤t) const;
//This function searches the video list for a particular
//video, specified by the parameter title
...
The parameter
//
current points to the node containing the video
...
Furthermore, unorderedLinkedList is a class template and
we have passed the class videoType as a parameter to this class
...
Because we are now dealing with a very specific data
type, the class videoListType is no longer needed to be a template
...
Through the member functions
of the class videoType, certain members—such as videoTitle and copiesInStock
of an object of type videoType—can now be accessed
...


The primary operations on the video list are to check in a video and to check out a
video
...
Other operations
such as seeing whether a particular video is in the store, updating the number of
copies of a video, and so on also require the video list to be searched
...
If the video is found, it sets a parameter found to true and returns
a pointer to the video so that check-in, check-out, and other operations on the
video object can be performed
...
First, we describe the search procedure
...
checkTitle(title)) //the item is found
found = true;
else
current = current->link; //advance current to
//the next node
}//end searchVideoList

If the search is successful, the parameter found is set to true and the parameter
current points to the node containing the video info
...

The definitions of the other functions of the class videoListType follow:
bool videoListType::isVideoAvailable(string title) const
{
bool found;
nodeType *location;
searchVideoList(title, found, location);
if (found)
found = (location->info
...
checkIn();

5

336 |

Chapter 5: Linked Lists

else
}

cout << "The store does not carry " << title
<< endl;

void videoListType::videoCheckOut(string title)
{
bool found = false;
nodeType *location;
searchVideoList(title, found, location); //search the list

}

if (found)
location->info
...
updateInStock(num);
else
cout << "The store does not carry " << title
<< endl;

void videoListType::videoSetCopiesInStock(string title, int num)
{
bool found = false;
nodeType *location;
searchVideoList(title, found, location);
if (found)
location->info
...
printTitle();
current = current->link;
}

PART 2:
CUSTOMER
COMPONENT

Customer
Object

The customer object stores information about a customer, such as the first name,
last name, account number, and a list of videos rented by the customer
...
We have already designed the class personType in Example
1-12 (Chapter 1) and described the necessary operations on the name of a person
...
First, however, we must redefine the class
personType to take advantage of the new features of object-oriented design that you have
learned, such as operator overloading, and then derive the class customerType
...

2
...

4
...


Print the name, the account number, and the list of rented videos
...

Rent a video; that is, add the rented video to the list
...

Show the account number
...
(See Programming Exercise 14 at the end of this chapter
...
We assume that the
necessary data for the videos are stored in a file
...
The data in the input file is in the following
form:
video title (that is, the name of the movie)
movie star1
movie star2
movie producer
movie director
movie production co
...


...


We will write a function, createVideoList, to read the data from the input file and
create the list of videos
...
The algorithm of the function main is as follows:
1
...

If the input file does not exist, exit the program
...
Create the list of videos (createVideoList)
...
Show the menu (displayMenu)
...
While not done
Perform various operations
...
Let us describe Steps 2 and 3, which are
accomplished by writing two separate functions: createVideoList and displayMenu
...

Because the data will be read from a file and the input file was opened in the function
main, we pass the input file pointer to this function
...
Both parameters are
reference parameters
...
The general algorithm is as follows:
1
...

2
...

3
...


Programming Example: Video Store

displayMenu

|

339

This function informs the user what to do
...
S
...

//****************************************************************
#include
#include
#include
#include
#include




"videoType
...
h"

using namespace std;
void createVideoList(ifstream& infile,
videoListType& videoList);
void displayMenu();
int main()
{
videoListType videoList;
int choice;
char ch;
string title;
ifstream infile;
//open the input file
infile
...
txt");
if (!infile)

5

340 |

Chapter 5: Linked Lists

{

}

cout << "The input file does not exist
...
close();
//show the menu
displayMenu();
cout << "Enter your choice: ";
cin >> choice;
//get the request
cin
...
videoSearch(title))
cout << "The store carries " << title
<< endl;
else
cout << "The store does not carry "
<< title << endl;
break;
case 2:
cout << "Enter the title: ";
getline(cin, title);
cout << endl;
if (videoList
...
isVideoAvailable(title))
{
videoList
...
" << endl;
}

Programming Example: Video Store

|

341

else
cout << "The store does not carry "
<< title << endl;
break;
case 3:
cout << "Enter the title: ";
getline(cin, title);
cout << endl;
if (videoList
...
videoCheckIn(title);
cout << "Thanks for returning "
<< title << endl;
}
else
cout << "The store does not carry "
<< title << endl;
break;
case 4:
cout << "Enter the title: ";
getline(cin, title);
cout << endl;
if (videoList
...
isVideoAvailable(title))
cout << title << " is currently in "
<< "stock
...
" << endl;
}
else
cout << "The store does not carry "
<< title << endl;
break;
case 5:
videoList
...
print();
break;
default:
cout << "Invalid selection
...
get(ch);
cout << endl;
}//end while
}

return 0;

void createVideoList(ifstream& infile,
videoListType& videoList)
{
string title;
string star1;
string star2;
string producer;
string director;
string productionCo;
char ch;
int inStock;
videoType newVideo;
getline(infile, title);
while (infile)
{
getline(infile, star1);
getline(infile, star2);
getline(infile, producer);
getline(infile, director);
getline(infile, productionCo);
infile >> inStock;
infile
...
setVideoInfo(title, star1, star2, producer,
director, productionCo, inStock);
videoList
...
" << endl;
cout << "2: To check out a video
...
" << endl;

Quick Review |

343

cout << "4: To check whether a particular video is "
<< "in stock
...
"
<< endl;
cout << "6: To print a list of all the videos
...

2
...

4
...

6
...

8
...

10
...

12
...

14
...

16
...

The pointer to a linked list—that is, the pointer to the first node in the
list—is stored in a separate location, called the head or first
...

The length of a linked list is the number of nodes in the list
...

A (single) linked list is traversed in only one direction
...

The first (or head) pointer of a linked list is always fixed, pointing to the
first node in the list
...

In a doubly linked list, every node has two links: one points to the next
node, and one points to the previous node
...

In a doubly linked list, item insertion and deletion requires the adjustment
of two pointers in a node
...

In addition to the operations that are common to sequence containers (see
Chapter 4), the other operations that can be used to manipulate the
elements in a list container are assign, push_front, pop_front,
front, back, remove, remove_if, unique, splice, sort, merge, and
reverse
...

The header and trailer nodes are not part of the actual list
...


5

344 |

17
...


Chapter 5: Linked Lists

A linked list with header and trailer nodes is empty if the only nodes in the
list are the header and the trailer
...


EXERCISES
1
...

a
...

c
...


e
...

In a linked list, memory allocated for the nodes is sequential
...

In a linked list, nodes are always inserted either at the beginning or the
end because a linked link is not a random access data structure
...


Consider the linked list shown in Figure 5-35
...
Use this list to answer Exercises 2 through 7
...
(Assume that list, p, s, A, and B are
pointers of type nodeType
...


87

25

44

B

Linked list for Exercises 2–7

What is the output of each of the following C++ statements?
a
...


cout << A->info;

c
...

3
...


list->info >= 18

b
...


A->link->info == 16

d
...


list->info == 18

Exercises

4
...


list->link = A->link;

c
...


*list = B;

e
...


B = A->link->info;

g
...


list = B->link->link;

i
...

b
...

c
...

d
...

e
...

f
...

g
...
Also, deallocate the memory occupied
by this node
...


6
...


345

Mark each of the following statements as valid or invalid
...

a
...


|

If the following C++ code is valid, show the output
...

a
...


p = A;
p = p->link;
s = p;
p->link = NULL;
s = s->link;
cout << p->info << " " << s->info << endl;

5

346 |

8
...
Assume the node is in
the usual info-link form with the info of type int
...
)
a
...


9
...
(The class unorderedLinkedList
is as defined in this chapter
...
insertFirst(15);
list
...
insertFirst(30);
list
...
insertLast(45);
list
...
insertLast(25);
list
...
insertFirst(18);
list
...
deleteNode(12);
list
...


What is the output of this program segment?
Suppose the input is:
18 30 4 32 45 36 78 19 48 75 -999

What is the output of the following C++ code? (The class unorderedLinkedList
is as defined in this chapter
...
insertFirst(num);
else
list
...
print();
cout << endl;
copyList = list;
copyList
...
deleteNode(35);
cout << "Copy List = ";
copyList
...


12
...

Suppose that intList is a list container and
intList = {3, 23, 23, 43, 56, 11, 11, 23, 25}

13
...
unique();
Suppose that intList1 and intList2 are list containers and
intList1 = {3, 58, 78, 85, 6, 15, 93, 98, 25}
intList2 = {5, 24, 16, 11, 60, 9}

Show intList1 after the following statement executes:
intList1
...
begin(), intList2);

5

348 |

14
...
push_back(5);
intList
...
push_front(45);
intList
...
push_back(35);
intList
...
push_back(50);
intList
...
begin(), intList
...
begin();
intList
...
insert(listIt,38);
intList
...
erase(listIt);
intList
...
back());
intList
...
front());
copy(intList
...
end(), screen);
cout << endl;

15
...


Draw the UML diagram of the class videoType of the Programming
Example Video Store
...


PROGRAMMING EXERCISES
1
...
Using linked lists, redo the program to
handle as many entries as required
...

b
...

When the program terminates, write the data in the address book to a disk
...


|

349

Extend the class linkedListType by adding the following operations:
Find and delete the node with the smallest info in the list
...
)
b
...
(Traverse
the list only once
...
Also write a program to test these
functions
...


3
...
If no such element exists, terminate the program
...
Write a function that deletes the k element of the linked list
...
Provide the definitions of these functions in the class linkedListType
...
(Use either the class
unorderedLinkedList or the class orderedLinkedList to test
your function
...


4
...


Add the operation divideMid to the class linkedListType as follows:
void divideMid(linkedListType &sublist);
//This operation divides the given list into two sublists
//of (almost) equal sizes
...

//
sublist
...
last
//
points to the last node of the second sublist
...
The statement:
myList
...


divides myList into two sublists: myList points to the list with the
elements 34 65 27, and subList points to the sublist with the elements
89 12
...
Also write a program to test your function
...


Chapter 5: Linked Lists

(Splitting a linked list, at a given node, into two sublists)
a
...

//Postcondition: first and last point to the first and last
//
nodes of the first sublist
...
first and secondList
...


Consider the following statements:
unorderedLinkedList myList;
unorderedLinkedList otherList;

Suppose myList points to the list with the elements:
34 65 18 39 27 89 12

(in this order)
...
divideAt(otherList, 18);

b
...


a
...

Write the definition of the function template to implement the operation divideAt
...

Add the following operation to the class orderedLinkedList:
void mergeLists(orderedLinkedList &list1,
orderedLinkedList &list2);
//This function creates a new list by merging the
//elements of list1 and list2
...
The statement:
newList
...


8
...
Also, after the preceding statement
executes, list1 and list2 are empty
...
Write the definition of the function template mergeLists to implement
the operation mergeLists
...

The function insert of the class orderedLinkedList does not check if
the item to be inserted is already in the list; that is, it does not check for
duplicates
...
If the item to be inserted is already in the list, the function outputs an
appropriate error message
...

In this chapter, the class to implement the nodes of a linked list is defined as a
struct
...

template
class nodeType
{
public:
const nodeType& operator=(const nodeType&);
//Overload the assignment operator
...

//Postcondition: info = elem;
Type getInfo() const;
//Function to return the info of the node
...

void setLink(nodeType *ptr);
//Function to set the link of the node
...

//Postcondition: The value of link is returned
...

//Postcondition: info = elem; link = ptr

|

351

5

352 |

Chapter 5: Linked Lists

nodeType(const nodeType &otherNode);
//Copy constructor
~nodeType();
//Destructor
private:
Type info;
nodeType *link;
};

9
...


11
...


Write the definitions of the member functions of the class nodeType
...

Programming Exercise 8 asks you to redefine the class to implement the nodes
of a linked list so that the instance variables are private
...
Rewrite the definitions of these classes so that these
classes use the member functions of the class nodeType to access the info
and link fields of a node
...

Write the definitions of the function copyList, the copy constructor, and the
function to overload the assignment operator for the class doublyLinkedList
...

(Linked List with Header and Trailer Nodes) This chapter defined and
identified various operations on a linked list with header and trailer nodes
...

b
...

(You may assume that the elements of the linked list with header and
trailer nodes are in ascending order
...
Write a program to test various operations of the class defined in (a)
...

a
...


Write the definitions of the class circularLinkedList and its
member functions
...
)
b
...

(Programming Example Video Store)
a
...


a
...


Complete the design and implementation of the class customerType
defined in the Programming Example Video Store
...


Programming Exercises

15
...


17
...
In other words, write a program that
uses the classes designed in the Programming Example Video Store and in
Programming Exercise 14 to make a video store operational
...

Extend the class linkedListType by adding the following function:
void rotate();
//Function to remove the first node of a linked list and put it
//at the end of the linked list
...


Write a program that prompts the user to input a string and then outputs
the string in the pig Latin form
...


b
...


d
...
For example, the pig Latin form of the string "eye" is "eye-way"
...
Then rotate the string one character at a time; that is, move the
first character of the string to the end of the string until the first
character of the string becomes a vowel
...
For example, the pig Latin form of the string "There" is
"ere-Thay"
...
In cases like this, the letter y
can be considered a vowel
...
Therefore, the pig Latin form of "by" is
"y-bay"
...
The pig Latin form of the
string "1234" is "1234-way"
...

Your program must store the characters of a string into a linked list and
use the function rotate, as described in Programming Exercise 17, to
rotate the string
...


Learn about recursive definitions


...


Learn about recursive algorithm


...


Explore how to use recursive functions to implement recursive algorithms

356 |

Chapter 6: Recursion

In previous chapters, to devise solutions to problems we used the most common
technique, called iteration
...
This chapter introduces another problemsolving technique, called recursion, and provides several examples demonstrating how
recursion works
...
Recursion is a very powerful way to solve certain problems for which the
solution would otherwise be very complicated
...

In an algebra course, you probably learned how to find the factorial of a nonnegative
integer
...

Similarly, 4! ¼ 4 Â 3 Â 2 Â 1 = 24
...
Note
that 5! ¼ 5 Â 4 Â 3 Â 2 Â 1 = 5 Â ( 4 Â 3 Â 2 Â 1 ) = 5Â 4!
...
To find (n À 1)!, we apply the definition
again
...

Thus, for an integer n greater than 0, n! is obtained by first finding (n À 1)! and then
multiplying (n À 1)! by n
...
Here n ¼ 3
...
Because n > 0, we use Equation 6-2 to obtain:
2! ¼ 2 Â 1!
Now to find 1!, we again use Equation 6-2 because n ¼ 1 > 0
...
Substituting 0! into 1! gives 1! ¼ 1
...


Recursive Definitions

|

357

The solution in Equation 6-1 is direct—that is, the right side of the equation contains no
factorial notation
...
The definition of the factorial given in Equations 6-1 and 6-2 is called a recursive
definition
...

Recursive definition: A definition in which something is defined in terms of a smaller
version of itself
...
Every recursive definition must have one (or more) base cases
...
The general case must eventually be reduced to a base case
...
The base case stops the recursion
...
Here, we talk about recursive
algorithms and recursive functions
...
The
recursive algorithm must have one or more base cases, and the general solution must
eventually be reduced to a base case
...
That is, the body of the
recursive function contains a statement that causes the same function to execute again
before completing the current call
...

Next, let us write the recursive function that implements the factorial function
...

Let us note the following from the previous example, involving the factorial function:


Logically, you can think of a recursive function as having an unlimited
number of copies of itself
...

• After completing a particular recursive call, control goes back to the
calling environment, which is the previous call
...
The execution in the previous call begins from the point immediately following the recursive call
...
A function that calls another
function and eventually results in the original function call is said to be indirectly

Problem Solving Using Recursion |

359

recursive
...
Indirect recursion can be several layers deep
...
Function A is then
indirectly recursive
...
The base cases
must be identified and appropriate solutions to them must be provided
...
You must, therefore, exercise extra care when
designing indirect recursive functions
...

A recursive function in which the last statement executed is the recursive call is called a
tail recursive function
...


Infinite Recursion
Figure 6-1 shows that the sequence of recursive calls eventually reached a call that made
no further recursive calls
...
On the other hand, if every recursive call results in another recursive call, the
recursive function (algorithm) is said to have infinite recursion
...
Every call to a recursive function requires the system to
allocate memory for the local variables and formal parameters
...
Therefore, because computer memory is finite, if you execute an infinite recursive
function on a computer, the function executes until the system runs out of memory and
results in an abnormal termination of the program
...
You must make
sure that every recursive call eventually reduces to a base case
...

To design a recursive function, you must do the following:
1
...

2
...
For example, for a list, the limiting condition
is the number of elements in the list
...
Identify the base cases and provide a direct solution to each base case
...
Identify the general cases and provide a solution to each general case in terms of
smaller versions of itself
...


6

360 |

Chapter 6: Recursion

Largest Element in an Array
In this example, we use a recursive algorithm to find the largest element in an array
...


list

FIGURE 6-2

[0] [1] [2] [3] [4] [5] [6]
5
8
2
10
9
4

list with six elements

The largest element in the list in Figure 6-2 is 10
...
Also, suppose that
list[a]
...
, list[b]
...
list[5] represents the array elements list[0], list[1],
list[2], list[3], list[4], and list[5]
...
list[5] represents
the array elements list[1], list[2], list[3], list[4], and list[5]
...

If list is of length 1, then list has only one element, which is the largest element
...
To find the largest element in
list[a]
...
list[b] and
then compare this largest element with list[a]
...
list[b] is given by:
maximum(list[a], largest(list[a + 1]
...
This
list has six elements, given by list[0]
...
Now the largest element in list is
given by:
maximum(list[0], largest(list[1]
...
list[5]
...
list[5], we use
the same formula again because the length of this list is greater than 1
...
list[5] is then:
maximum(list[1], largest(list[2]
...
We see that every time we use the previous formula to find the largest element
in a sublist, the length of the sublist in the next call is reduced by one
...
From this point onward, we backtrack through the recursive
calls
...
list[b]
1
...
list[b]
and call it max
2
...
list[b] is list[a]
otherwise
the largest element in list[a]
...


list

FIGURE 6-3

[0] [1] [2] [3]
5
10 12
8

list with four elements

Let us trace the execution of the following statement:
cout << largest(list, 0, 3) << endl;

6

362 |

Chapter 6: Recursion

Here upperIndex = 3 and the list has four elements
...


largest(list,0,3)

return 12

lowerIndex 0
upperIndex 3
max
because lowerIndex != upperIndex
max = largest(list,1,3)
largest(list,1,3)

max 12
because list[0] < max
return max
return 12

lowerIndex 1
upperIndex 3
max
because lowerIndex != upperIndex
max = largest(list,2,3)
largest(list,2,3)
lowerIndex 2
upperIndex 3
max
because lowerIndex != upperIndex
max = largest(list,3,3)

max 12
because list[1] < max
return max
return 12

max 8
because list[2] > max
return list[2]

largest(list,3,3)
lowerIndex 3
upperIndex 3
max

return 8

because lowerIndex = upperIndex
return list[3]

FIGURE 6-4

Execution of largest(list, 0, 3)

The value returned by the expression largest(list, 0, 3) is 12, which is the largest
element in list
...
S
...

//************************************************************
#include
using namespace std;

Problem Solving Using Recursion |

363

int largest(const int list[], int lowerIndex, int upperIndex);
int main()
{
int intArray[10] = {23, 43, 35, 38, 67, 12, 76, 10, 34, 8};
cout << "The largest element in intArray: "
<< largest(intArray, 0, 9);
cout << endl;
}

return 0;

int largest(const int list[], int lowerIndex, int upperIndex)
{
int max;
if (lowerIndex == upperIndex) //size of the sublist is one
return list[lowerIndex];
else
{
max = largest(list, lowerIndex + 1, upperIndex);

}

}

if (list[lowerIndex] >= max)
return list[lowerIndex];
else
return max;

Sample Run:
The largest element in intArray: 76

Print a Linked List in Reverse Order
The nodes of an ordered linked list (as constructed in Chapter 5) are in ascending order
...
We now discuss the function reversePrint
...

Consider the linked list shown in Figure 6-5
...
Let us see how we can effectively use recursion to print the list in reverse order
...
We cannot print the info of the first node until we
have printed the remainder of the list (that is, the tail of the first node)
...
Every time we consider the tail of a node, we reduce the size of the list
by 1
...

Base Case: List is empty: no action
General Case: List is nonempty
1
...
Print the element
Let us write this algorithm
...
)
if (current != NULL)
{
reversePrint(current->link);
cout << current->info << endl;
}

//print the tail
//print the node

Here, we do not see the base case; it is hidden
...
Also, inside the if statement the recursive call is on the
tail of the list
...
Also, note that statements (for example, printing
the info of the node) appear after the recursive call; thus, when the transfer comes back
to the calling function, we must execute the remaining statements
...
(By the ‘‘last statement,’’ we do
not mean the physical last statement, but rather the logical last statement
...

template
void linkedListType::reversePrint
(nodeType *current) const
{
if (current != NULL)
{
reversePrint(current->link);
//print the tail
cout << current->info << " ";
//print the node
}
}

Consider the statement
reversePrint(first);

where first is a pointer of type nodeType
...
Because the formal parameter is a value parameter, the value of the actual
parameter is passed to the formal parameter
...


reversePrint(first)
current->5
because(current != NULL)
reversePrint(current->link)

execute the statement
cout << current->info;
Print 5
Now control goes back
to the caller

reversePrint(current->link)
current->10
because(current != NULL)
reversePrint(current->link)

execute the statement
cout << current->info;
Print 10
Now control goes back
to the caller

reversePrint(current->link)
current->15
because(current != NULL)
reversePrint(current->link)

execute the statement
cout << current->info;
Print 15
Now control goes back
to the caller

reversePrint(current->link)
current is NULL
because current is NULL
the if statement fails
control goes back to the
caller

FIGURE 6-6

Execution of the statement reversePrint(first);

THE FUNCTION printListReverse
Now that we have written the function reversePrint, we can write the definition of the
function printListReverse, which can be used to print an ordered linked list contained in
an object of the type linkedListType
...
We include the

6

366 |

Chapter 6: Recursion

function reversePrint as a private member because it is used only to implement the
function printListReverse
...
:
Given the first two numbers of the sequence (say a1 and a2), the nth number an, n>¼ 3,
of this sequence is given by:
an ¼ anÀ1 þ anÀ2
Thus:
a3 ¼ a2 + a1 ¼ 1 + 1 ¼ 2, a4 ¼ a3 + a2 ¼ 2 + 1 ¼ 3, and so on
...
In the previous sequence, a2 ¼ 1 and a1 ¼ 1
...
The number determined this way is called the nth Fibonacci
number
...

Then:
a3 ¼ a2 þ a1 ¼ 6 þ 3 ¼ 9;

a4 ¼ a3 þ a2 ¼ 9 þ 6 ¼ 15:

In this example, we write a recursive function, rFibNum, to determine the desired
Fibonacci number
...
The function rFibNum returns the nth Fibonacci number in the sequence
...

The fourth Fibonacci number in a sequence is the sum of the second and third Fibonacci
numbers
...
The following recursive algorithm calculates the nth Fibonacci
number, where a denotes the first Fibonacci number, b the second Fibonacci number,
and n the nth Fibonacci number:
8
if n ¼ 1
>a
>
<
b
if n ¼ 2 ðEquation 6-3Þ
rFibNumða; b; nÞ ¼
> rFibNumða; b; n À 1Þþ
>
:
rFibNumða; b; n À 2Þ
if n > 2:

Problem Solving Using Recursion |

367

The following recursive function implements this algorithm:
int rFibNum(int a, int b, int n)
{
if (n == 1)
return a;
else if (n == 2)
return b;
else
return rFibNum(a, b, n - 1) + rFibNum(a, b, n - 2);
}

Let us trace the execution of the following statement:
cout << rFibNum(2, 3, 4) << endl;

In this statement, the first number is 2, the second number is 3, and we want to
determine the 4th Fibonacci number of the sequence
...
The value returned is 8, which is the 4th Fibonacci
number of the sequence whose first number is 2 and whose second number is 3
...
In
the recursive version, some values are calculated more than once
...
So a recursive
method might be easier to write, but might not execute as efficiently
...

The following C++ program uses the function rFibNum:
//*************************************************************
// Author: D
...
Malik
//
// Given the first two numbers of a Fibonacci sequence, this
// program uses a recursive function to determine a specific
// number(s) of a Fibonacci sequence
...

Enter the first Fibonacci number: 2
Enter the second Fibonacci number: 5
Enter the position of the desired Fibonacci number: 6
The Fibonacci number at position 6 is: 31

Tower of Hanoi
In the nineteenth century, a game called the Tower of Hanoi became popular in Europe
...
At the creation of
the universe, priests in the temple of Brahma were supposedly given three diamond
needles, with one needle containing 64 golden disks
...
The priests’ task is to move all 64 disks from the first needle to the
third needle
...
Only one disk can be moved at a time
...
The removed disk must be placed on one of the needles
...
A larger disk cannot be placed on top of a smaller disk
...

Our objective is to write a program that prints the sequence of moves needed to transfer
the disks from the first needle to the third needle
...


1
2
3

FIGURE 6-8

Tower of Hanoi problem with three disks

6

370 |

Chapter 6: Recursion

As before, we think in terms of recursion
...
In this case, the disk can be moved directly from
needle 1 to needle 3
...
In this case, first we move the first disk from needle 1 to needle 2, and
then we move the second disk from needle 1 to needle 3
...
Next, we consider the case when the first needle
contains three disks, and then generalize this to the case of 64 disks (in fact, to an
arbitrary number of disks)
...
To move disk number 3 to needle 3, the top
two disks must first be moved to needle 2
...
To move the top two disks from needle 2 to needle 3, we use the
same strategy as before
...
Figure 6-9
shows a solution to the Tower of Hanoi problem with three disks
...
To begin, the first needle
contains all 64 disks
...
So first we move the top 63 disks from needle
1 to needle 2, and then we move disk number 64 from needle 1 to needle 3
...
To move disk number 63 from needle 2 to needle 3, we first
move the top 62 disks from needle 2 to needle 1, and then we move disk number 63
from needle 2 to needle 3
...

This discussion translates into the following recursive algorithm given in pseudocode
...

1
...

2
...

3
...

This recursive algorithm translates into the following C++ function:
void moveDisks(int count, int needle1, int needle3, int needle2)
{
if (count > 0)
{
moveDisks(count - 1, needle1, needle2, needle3);
cout << "Move disk " << count << " from " << needle1
<< " to " << needle3 << "
...

If needle 1 contains 3 disks, then the number of moves required to move all 3 disks from
needle 1 to needle 3 is 23 À1 ¼ 7
...

Because 210 ¼ 1024 % 1000 ¼ 103, we have

264 ¼ 24 Â 260 % 24 Â 1018 ¼ 1:6 Â 1019
The number of seconds in one year is approximately 3
...
Suppose the priests move
one disk per second and they do not rest
...
It is estimated that our universe is about 15 billion years old (1
...
Also,

6

372 |

Chapter 6: Recursion

5 Â 1011 ¼ 50 Â 1010 % 33 Â (1
...
This calculation shows that our universe
would last about 33 times as long as it already has
...
Then the number
of moves that the computer can generate in one year is:
ð3:2 Â 107 Þ Â 109 ¼ 3:2 Â 1016
So the computer time required to generate 264 moves is:
264 % 1:6 Â 1019 ¼ 1:6 Â 1016 Â 103 ¼ ð3:2 Â 1016 Þ Â 500
Thus, it would take about 500 years for the computer to generate 264 moves at the rate of
1 billion moves per second
...

First we define some terms
...
We call the remainder of x after division by 2 the rightmost bit of x
...
(Recall that in C++, % is the mod operator; it produces the remainder
of the integer division
...

Suppose we want to find the binary representation of 35
...
The
quotient is 17 and the remainder—that is, the rightmost bit of 35—is 1
...
The quotient is 8 and the remainder—that is, the rightmost bit of 17—is 1
...
The quotient is 4 and the remainder—that is, the rightmost bit of
8—is 0
...

The rightmost bit of 35 cannot be printed until we have printed the rightmost bit of 17
...

Thus, the binary representation of 35 is the binary representation of 17 (that is, the quotient
of 35 after division by 2), followed by the rightmost bit of 35
...

This discussion translates into the following recursive algorithm, where binary(num)
denotes the binary representation of num:
1
...

2
...


Problem Solving Using Recursion |

373

The following recursive function implements this algorithm:
void decToBin(int num, int base)
{
if (num > 0)
{
decToBin(num / base, base);
cout << num % base;
}
}

Figure 6-10 traces the execution of the following statement:
decToBin(13, 2);

where num is 13 and base is 2
...
The first output
is produced by call 4, which prints 1; the second output is produced by call 3, which
prints 1; the third output is produced by call 2, which prints 0; and the fourth output is
produced by call 1, which prints 1
...
S
...

//******************************************************
#include
using namespace std;
void decToBin(int num, int base);
int main()
{
int decimalNum;
int base;
base = 2;
cout << "Enter number in decimal: ";
cin >> decimalNum;
cout << endl;
cout << "Decimal " << decimalNum << " = ";
decToBin(decimalNum, base);
cout << " binary" << endl;
}

return 0;

void decToBin(int num, int base)
{
if (num > 0)
{
decToBin(num / base, base);
cout << num % base;
}
}

Recursion or Iteration?

|

375

Sample Run: In this sample run, the user input is shaded
...
The
programs in the previous chapters used a loop to repeat a set of statements to perform
certain calculations
...
Formally, iterative control structures use a
looping structure, such as while, for, or do
...

This chapter began by designing a recursive method to find the factorial of a nonnegative
integer
...
Given our familiarity with iterative techniques, the iterative
solution will seem simpler than the recursive solution
...

In this chapter, we also used recursion to determine the largest element in a list,
determining a Fibonacci number
...
Similarly, an algorithm that uses an
iterative control structure can be designed to find the Fibonacci number
...
In addition to the nature of the solution, efficiency is the
other key factor in determining the better approach
...
When the function terminates, that memory space is deallocated
...
That is, every recursive call required the system to
allocate memory space for its formal parameters and local variables, and then deallocate the
memory space when the function exited
...
Therefore, a
recursive function executes more slowly than its iterative counterpart
...
Clearly, a recursive function is less efficient than a
corresponding iterative function in terms of execution time and memory usage
...
Chances are that
you have never been concerned with either execution time or memory usage as you have
been writing C++ programs
...
Chances are that you have considered carefully what you can do to minimize
the time required to produce C++ programs
...
As a
professional programmer, your time typically is far more expensive than the cost of the

6

376 |

Chapter 6: Recursion

computer you use to produce programs
...

Today’s computers are fast and have abundant memory
...
Given the ever-increasing speed and memory capacity of today’s computers, the
choice between iteration and recursion depends more and more on how the programmer
envisions the solution to the problem, and less on execution time and memory usage
...
Fortunately, any program that can be written recursively also
can be written iteratively
...
On the other hand, if the
recursive solution is more obvious and easier to construct, such as the solution to the
Towers of Hanoi problem, choose the recursive solution
...
You’ll quickly gain an increased appreciation for
recursion
...


Recursion and Backtracking: 8-Queens Puzzle
This section describes a problem-solving and algorithm design technique called backtracking
...
For any two queens to
be nonattacking, they cannot be in the same row, same column, or same diagonals
...


Q
Q
Q
Q
Q
Q
Q
Q

FIGURE 6-11

A solution to the 8-queens puzzle

Recursion and Backtracking: 8-Queens Puzzle

|

377

In 1850, the 8-queens puzzle was considered by the great C
...
Gauss, who was unable to
obtain a complete solution
...
H
...

In 1960, R
...
Walker gave an algorithmic account of backtracking
...
Golomb and L
...


Backtracking
The backtracking algorithm attempts to find solutions to a problem by constructing
partial solutions and making sure that any partial solution does not violate the problem
requirements
...
However, if it is determined that the partial solution would not lead to a solution, that is, the
partial solution would end in a dead end, then the algorithm backs up by removing the
most recently added part and trying other possibilities
...
, xn), where xi is an integer such that
1 xi n
...
Therefore, for the 8-queens puzzle the solution is an 8-tuple (x1, x2, x3, x4, x5, x6,
x7, x8), where xi is the column where to place the ith queen in the ith row
...
That is, the first
queen is placed in the first row and fourth column, the second queen is placed in the second
row and sixth column, and so on
...

Let us again consider the 8-tuple (x1, x2, x3, x4, x5, x6, x7, x8), where xi is an integer such
that 1 xi 8
...
However, because no two queens can be placed in the same row, no two
elements of the 8-tuple (x1, x2, x3, x4, x5, x6, x7, x8) are the same
...

The solution that we develop can, in fact, be applied to any number of queens
...
That is,
you are given a 4 by 4 square board (see Figure 6-12) and you are to place 4 queens on
the board so that no two queens attack each other
...

(A cross in a box means no other queen can be placed in that box
...
Clearly,
the first square in the second row where the second queen can be placed is the third
column
...

Next, we try to place the third queen in the third row
...
At this point we
backtrack to the previous board configuration and place the second queen in the fourth
column; see Figure 6-13(c)
...
This
time, we successfully place the third queen in the second column of the third row; see
Figure 6-13(d)
...

We backtrack to the third row and try placing the queen in any other column
...
We, therefore, backtrack to
the first row and place the first queen in the next column
...
This time we
obtain the solution, as shown by Figure 6-14
...
(Recall that in C++, an array
index starts at 0
...
We then place the second queen in the third
column of the second row and so generate the tuple (0,2)
...
With the partial solution (0,3), we then try to place the
third queen in the third row and generate the tuple (0,3,1)
...

From the partial solution (0,3,1), the backtracking algorithm, in fact, backs up to
placing the first queen and so removes all the elements of the tuple
...
In this case, the sequence of partial solution generated is, (1), (1,3),
(1,3,0), and (1,3,0,2), which represents a solution to the 4-queens puzzle
...


1

x 1= 1

x1 = 0
(0)
x2 = 1

2

x2 = 2

3

9

x2 = 3

4

x2 = 0

5 (0,3)

x3 = 1
(0,3,1) 6

10

(1)
x2 = 3

x2 = 2
11

12 (1,3)

x3 = 0
8

13 (1,3,0)

x4 = 2
7

FIGURE 6-15

14 (1,3,0,2)
solution

4-queens tree

8-Queens Puzzle
Let us now consider the 8-queens puzzle
...
Determining whether two
queens are in the same row or column is easy because we can check their row and
column position
...


6

380 |

Chapter 6: Recursion

Consider the 8 by 8 square board shown in Figure 6-16
...
(Recall that, in C++, the array index
starts at 0
...
The
positions of the squares on this diagonal are (0,4), (1,5), (2,6), and (3,7)
...
For example, 0 – 4 ¼ 1 – 5 ¼ 2 –
6 ¼ 3 – 7 ¼ – 4
...

Now consider the diagonal from upper right to lower left as indicated by the arrow
...

Here rowPosition + columnPosition ¼ 6
...

We can use these results to determine whether two queens are on the same diagonal or
not
...
These queens are on the same diagonal if either i +
j ¼ k + l or i - j ¼ k – l
...
From this it follows that the two queens are on the
same diagonal if |j – l| ¼ |i – k|, where |j – l| is the absolute value of j – l and so on
...
For example, queensInRow[0] = 3 means the first queen is placed in
column 3 (which is the fourth column) of row 0 (which is the first row)
...
Next we try to place the kth
queen in a column of the kth row
...

The first k-1 queens are in the first k-1 rows and we are trying to place the kth queen in
the kth row
...
It thus follows that the kth
queen can be placed in column i of row k, provided no other queen is in column i and no
queens are on the diagonals on which square (k, i ) lies
...
If it finds a queen at any of such positions, the for
loop returns the value false
...

The following class defines the n-queens puzzle as an ADT:
//***************************************************************
// D
...
Malik
//
// This class specifies the functions to solve the n-queens puzzle
...

//Postcondition: returns true if a queen can be placed in
//
row k and column i; otherwise it returns false
void queensConfiguration(int k);
//Function to determine all solutions to the n-queens
//puzzle using backtracking
...

//Postcondition: All n-tuples representing solutions of
//
n-queens puzzle are generated and printed
...

int solutionsCount();
//Function to return the total number of solutions
...

private:
int noOfSolutions;
int noOfQueens;
int *queensInRow;
};

The definitions of the member functions of the class nQueensPuzzle are given next:
nQueensPuzzle::nQueensPuzzle(int queens)
{
noOfQueens = queens;
queensInRow = new int[noOfQueens];
noOfSolutions = 0;
}
bool nQueensPuzzle::canPlaceQueen(int k, int i)
{
for (int j = 0; j < k; j++)
if ((queensInRow[j] == i)
|| (abs(queensInRow[j] - i) == abs(j-k)))
return false;
return true;
}

Using recursion, the function queensConfiguration implements the backtracking
technique to determine all solutions to the n-queens puzzle
...
Its definition is straightforward and is given next
...


6

Recursion, Backtracking, and Sudoku
In the previous section, we used recursion and backtracking to solve the 8-queens
problem
...
This problem involves filling numbers 1 to 9 in
a partially filled, 9 Â 9 grid with constraints described in this section
...


6

3
2

4
6

4
8
1
3

2
5

9
8

7
1 4 3
8
5
6
3 2
2
7 5 8
6 1

6 5 3
1

2

4
6

6
4
7
5

8
1
3

(a)

FIGURE 6-17

2
5

9
8

7
1 4 3
8
5
6
3 2
2
7 5 8
6 1

(b)

1
6
7
5

6
1
9
2
3
5
8
4
7

5
7
2
8
9
4
6
1
3

3
4
8
6
1
7
5
2
9

1
3
4
5
7
6
2
9
8

2
5
6
1
8
9
3
7
4

8
9
7
4
2
3
1
5
6

7
6
5
3
4
2
9
8
1

9
8
3
7
5
1
4
6
2

4
2
1
9
6
8
7
3
5

(c)

Sudoku problem and its solution

The sudoku grid is a 9 Â 9 grid consisting of nine rows, nine columns, and nine 3 Â 3
smaller grids
...
The first 3 Â 3 smaller grid is in rows 1 to 3 and columns
1 to 3, the second 3 Â 3 smaller grid is in rows 1 to 3 and columns 4 to 6, and so on
...
For example, the
solution to the sudoku problem in Figure 6-17(a) is shown in Figure 6-17(c)
...

Starting at the first row, we find the first empty grid slot
...
Next we find the first
number, between 1 and 9, that can be placed in this slot
...
) For example, in Figure 6-17(a), the first
number that can be placed in the second row and second column is 5; see Figure 6-17(b)
...
If a number cannot be placed in a slot, then we must backtrack to
the previous slot, where the number was placed, place a different number, and continue
...

The following class implements the sudoku problem as an ADT:
//***************************************************************
// D
...
Malik
//
// This class specifies the functions to solve a sudoku problem
...

//Postcondition: grid is initialized to the numbers
//
specified by the user
...

bool solveSudoku();
//Funtion to solve the sudoku problem
...


Recursion, Backtracking, and Sudoku |

385

bool findEmptyGridSlot(int &row, int &col);
//Function to determine if the grid slot specified by
//row and col is empty
...

bool numAlreadyInRow(int row, int num);
//Function to determine if num is in grid[row][]
//Postcondition: Returns true if num is in grid[row][],
//
otherwise it returns false
...

bool numAlreadyInBox(int smallGridRow, int smallGridCol,
int num);
//Function to determine if num is in the small grid
//Postcondition: Returns true if num is in small grid,
//
otherwise it returns false
...
For example, the partially filled
sudoku grid in Figure 6-17(a) is entered and stored as:
6
0
0
0
0
0
8
0
0

0
0
2
0
0
4
0
1
3

3
0
0
6
0
0
0
0
0

0
0
4
0
0
6
2
0
0

2
5
0
1
8
0
0
7
0

0
0
7
4
0
3
0
5
6

0
0
0
3
0
2
0
8
1

9
8
0
0
5
0
0
0
0

0
0
1
0
6
0
7
0
5

Next we write the definition of the function solveSudoku
...
The general algorithm is as follows:
1
...

If the grid has no empty slots, return true and print the solution
...
Suppose the variables row and col specify the position of the empty grid
position
...

}
If all the digits have been tried and nothing worked, return false
...


QUICK REVIEW
1
...

3
...

5
...

A recursive definition defines a problem in terms of smaller versions of itself
...

A recursive algorithm solves a problem by reducing it to smaller versions of itself
...


Exercises

6
...

8
...

10
...

12
...


|

387

The solution to the problem in a base case is obtained directly
...

Recursive algorithms are implemented using recursive functions
...

The general solution breaks the problem into smaller versions of itself
...

The base case stops the recursion
...

15
...

17
...

• Every call to a recursive function—that is, every recursive call—has its
own code and its own set of parameters and local variables
...
The current (recursive)
call must execute completely before control goes back to the previous
call
...

A function is called directly recursive if it calls itself
...

A recursive function in which the last statement executed is the recursive
call is called a tail recursive function
...

b
...

d
...

Determine the limiting conditions
...

Identify the base cases and provide a direct solution to each base case
...


EXERCISES
1
...

a
...

c
...

e
...

Every recursive function must have one or more base cases
...

In the general case, the solution to the problem is obtained directly
...


6

388 |

2
...

4
...

6
...


Chapter 6: Recursion

What is a base case?
What is a recursive case?
What is direct recursion?
What is indirect recursion?
What is tail recursion?
Consider the following recursive function:
int mystery(int number)
{
if (number == 0)
return number;
else
return(number + mystery(number - 1));
}

//Line
//Line
//Line
//Line
//Line
//Line
//Line

Identify the base case
...

Identify the general case
...

What valid values can be passed as parameters to the function mystery?
d
...

e
...

f
...

Consider the following recursive function:
a
...


void funcRec(int u, char v)
{
if (u == 0)
cout << v;
else if (u == 1)
cout << static_cast
(static_cast(v) + 1);
else
funcRec(u - 1, v);
}

Answer the following questions:
a
...

c
...

Identify the general case
...


1
2
3
4
5
6
7

Consider the following recursive function:
void exercise(int x)
{
if (x > 0 && x < 10)
{
cout << x << " ";
exercise(x + 1);
}
}

//Line
//Line
//Line
//Line
//Line

1
2
3
4
5

//Line
//Line
//Line
//Line

6
7
8
9

Exercises

|

389

What is the output of the following statements?
a
...


exercise(10);

d
...


exercise(0);

b
...

b
...


cout << test(5, 10) << endl;
cout << test(3, 9) << endl;

Consider the following function:
int func(int x)
{
if (x == 0)
return 2;
else if (x == 1)
return 3;
else
return (func(x - 1) + func(x - 2));
}

What is the output of the following statements?
a
...


cout << func(2) << endl;

d
...


cout << func(1) << endl;

c
...


cout << func(0) << endl;

cout << func(5) << endl;

Suppose that intArray is an array of integers, and length specifies the
number of elements in intArray
...

That is, low and high are two indices in intArray
...

Write a recursive algorithm to multiply two positive integers m and n using
repeated addition
...


6

390 |

14
...
The general problem can be stated as
follows: Find the number of ways r different things can be chosen from a
set of n items, where r and n are nonnegative integers and r n
...
Then C(n, r) is given by the following formula:
Cðn; rÞ ¼

n!
r!ðn À rÞ!

where the exclamation point denotes the factorial function
...
It is also known that C(n, r) ¼ C(n – 1, r – 1) + C(n – 1, r)
...


b
...
Identify the base
case(s) and the general case(s)
...


PROGRAMMING EXERCISES
1
...
If the nonnegative integer is 4,
the pattern generated is as follows:
****
***
**
*
*
**
***
****

2
...
For
example, specifying 4 as the number of lines generates the preceding pattern
...


|

391

Also, write a program that prompts the user to enter the number of lines in
the pattern and uses the recursive function to generate the pattern
...

Write a recursive a function to generate the following pattern of stars:
*
* *
* * *
* * * *
* * *
* *
*

4
...


6
...


8
...


Also, write a program that prompts the user to enter the number of lines in
the pattern and uses the recursive function to generate the pattern
...

Write a recursive function, vowels, that returns the number of vowels in a
string
...

Write a recursive function that finds and returns the sum of the elements of
an int array
...

A palindrome is a string that reads the same both forward and backward
...
Write a program that uses a
recursive function to check whether a string is a palindrome
...
Do not use any global variables;
use the appropriate parameters
...
Do
not use any global variables; use the appropriate parameters
...
Also, write a
program to test your function
...
You can use the following
recursive definition to calculate xy
...


y¼0
y¼1
y > 1:

6

392 |

10
...


11
...


13
...


Write a recursive function, gcd, that takes as parameters two integers and
returns the greatest common divisor of the numbers
...

Write a recursive function to implement the recursive algorithm of Exercise 12
(reversing the elements of an array between two indices)
...

Write a recursive function to implement the recursive algorithm of Exercise 13
(multiplying two positive integers using repeated addition)
...

Write a recursive function to implement the recursive algorithm of Exercise 14
(determining the number of ways to select a set of things from a given set of
things)
...

In the section ‘‘Converting a Number from Decimal to Binary,’’ in this
chapter, you learned how to convert a decimal number into the equivalent
binary number
...
In fact, in C++, you can
instruct the computer to store a number in octal or hexadecimal
...
The
digits in the hexadecimal number system are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B,
C, D, E, and F
...

The algorithm to convert a positive decimal number into an equivalent
number in octal (or hexadecimal) is the same as discussed for binary
numbers
...
Suppose ab represents the number a to the base b
...
Then:
75310 ¼ 13618
75310 ¼ 2F116
The method of converting a decimal number to base 2, or 8, or 16 can be extended
to any arbitrary base
...
You then divide the
decimal number n by b as in the algorithm for converting decimal to binary
...

Write a program that uses a recursive function to convert a number in decimal
to a given base b, where b is between 2 and 36
...

Test your program on the following data:

15
...


9098 and base 20
692 and base 2
753 and base 16
(Recursive Sequential Search) The sequential search algorithm given in
Chapter 3 is nonrecursive
...

The function sqrt from the header file cmath can be used to find the
square root of a nonnegative real number
...

Start with a ¼ x;
a
...


If |a2- x| epsilon, then a is the square root of x within the tolerance;
otherwise:
Replace a with (a2 + x) / (2a) and repeat Step a
where | a2- x| denotes the absolute value of a2- x
...


18
...


Write a recursive function to implement this algorithm to find the square
root of a nonnegative real number
...

Write a program to find solutions to the n-queens puzzle for various values
of n
...

Write the definitions of the remaining functions of the class sudoku
...

(Knight’s Tour) This chapter described the backtracking algorithm and
how to use recursion to implement it
...
Given an initial board position, determine a sequence of moves by a
knight that visits every square of the chessboard exactly once
...
Write a recursive backtracking
program that takes as input an initial board position and determines a sequence
of moves by a knight that visits each square of the board exactly once
...


Learn about stacks


...


Learn how to implement a stack as an array


...


Discover stack applications


...
It has numerous
applications in computer science
...
To be specific, suppose that you
have the functions A, B, C, and D in your program
...
When function
D terminates, control goes back to function C; when function C terminates, control goes
back to function B; and when function B terminates, control goes back to function A
...
What if you want to write a nonrecursive algorithm to print a linked list
backward?
This section discusses the data structure called the stack, which the computer uses to
implement function calls
...
Stacks
have numerous other applications in computer science
...

A stack is a list of homogenous elements in which the addition and deletion of elements
occurs only at one end, called the top of the stack
...
For another
example, to get to your favorite computer science book, which is underneath your math
and history books, you must first remove the math and history books
...
Figure 7-1 shows some examples of stacks
...
The top
element of the stack is the last element added to the stack
...
For this reason, a stack is also called a Last In First Out (LIFO) data
structure
...

Now that you know what a stack is, let us see what kinds of operations can be performed
on a stack
...
Similarly, because the top item
can be retrieved and/or removed from the stack, we can perform the operation top to
retrieve the top element of the stack, and the operation pop to remove the top element
from the stack
...
Initially, all of the boxes are on the floor and
the stack is empty
...
)

A
B

7

D
C

Empty stack

E

FIGURE 7-2

Empty stack

First we push box A onto the stack
...


(a) Push Box A
A

A

(b) Push Box B

(c) Push Box C

C
B

(d) Peek at the top element

FIGURE 7-3

C
B

AB

A

D
C
AB

(e) Push Box D

A

C
B

(f) Pop stack

Stack operations

We then push box B onto the stack
...
Next, we push box C onto the stack
...
Next, we look at the top element of the stack
...
We then push box D onto

398 |

Chapter 7: Stacks

the stack
...
Next, we pop
the stack
...

An element can be removed from the stack only if there is something in the stack, and an
element can be added to the stack only if there is room
...
Because a stack
keeps changing as we add and remove elements, the stack must be empty before we first
start using it
...
Therefore, to successfully implement a stack, we
need at least these six operations, which are described next
...
)



initializeStack—Initializes the stack to an empty state
...
If the stack
is empty, it returns the value true; otherwise, it returns the value
false
...
If the stack is full, it
returns the value true; otherwise, it returns the value false
...
The input to this
operation consists of the stack and the new element
...

• top—Returns the top element of the stack
...

• pop—Removes the top element of the stack
...


The following abstract class stackADT defines these operations as an ADT:
//*************************************************************
// Author: D
...
Malik
//
// This class specifies the basic operations on a stack
...

//Postcondition: Stack is empty
...

//Postcondition: Returns true if the stack is empty,
//
otherwise returns false
...

//Postcondition: Returns true if the stack is full,
//
otherwise returns false
...

//Precondition: The stack exists and is not full
...

virtual Type top() const = 0;
//Function to return the top element of the stack
...

//Postcondition: If the stack is empty, the program
//
terminates; otherwise, the top element of the stack
//
is returned
...

//Precondition: The stack exists and is not empty
...


Figure 7-4 shows the UML class diagram of the class stackADT
...
Functions such
as push and pop that are required to implement a stack are not available to C++
programmers
...

Because all the elements of a stack are of the same type, a stack can be implemented as
either an array or a linked structure
...


7

400 |

Chapter 7: Stacks

Implementation of Stacks as Arrays
Because all the elements of a stack are of the same type, you can use an array to
implement a stack
...
The top of the stack is the
index of the last element added to the stack
...
However, by definition, a stack is a data structure in which the elements are
accessed (popped or pushed) at only one end—that is, a Last In First Out data
structure
...
This feature of a stack is extremely important and must be
recognized in the beginning
...

The following class, stackType, implements the functions of the abstract class
stackADT
...
We assume that the
default stack size is 100
...
Moreover, we give a generic
definition of the stack
...

//*************************************************************
// Author: D
...
Malik
//
// This class specifies the basic operation on a stack as an
// array
...

void initializeStack();
//Function to initialize the stack to an empty state
...

//Postcondition: Returns true if the stack is empty,
//
otherwise returns false
...

//Postcondition: Returns true if the stack is full,
//
otherwise returns false
...

//Precondition: The stack exists and is not full
...

Type top() const;
//Function to return the top element of the stack
...

//Postcondition: If the stack is empty, the program
//
terminates; otherwise, the top element of the stack
//
is returned
...

//Precondition: The stack exists and is not empty
...

stackType(int stackSize = 100);
//Constructor
//Create an array of the size stackSize to hold
//the stack elements
...

//Postcondition: The variable list contains the base address
//
of the array, stackTop = 0, and maxStackSize = stackSize
stackType(const stackType& otherStack);
//Copy constructor
~stackType();
//Destructor
//Remove all the elements from the stack
...

private:
int maxStackSize; //variable to store the maximum stack size
int stackTop;
//variable to point to the top of the stack
Type *list; //pointer to the array that holds the stack elements

};

void copyStack(const stackType& otherStack);
//Function to make a copy of otherStack
...


7

402 |

Chapter 7: Stacks

Figure 7-5 shows the UML class diagram of the class stackType
...
If stackTop is nonzero, the stack is nonempty
and the top element of the stack is given by stackTop – 1 because the first stack
element is at position 0
...
It contains the code that
is common to the functions to overload the assignment operator and the copy constructor
...
To copy a stack into another stack, the program can use the assignment
operator
...

Note that stackTop can range from 0 to maxStackSize
...
Suppose that maxStackSize
= 100
...


...


...

D
C
B
A

stack
elements

Example of a stack

Note that the pointer list contains the base address of the array (holding the stack
elements)—that is, the address of the first array component
...


7

Initialize Stack
Let us consider the initializeStack operation
...
(See Figure 7-7
...


...

D
C
B
A

unused
stack

Empty stack

The definition of the function initializeStack is as follows:
template
void stackType::initializeStack()
{
stackTop = 0;
}//end initializeStack

404 |

Chapter 7: Stacks

Empty Stack
We have seen that the value of stackTop indicates whether the stack is empty
...
The definition of
the function isEmptyStack is as follows:
template
bool stackType::isEmptyStack() const
{
return(stackTop == 0);
}//end isEmptyStack

Full Stack
Next, we consider the operation isFullStack
...
The definition of the function isFullStack is as follows:
template
bool stackType::isFullStack() const
{
return(stackTop == maxStackSize);
} //end isFullStack

Push
Adding, or pushing, an element onto the stack is a two-step process
...
Therefore, the push operation is as follows:
1
...

2
...

Figure 7-8(a) shows the stack before pushing 'y' into the stack
...


[99]

[99]

...


...


...

stack

y

n

maxStackSize 100
stackTop
list

4

[3]

n

[2]

u

[1]

S

[0]

stack
elements maxStackSize 100
stackTop

(a) Before pushing y

FIGURE 7-8

Stack before and after the push operation

5

list
(b) After pushing y

[4]

n

stack

[3]

u

stack
[2] elements
[1]

S

[0]

n

Implementation of Stacks as Arrays

|

405

The definition of the function push is as follows:
template
void stackType::push(const Type& newItem)
{
if (!isFullStack())
{
list[stackTop] = newItem; //add newItem at the top
stackTop++; //increment stackTop
}
else
cout << "Cannot add to a full stack
...

Error checking for an overflow can be handled in different ways
...
Or, we can check for an overflow before calling the function push, as shown
next (assuming stack is an object of type stackType)
...
isFullStack())
stack
...
Its definition is as follows:
template
Type stackType::top() const
{
assert(stackTop != 0); //if stack is empty, terminate the
//program
return list[stackTop - 1]; //return the element of the stack
//indicated by stackTop - 1
}//end top

Pop
To remove, or pop, an element from the stack, we simply decrement stackTop by 1
...
Figure 7-9(b) shows the
stack after popping 'D' from the stack
...


...

stack


...


...
" << endl;
}//end pop

If we try to remove an item from an empty stack, the resulting condition is called an
underflow
...
One way
is as shown previously
...

if (!stack
...
pop();

Copy Stack
The function copyStack makes a copy of a stack
...
We will, in fact, use this function to implement
the copy constructor and overload the assignment operator
...
maxStackSize;
stackTop = otherStack
...
list[j];
} //end copyStack

Constructor and Destructor
The functions to implement the constructor and the destructor are straightforward
...
If
the user does not specify the size of the array in which to store the stack elements, the
constructor uses the default value, which is 100, to create an array of size 100
...
The definitions of the constructor and destructor are as follows:
template
stackType::stackType(int stackSize)
{
if (stackSize <= 0)
{
cout << "Size of the array to hold the stack must "
<< "be positive
...
" << endl;
maxStackSize = 100;
}
else
maxStackSize = stackSize;

stackTop = 0;
list = new Type[maxStackSize];
}//end constructor

//set the stack size to
//the value specified by
//the parameter stackSize
//set stackTop to 0
//create the array to
//hold the stack elements

template
stackType::~stackType() //destructor
{
delete [] list; //deallocate the memory occupied
//by the array
}//end destructor

Copy Constructor
The copy constructor is called when a stack object is passed as a (value) parameter to a
function
...
Its definition is as follows:

7

408 |

Chapter 7: Stacks

template
stackType::stackType(const stackType& otherStack)
{
list = NULL;
copyStack(otherStack);
}//end copy constructor

Overloading the Assignment Operator (=)
Recall that for classes with pointer member variables, the assignment operator must be
explicitly overloaded
...
For the sake of completeness, we next describe the header file
...
h
...

//Header file: myStack
...
h"
using namespace std;
//Place the definition of the class template stackType, as given
//previously in this chapter, here
...

#endif

Implementation of Stacks as Arrays

|

409

The analysis of the stack operations is similar to the operations of the class arrayListType
(Chapter 3)
...

TABLE 7-1 Time complexity of the operations of the class stackType on a stack with

n elements
Function

Time complexity

isEmptyStack

O (1)

isFullStack

O (1)

initializeStack

O (1)

constructor

O (1)

top

O (1)

push

O (1)

pop

O (1)

copyStack

O (n)

destructor

O (1)

copy constructor

O (n)

Overloading the assignment operator

O (n)

EXAMPLE 7-1
Before we give a programming example, let us first write a simple program that uses the
class stackType and tests some of the stack operations
...
The program and its output are as follows:
//*************************************************************
// Author: D
...
Malik
//
// This program tests various operations of a stack
...
h"
using namespace std;

7

410 |

Chapter 7: Stacks

void testCopyConstructor(stackType otherStack);
int main()
{
stackType stack(50);
stackType copyStack(50);
stackType dummyStack(100);
stack
...
push(23);
stack
...
push(38);
copyStack = stack; //copy stack into copyStack
cout << "The elements of copyStack: ";
while (!copyStack
...
top() << " ";
copyStack
...
isEmptyStack())
cout << "The original stack is not empty
...
top() << endl;
dummyStack = stack;

//copy stack into dummyStack

cout << "The elements of dummyStack: ";
while (!dummyStack
...
top() << " ";
dummyStack
...
isEmptyStack())
cout << "otherStack is not empty
...
top() << endl;
}

Programming Example: Highest GPA

|

411

Sample Run:
The elements of copyStack: 38 45 23
otherStack is not empty
...

The top element of the original stack: 38
The elements of dummyStack: 38 45 23

It is recommended that you do a walk-through of this program
...
The program then prints the highest
GPA and the names of all the students who received that GPA
...
Moreover, we assume that there are a maximum of 100 students
in the class
...
Sample data is as follows:
3
...
6
2
...
9
3
...
9
3
...
For example, for the preceding data, the highest GPA is 3
...


We read the first GPA and the name of the student
...
Next, we read the second GPA and the name of the student
...
Three cases arise:
1
...
In this case, we
do the following:
a
...

b
...

c
...


7

412 |

Chapter 7: Stacks

2
...
In this case, we
add the name of the new student to the stack
...
The new GPA is smaller than the highest GPA so far
...

We then read the next GPA and the name of the student, and repeat Steps 1
through 3
...

From this discussion, it is clear that we need the following variables:
double GPA;
//variable to hold
double highestGPA; //variable to hold
string name;
//variable to hold
stackType stack(100); //object

the current GPA
the highest GPA
the name of the student
to implement the stack

The previous discussion translates into the following algorithm:
1
...

3
...


Declare the variables and initialize stack
...

If the input file does not exist, exit the program
...
Also, set the precision to two decimal places
...
Read the GPA and the student name
...
highestGPA = GPA;
7
...
1
...
1
...
initializeStack(stack);
7
...
2
...
1
...
highestGPA = GPA;
}

7
...
else
if (GPA is equal to highestGPA)
push(stack, student name);

7
...
Read GPA and student name;
}

8
...

9
...


Programming Example: Highest GPA

|

413

PROGRAM LISTING
//*************************************************************
// Author: D
...
Malik
//
// This program reads a data file consisting of students' GPAs
// followed by their names
...

//*************************************************************
#include
#include
#include
#include






#include "myStack
...
open("HighestGPAData
...
Program terminates!" << endl;
return 1;
}
cout << fixed << showpoint;
cout << setprecision(2);

//Step 4
//Step 4

infile >> GPA >> name;

//Step 5

highestGPA = GPA;

//Step 6

while (infile)
{
if (GPA > highestGPA)
{
stack
...
1
//Step 7
...
1

414 |

Chapter 7: Stacks

if (!stack
...
push(name);

//Step 7
...
2

highestGPA = GPA;
//Step 7
...
3
}
else if (GPA == highestGPA)
//Step 7
...
isFullStack())
stack
...
"
<< "Program terminates!" << endl;
return 1; //exit program
}
}

infile >> GPA >> name;

//Step 7
...
isEmptyStack())
{
cout << stack
...
pop();
}
cout << endl;
}

return 0;

Sample Run:
Input File (HighestGPAData
...
4
3
...
5
3
...
8
3
...
6
3
...
8
3
...
9
3
...
9
2
...
9
3
...
90
The students holding the highest GPA are:
Vinay
Minnie
Andy

Note that the names of the students with the highest GPA are output in the reverse
order, relative to the order they appear in the input because the top element of the
stack is the last element added to the stack
...
If in a program the number of
elements to be pushed exceeds the size of the array, the program might terminate in an
error
...

We have seen that by using pointer variables we can dynamically allocate and deallocate
memory, and by using linked lists we can dynamically organize data (such as an ordered
list)
...

Recall that in the linear representation of a stack, the value of stackTop indicates the
number of elements in the stack, and the value of stackTop - 1 points to the top item in
the stack
...

Similar to the linear representation, in a linked representation stackTop is used to locate
the top element in the stack
...
In the former case,
stackTop gives the index of the array
...

The following class implements the functions of the abstract class stackADT:
//*************************************************************
// Author: D
...
Malik
//
// This class specifies the basic operation on a stack as a
// linked list
...

bool isEmptyStack() const;
//Function to determine whether the stack is empty
...

bool isFullStack() const;
//Function to determine whether the stack is full
...

void initializeStack();
//Function to initialize the stack to an empty state
...

//Precondition: The stack exists and is not full
...

Type top() const;
//Function to return the top element of the stack
...

//Postcondition: If the stack is empty, the program
//
terminates; otherwise, the top element of
//
the stack is returned
...

//Precondition: The stack exists and is not empty
...

linkedStackType();
//Default constructor
//Postcondition: stackTop = NULL;
linkedStackType(const linkedStackType& otherStack);
//Copy constructor
~linkedStackType();
//Destructor
//Postcondition: All the elements of the stack are removed
...

//Postcondition: A copy of otherStack is created and
//
assigned to this stack
...
Logically, the stack is never full
...
Therefore, in reality, the function isFullStack does not apply to
linked implementations of stacks
...


We leave the UML class diagram of the class linkedStackType as an exercise for you
...
)

7
EXAMPLE 7-2
Suppose that stack is an object of type linkedStackType
...


stack

stack

stackTop

stackTop

(a) Empty stack

C

B

A

(b) Nonempty stack

FIGURE 7-10

Empty and nonempty linked stacks

In Figure 7-10(b), the top element of the stack is C; that is, the last element pushed onto
the stack is C
...


Default Constructor
The first operation that we consider is the default constructor
...
Thus, this function
sets stackTop to NULL
...
The
stack is empty if stackTop is NULL
...
(The stack is full
only if we run out of memory
...
The definitions of the functions to implement these operations are as
follows:
template
bool linkedStackType::isEmptyStack() const
{
return(stackTop == NULL);
} //end isEmptyStack
template
bool linkedStackType::isFullStack() const
{
return false;
} //end isFullStack

Recall that in the linked implementation of stacks, the function isFullStack does not
apply because logically the stack is never full
...


Initialize Stack
The operation initializeStack reinitializes the stack to an empty state
...
The definition of this function is as follows:

Linked Implementation of Stacks

|

419

template
void linkedStackType:: initializeStack()
{
nodeType *temp; //pointer to delete the node
while (stackTop != NULL)
{

//while there are elements in
//the stack

temp = stackTop;

//set temp to point to the
//current node
stackTop = stackTop->link; //advance stackTop to the
//next node
delete temp;
//deallocate memory occupied by temp

}
} //end initializeStack

Next, we consider the push, top, and pop operations
...
In the case of pop, the node pointed to by stackTop will be
removed
...
The operation top
returns the info of the node to which stackTop is pointing
...


stack
stackTop

C

B

A

FIGURE 7-11

Stack before the push operation

7

420 |

Chapter 7: Stacks

Figure 7-12 shows the steps of the push operation
...
)

newNode

newNode
D

stack
stackTop

C

D

stack
stackTop

stack
C

B

stackTop

A

(a) Create newNode
and store D

D

C

B

A

FIGURE 7-12

newNode

B

A
(b) Put newNode on
the top of stack

(c) Make stackTop point
to the top element

Push operation

As shown in Figure 7-12, to push 'D' into the stack, first we create a new node and
store 'D' into it
...
Finally, we make
stackTop point to the top element of the stack
...


Return the Top Element
The operation to return the top element of the stack is quite straightforward
...

Consider the stack shown in Figure 7-13
...


temp
stack
temp

C

stackTop

C

stack
B

A

(a) Make temp point to the
top element

FIGURE 7-14

Pop operation

stackTop

B

stackTop

A

(b) Make stackTop point to
the next element

B

A

(c) Delete temp

422 |

Chapter 7: Stacks

As shown in Figure 7-14, first we make a pointer temp point to the top of the stack
...
Finally, we delete temp
...
" << endl;
}//end pop

Copy Stack
The function copyStack makes an identical copy of a stack
...
The definition of the function
copyStack is as follows:
template
void linkedStackType::copyStack
(const linkedStackType& otherStack)
{
nodeType *newNode, *current, *last;
if (stackTop != NULL) //if stack is nonempty, make it empty
initializeStack();
if (otherStack
...
stackTop; //set current to point
//to the stack to be copied
//copy the stackTop element of the stack
stackTop = new nodeType; //create the node
stackTop->info = current->info; //copy the info
stackTop->link = NULL; //set the link field to NULL
last = stackTop;
//set last to point to the node
current = current->link; //set current to point to the
//next node

Linked Implementation of Stacks

|

423

//copy the remaining stack
while (current != NULL)
{
newNode = new nodeType;
newNode->info = current->info;
newNode->link = NULL;
last->link = newNode;
last = newNode;
current = current->link;
}//end while
}//end else
} //end copyStack

Constructors and Destructors
We have already discussed the default constructor
...
(These functions
are similar to those discussed for linked lists in Chapter 5
...

TABLE 7-2 Time complexity of the operations of the class linkedStackType on a
stack with n elements

Function

Time complexity

isEmptyStack

O (1)

isFullStack

O (1)

initializeStack

O (n)

constructor

O (1)

top

O (1)

push

O (1)

pop

O (1)

copyStack

O (n)

destructor

O (n)

copy constructor

O (n)

Overloading the assignment operator

O (n)

The definition of a stack, and the functions to implement the stack operations discussed
previously, are generic
...
A client’s program can include
this header file via the include statement
...

EXAMPLE 7-3
We assume that the definition of the class linkedStackType and the functions to
implement the stack operations are included in the header file "linkedStack
...

//*************************************************************
// Author: D
...
Malik
//
// This program tests various operations of a linked stack
...
h"
using namespace std;
void testCopy(linkedStackType OStack);
int main()
{
linkedStackType stack;
linkedStackType otherStack;
linkedStackType newStack;
//Add elements into stack
stack
...
push(43);
stack
...
isEmptyStack())
{
cout << newStack
...
pop();
}
//Use the assignment operator to copy the elements
//of stack into otherStack
otherStack = stack;
cout << "Testing the copy constructor
...
isEmptyStack())
{
cout << otherStack
...
pop();
}
}

return 0;

7

426 |

Chapter 7: Stacks

//Function to test the copy constructor
void testCopy(linkedStackType OStack)
{
cout << "Stack in the function testCopy:" << endl;

}

while (!OStack
...
top() << endl;
OStack
...

Stack in the function testCopy:
27
43
34
After the copy constructor, otherStack:
27
43
34

Stack as Derived from the class unorderedLinkedList
If we compare the push function of the stack with the insertFirst function discussed
for general lists in Chapter 5, we see that the algorithms to implement these operations
are similar
...
Moreover,
the functions pop and isFullStack can be implemented as in the previous section
...
However, the class unorderedLinkedList is derived from the
the class linkedListType and provides the definitions of the abstract functions of
the class linkedListType
...

Next, we define the class linkedStackType that is derived from the class
unorderedLinkedList
...

#include
#include "unorderedLinkedList
...
For example, in the expression a + b, the operator + is between the
operands a and b
...
That is, we must
evaluate expressions from left to right, and multiplication and division have higher
precedence than addition and subtraction
...
For example, in the expression a + b * c, we
first evaluate * using the operands b and c, and then we evaluate + using the operand a
and the result of b * c
...
In the late 1950s, the Australian philosopher and early
computer scientist Charles L
...
This has the
advantage that the operators appear in the order required for computation
...

EXAMPLE 7-4
Infix expression
a+b
a+b*c
a*b+c
(a + b ) * c
(a – b) * (c + d)
(a + b) * (c – d / e) + f

Equivalent postfix expression
ab+
abc*+
ab*c+
ab+c*
ab–cd+*
ab+cde/–*f+

Shortly after Lukasiewicz’s discovery, it was realized that postfix notation had important
applications in computer science
...
Postfix expressions can be evaluated using the following algorithm:

Application of Stacks: Postfix Expressions Calculator |

429

Scan the expression from left to right
...

Consider the following postfix expression:
6 3 + 2 * =

Let us evaluate this expression using a stack and the previous algorithm
...


Expression: 6 3 + 2 * =
Push
6
into
stack

6
(a)

Push
2
into
stack

2
9
(e)

FIGURE 7-15

Push
3
into
stack

3
6

+
Pop
stack
twice
op2 = 3;
op1 = 6;

(b)
*
Pop
stack
twice
op2 = 2;
op1 = 9;
(f)

op1 + op2
= 9
Push 9
into
stack

9

(d)

(c)

op1 * op2
= 18
Push 18
into
stack
(g)

18

=
Pop
stack
and
print:
18

7

(h)

Evaluating the postfix expression: 6 3 + 2 * =

Read the first symbol, 6, which is a number
...
Read the next symbol, 3, which is a number
...
Read the next symbol, +, which is an operator
...

Perform the operation and put the result back onto the stack; see Figure 7-15(d)
...
Push the number onto the stack; see
Figure 7-15(e)
...
Because an operator
requires two operands to be evaluated, pop the stack twice; see Figure 7-15(f)
...

Scan the next symbol, =, which is the equal sign, indicating the end of the expression
...
The result of the expression is in the stack, so pop and print;
see Figure 7-15(h)
...


430 |

Chapter 7: Stacks

From this discussion, it is clear that when we read a symbol other than a number, the
following cases arise:
1
...

If the symbol is +, -, *, or /, the symbol is an operator and so we
must evaluate it
...

b
...
At this step, the stack must contain exactly one
element; otherwise, the expression has an error
...
The symbol we read is something other than +, -, *, /, or ¼
...

a
...

Consider the following expressions:
i
...
14 + 2 3 * =
iii
...
In the case of expression (iii), when we
encounter the equal sign (=), the stack will have two elements and this error cannot be
discovered until we are ready to print the value of the expression
...
If the symbol scanned is #, the
next input is a number (that is, an operand)
...

Furthermore, we assume that each expression contains only the +, -, *, and / operators
...
If the
expression has an error, the expression is discarded
...
Because an expression might
contain an error, we must clear the stack before processing the next expression
...

MAIN ALGORITHM
Pursuant to the previous discussion, the main algorithm in pseudocode is as follows:

Application of Stacks: Postfix Expressions Calculator |

431

Read the first character
while not the end of input data
{
a
...
process the expression
c
...
get the next expression
}

To simplify the complexity of the function main, we write four functions—
evaluateExpression, evaluateOpr, discardExp, and printResult
...
If the postfix expression is error free, the function printResult outputs the result
...

FUNCTION evaluateExpression
The function evaluateExpression evaluates each postfix expression
...
The general algorithm in pseudocode is as follows:
while (ch is not = '=') //process each expression
//= marks the end of an expression
{
switch (ch)
{
case '#':
read a number
output the number;
push the number onto the stack;
break;
default:
assume that ch is an operation
evaluate the operation;
} //end switch
if no error was found, then
{
read next ch;
output ch;
}
else
Discard the expression
} //end while

From this algorithm, it follows that this method has five parameters—a parameter to
access the input file, a parameter to access the output file, a parameter to access the stack, a
parameter to pass a character of the expression, and a parameter to indicate whether there
is an error in the expression
...
isFullStack())
stack
...
"
<< "Program terminates!" << endl;
exit(0); //terminate the program
}
break;
default:
evaluateOpr(outF, stack, ch, isExpOk);
}//end switch
if (isExpOk) //if no error
{
inpF >> ch;
outF << ch;

}
else

if (ch != '#')
outF << " ";

discardExp(inpF, outF, ch);
} //end while (!= '=')
} //end evaluateExpression

Note that the funtion exit terminates the program
...
Two operands are needed to evaluate
an operation and operands are saved in the stack
...
If the stack contains fewer than two numbers, the expression has an
error
...
This function also checks for any illegal operations
...

switch (ch)
{
case '+': //add the operands: op1 + op2
stack
...
push(op1 - op2);
break;
case '*': //multiply the operands: op1 * op2
stack
...
push(op1 / op2);
break;
otherwise operation is illegal
{
output an appropriate message;
set expressionOk to false
}
} //end switch

Following this pseudocode, the definition of the function evaluateOpr is as follows:
void evaluateOpr(ofstream& out, stackType& stack,
char& ch, bool& isExpOk)
{
double op1, op2;

7

434 |

Chapter 7: Stacks

if (stack
...
top();
stack
...
isEmptyStack())
{
out << " (Not enough operands)";
isExpOk = false;
}
else
{
op1 = stack
...
pop();
switch (ch)
{
case '+':
stack
...
push(op1 - op2);
break;
case '*':
stack
...
push(op1 / op2);
else
{
out << " (Division by 0)";
isExpOk = false;
}
break;
default:
out << " (Illegal operator)";
isExpOk = false;
}//end switch
} //end else
} //end else
} //end evaluateOpr

Application of Stacks: Postfix Expressions Calculator |

435

FUNCTION discardExp
This function is called whenever an error is discovered in the expression
...
The definiton
of this function is as follows:
void discardExp(ifstream& in, ofstream& out, char& ch)
{
while (ch != '=')
{
in
...
The result of the expression is in the stack
and the output is sent to a file
...
Suppose that no errors were encountered by the method
evaluateExpression
...
If either the stack is empty or it has more than
one element, there is an error in the postfix expression
...
The definition of this method is as follows:
void printResult(ofstream& outF, stackType& stack,
bool isExpOk)
{
double result;
if (isExpOk) //if no error, print the result
{
if (!stack
...
top();
stack
...
isEmptyStack())
outF << result << endl;
else
outF << " (Error: Too many operands)" << endl;
} //end if
else
outF << " (Error in the expression)" << endl;

}
else

outF << " (Error in the expression)" << endl;
outF << "_________________________________"
<< endl << endl;
} //end printResult

7

436 |

Chapter 7: Stacks

PROGRAM LISTING
//*************************************************************
// Author: D
...
Malik
//
// This program uses a stack to evaluate postfix expressions
...
h"

using namespace std;
void evaluateExpression(ifstream& inpF, ofstream& outF,
stackType& stack,
char& ch, bool& isExpOk);
void evaluateOpr(ofstream& out, stackType& stack,
char& ch, bool& isExpOk);
void discardExp(ifstream& in, ofstream& out, char& ch);
void printResult(ofstream& outF, stackType& stack,
bool isExpOk);
int main()
{
bool expressionOk;
char ch;
stackType stack(100);
ifstream infile;
ofstream outfile;
infile
...
txt");
if (!infile)
{
cout << "Cannot open the input file
...
open("RpnOutput
...
initializeStack();
expressionOk = true;
outfile << ch;

Application of Stacks: Postfix Expressions Calculator |

437

evaluateExpression(infile, outfile, stack, ch,
expressionOk);
printResult(outfile, stack, expressionOk);
infile >> ch; //begin processing the next expression
} //end while
infile
...
close();
return 0;
} //end main
//Place the definitions of the function evaluateExpression,
//evaluateOpr, discardExp, and printResult as described
//previously here
...
00 #27
...
00 * = 186
...
00 #28
...
00 #2
...
00 #30
...
00 * / = 0
...
00 #3
...
00 + = (Error: Too many operands)
_________________________________
#20
...
00 #9
...
00 #23
...
00 #24
...
00 #7
...
00 - = 52
...
In this section, you will
learn how a stack can be used to design a nonrecursive algorithm to print a linked list
backward
...


first

FIGURE 7-16

5

10

15

Linked list

To print the list backward, first we need to get to the last node of the list, which we can
do by traversing the linked list starting at the first node
...
Moreover, if we do this for every node in the list, the
program might execute very slowly
...

After printing the info of a particular node, we need to move to the node immediately
behind this node
...
Thus, while initially traversing the list to move to the last node, we must save a
pointer to each node
...
After printing 15, we go back to the node with
info 10; after printing 10, we go back to the node with info 5
...

Because the number of nodes in a linked list is usually not known, we will use the linked
implementation of a stack
...
Consider the following
statements:
current = first;

//Line 1

while (current != NULL)
{
stack
...
(See Figure 7-17)

stack
first

10

5

15

stackTop

current

FIGURE 7-17

List after the statement current = first; executes

Because current is not NULL, the statements in Lines 4 and 5 execute
...
)

stack
first

5

10

15

stackTop

current

5

7
FIGURE 7-18

List and stack after the statements stack
...
In fact, statements in
Lines 4 and 5 execute until current beomes NULL
...


stack
first

5

10

15

stackTop

15

current
10

5

FIGURE 7-19

List and stack after the while statement executes

440 |

Chapter 7: Stacks

After the statement in Line 4 executes, the loop condition, in Line 2, is evaluated again
...
From Figure 7-19, it follows that a pointer to each node in the linked
list is saved in the stack
...
Let us now execute the following statements:
while (!stack
...
top();
stack
...
Therefore, the statements in Lines 9, 10, and 11 execute
...
The statement in Line 10 removes the top element of
the stack; see Figure 7-20
...
top(); and stack
...

Because stack is nonempty, the body of the while loop executes again
...
Ater printing 5, the stack becomes empty and the while
loop terminates
...
Because a stack is an
important data structure, the Standard Template Library (STL) provides a class to implement a stack in a program
...
The implementation of the class stack provided by the STL is similar to the one described in this
chapter
...

TABLE 7-3

Operations on a stack object

Operation

Effect

size

Returns the actual number of elements in
the stack
...


push(item)

Inserts a copy of item into the stack
...
This operation is implemented as a
value-returning function
...


pop

In addition to the operations size, empty, push, top, and pop, the stack container
class provides relational operators to compare two stacks
...

The program in Example 7-5 illustrates how to use the stack container class
...
S
...

//*************************************************************
#include
#include

//Line 1
//Line 2

using namespace std;

//Line 3

int main()
{
stack

//Line 4
//Line 5
//Line 6

intStack;

7

442 |

Chapter 7: Stacks

intStack
...
push(8);
intStack
...
push(3);

//Line
//Line
//Line
//Line

7
8
9
10

cout << "Line 11: The top element of intStack: "
<< intStack
...
pop();

//Line 12

cout << "Line 13: After the pop operation, the "
<< " top element of intStack: "
<< intStack
...
empty())
{
cout << intStack
...
pop();
}

//Line
//Line
//Line
//Line
//Line

cout << endl;
}

//Line 14

//Line 20

return 0;

//Line 21
//Line 22

15
16
17
18
19

Sample Run:
Line 11: The top element of intStack: 3
Line 13: After the pop operation, the top element of intStack: 20
Line 14: intStack elements: 20 8 16

The preceding output is self-explanatory
...


QUICK REVIEW
1
...

3
...

5
...


A stack is a data structure in which the items are added and deleted from one
end only
...

The basic operations on a stack are as follows: Push an item onto the stack,
pop an item from the stack, retrieve the top element of the stack, initialize the
stack, check whether the stack is empty, and check whether the stack is full
...

The middle elements of a stack should not be accessed directly
...


Exercises

7
...

9
...

In postfix notation, the operators are written after the operands
...

If an operator is found, back
operands, evaluate the operator,
The STL class stack can be used
a
...


|

right
...

to implement a stack in a program
...


Consider the following statements:
stackType stack;
int x, y;

Show what is output by the following segment of code:
x = 4;
y = 0;
stack
...
push(x);
stack
...
top();
stack
...
push(x + y);
stack
...
push(3);
x = stack
...
pop();
cout << "x = " << x << endl;
cout << "y = " << y << endl;
while (!stack
...
top() << endl;
stack
...


Consider the following statements:
stackType stack;
int x;

Suppose that the input is:
14 45 34 23 10 5 -999

Show what is output by the following segment of code:

7

444 |

Chapter 7: Stacks

stack
...
isFullStack())
stack
...
isEmptyStack())
{
cout << " " << stack
...
pop();
}
cout << endl;
3
...


12 25 5 1 / / * 8 7 + - =

c
...

4
...


3 5 6 * + 13 - 18 2 / + =

Convert the following infix expressions to postfix notations:
a
...


(A + B) * (C + D) - E
A - (B + C) * D + E / F

c
...

5
...

b
...

6
...
h"
using namespace std;

Exercises

|

445

template
void mystery(stackType& s, stackType& t);
int main()
{
stackType s1;
stackType s2;
string list[] = {"Winter", "Spring", "Summer", "Fall",
"Cold", "Warm", "Hot"};
for (int i = 0; i < 7; i++)
s1
...
isEmptyStack())
{
cout << s2
...
pop();
}
cout << endl;

template
void mystery(stackType& s, stackType& t)
{
while (!s
...
push(s
...
pop();
}
}
7
...
The classes stackADT, stackType, and linkedStackType
are as defined in this chapter
...

b
...


stackType names;

d
...


stackADT newStack;

linkedStackType numStack(50);

What is the output of the following program?
#include
#include
#include "myStack
...
push(list[i]);
mystery(s1, s2);

}

while (!s2
...
top() << " ";
s2
...
isEmptyStack())
{
t
...
top());
s
...


What is the output of the following program segment?
linkedStackType myStack;
myStack
...
push(20);
myStack
...
top() << endl;
myStack
...
push(2 * myStack
...
push(-10);
myStack
...
isEmptyStack())
{
cout << tempStack
...
pop();
}
cout << endl;
cout << myStack
...


11
...

13
...
Assume that this function is a
member of the class linkedListType, designed in Chapter 5
...
The
original stack remains unchanged
...

Write the definition of the function template clear that takes as a parameter a stack object of the type stack (STL class) and removes all the
elements from the stack
...


2
...


Two stacks of the same type are the same if they have the same number of
elements and their elements at the corresponding positions are the same
...
Also,
write the definition of the function template to overload this operator
...

a
...

Consider the following statements:
stackType stack1;
stackType stack2;

The statement
stack1
...

5
...
That is,
the top element of stack1 is the bottom element of stack2, and so on
...

b
...

Repeat Exercises 3a and 3b for the class linkedStackType
...
The program
outputs whether the expression contains matching grouping symbols
...
However, the expression 5 + {(13 + 7) / 8 - 2 * 9
does not contain matching grouping symbols
...


7
...

(Converting a Number from Binary to Decimal) The language of a
computer, called machine language, is a sequence of 0s and 1s
...
Also,
the collating sequence of A in the ASCII character set is 65
...

The numbering system we use is called the decimal system, or base 10
system
...
The purpose of this exercise is to write a
function to convert a number from base 2 to base 10
...
The weight of each bit in the binary number is
assigned from right to left
...
The weight
of the bit immediately to the left of the rightmost bit is 1, the weight of the
bit immediately to the left of it is 2, and so on
...
The weight of each bit is as follows:
weight 6 5 4 3 2 1 0
1001101
We use the weight of each bit to find the equivalent decimal number
...
For the binary number 1001101, the equivalent
decimal number is
1 Â 26 þ 0 Â 25 þ 0 Â 24 þ 1 Â 23 þ 1 Â 22 þ 0 Â 21 þ 1 Â 20
¼ 64 þ 0 þ 0 þ 8 þ 4 þ 0 þ 1 ¼ 77

8
...
Because we do not know in advance how many bits are in the binary
number, we must process the bits from right to left
...
Also, each bit must be extracted from the binary number and multiplied
by 2 to the power of its weight
...
Write a program that uses a stack to convert a binary number into
an equivalent decimal number and test your function for the following
values: 11000101, 10101010, 11111111, 10000000, 1111100000
...
Write a program that uses a stack to convert a
decimal number into an equivalent binary number
...


|

449

(Infix to Postfix) Write a program that converts an infix expression into an
equivalent postfix expression
...
The rules to convert infx into pfx are as follows:
a
...


Initialize pfx to an empty expression and also initialize the stack
...

b
...

b
...

b
...


b
...


If sym is an operand, append sym to pfx
...

If sym is ), pop and append all the symbols from the stack until the
most recent left parenthesis
...

If sym is an operator:

Pop and append all the operators from the stack to pfx that
are above the most recent left parenthesis and have precedence greater than or equal to sym
...
4
...
Push sym onto the stack
...
Pop
and append to pfx everything from the stack
...
4
...


c
...
You may assume that the expressions you will process are
error free
...
The class must include
the following operations:




getInfix—Stores the infix expression
showInfix—Outputs the infix expression
showPostfix—Outputs the postfix expression

Some other operations that you might need are the following:


convertToPostfix—Converts the infix expression into a postfix
expression
...

• precedence—Determines the precedence between two operators
...


Include the constructors and destructors for automatic initialization and
dynamic memory deallocation
...


11
...

Redo Programming Exercise 9 so that it uses the STL class stack to
convert the infix expressions to postfix expressions
...


Learn about queues


...


Learn how to implement a queue as an array


...


Discover queue applications


...
The notion of a
queue in computer science is the same as the notion of the queues to which you are
accustomed in everyday life
...
Similarly, because a computer can
send a print request faster than a printer can print, a queue of documents is often waiting to
be printed at a printer
...
That is, a queue is a First In First Out data structure
...
Whenever a system is modeled
on the First In First Out principle, queues are used
...
First,
however, we need to develop the tools necessary to implement a queue
...

A queue is a set of elements of the same type in which the elements are added at one end,
called the back or rear, and deleted from the other end, called the front
...
Each new customer gets in the line at the rear
...

The rear of the queue is accessed whenever a new element is added to the queue, and the
front of the queue is accessed whenever an element is deleted from the queue
...

Queue: A data structure in which the elements are added at one end, called the rear, and
deleted from the other end, called the front; a First In First Out (FIFO) data structure
...
We
call the add operation addQueue and the delete operation deleteQueue
...

We also need an operation, initializeQueue, to initialize the queue to an empty state
...
Some of the queue operations are as follows:



initializeQueue—Initializes the queue to an empty state
...
If the queue
is empty, it returns the value true; otherwise, it returns the value false
...
If the queue is
full, it returns the value true; otherwise, it returns the value false
...
Prior
to this operation, the queue must exist and must not be empty
...
Prior to this operation, the
queue must exist and must not be empty
...
Prior to this
operation, the queue must exist and must not be full
...
Prior to
this operation, the queue must exist and must not be empty
...
We will
consider both implementations
...

The following abstract class queueADT defines these operations as an ADT:
//*************************************************************
// Author: D
...
Malik
//
// This class specifies the basic operations on a queue
...

//Postcondition: Returns true if the queue is empty,
//
otherwise returns false
...

//Postcondition: Returns true if the queue is full,
//
otherwise returns false
...

//Postcondition: The queue is empty
...

//Precondition: The queue exists and is not empty
...


8

454 |

Chapter 8: Queues

virtual Type back() const = 0;
//Function to return the last element of the queue
...

//Postcondition: If the queue is empty, the program
//
terminates; otherwise, the last element of the queue
//
is returned
...

//Precondition: The queue exists and is not full
...


};

virtual void deleteQueue() = 0;
//Function to remove the first element of the queue
...

//Postcondition: The queue is changed and the first element
//
is removed from the queue
...


Implementation of Queues as Arrays
Before giving the definition of the class to implement a queue as an ADT, we need to
decide how many member variables are needed to implement the queue
...
Thus, we need at least four member variables
...
How do queueFront
and queueRear indicate that the queue is empty or full? Suppose that queueFront gives
the index of the first element of the queue, and queueRear gives the index of the last
element of the queue
...

To delete an element from the queue, first we retrieve the element that queueFront is
pointing to and then advance queueFront to the next element of the queue
...

Let us see what happens when queueFront changes after a deleteQueue operation and
queueRear changes after an addQueue operation
...

Initially, the queue is empty
...


[0] [1]
[98][99]
A

...


[0] [1] [2] [3]
[98][99]
A
B
C

...


[0] [1] [2] [3]
[98][99]
A
B
C

...
Consider the following sequence of operations:
AAADADADADADADADA
...
However, the
queue has only two or three elements and the front of the array is empty
...
)

[0] [1] [2]

[97] [98][99]

...


One solution to this problem is that when the queue overflows to the rear (that is,
queueRear points to the last array position), we can check the value of the index
queueFront
...
This solution is good if the queue size is very small;
otherwise, the program might execute more slowly
...
(See Figure 8-5
...


Implementation of Queues as Arrays

|

457

Suppose that we have the queue as shown in Figure 8-6(a)
...


[0] [1]
[98][99]
Z

...

Because the array containing the queue is circular, we can use the following statement to
advance queueRear (queueFront) to the next array position:
queueRear = (queueRear + 1) % maxQueueSize;

If queueRear < maxQueueSize - 1, then queueRear + 1 <= maxQueueSize - 1, so
(queueRear + 1) % maxQueueSize = queueRear + 1
...
In this case, queueRear
will be set to 0, which is the first array position
...
Before we write the algorithms to implement the
queue operations, consider the following two cases
...


[0]

...


queueFront 99 queueRear 98
(b) After deleteQueue();

Queue before and after the delete operation

After the operation deleteQueue();, the resulting array is as shown in Figure 8-7(b)
...


[0]

[97] [98][99]

...

Z
queue elements

queueFront 99 queueRear 97
(a) Before addQueue(Queue,'Z');

FIGURE 8-8

queueFront 99 queueRear 98
(b) After addQueue(Queue,'Z');

Queue before and after the add operation

After the operation addQueue(Queue,'Z');, the resulting array is as shown in Figure 8-8(b)
...
However, the resulting array in Figure 8-7(b) represents an empty queue,
whereas the resulting array in Figure 8-8(b) represents a full queue
...

This problem has several solutions
...
In addition to the member
variables queueFront and queueRear, we need another variable, count, to implement the
queue
...
In this case, the
function initializeQueue initializes count to 0
...

Another solution is to let queueFront indicate the index of the array position preceding the
first element of the queue, rather than the index of the (actual) first element itself
...
In this solution, the slot indicated by the
index queueFront (that is, the slot preceding the first true element) is reserved
...

Finally, because the array position indicated by queueFront is to be kept empty, if the
array size is, say, 100, then 99 elements can be stored in the queue
...
)

reserved slot
[0] [1] [2]

[97] [98][99]

...
That is, we use the variable count to
indicate whether the queue is empty or full
...
Because
arrays can be allocated dynamically, we will leave it for the user to specify the size of the
array to implement the queue
...

//*************************************************************
// Author: D
...
Malik
//
// This class specifies the basic operation on a queue as an
// array
...

bool isEmptyQueue() const;
//Function to determine whether the queue is empty
...

bool isFullQueue() const;
//Function to determine whether the queue is full
...

void initializeQueue();
//Function to initialize the queue to an empty state
...

Type front() const;
//Function to return the first element of the queue
...

//Postcondition: If the queue is empty, the program
//
terminates; otherwise, the first element of the
//
queue is returned
...

//Precondition: The queue exists and is not empty
...

void addQueue(const Type& queueElement);
//Function to add queueElement to the queue
...


8

460 |

Chapter 8: Queues

//Postcondition: The queue is changed and queueElement is
//
added to the queue
...

//Precondition: The queue exists and is not empty
...

queueType(int queueSize = 100);
//Constructor
queueType(const queueType& otherQueue);
//Copy constructor
~queueType();
//Destructor
private:
int maxQueueSize; //variable to store the maximum queue size
int count;
//variable to store the number of
//elements in the queue
int queueFront;
//variable to point to the first
//element of the queue
int queueRear;
//variable to point to the last
//element of the queue
Type *list;
//pointer to the array that holds
//the queue elements
};

We leave the UML diagram of the class queueType as an exercise for you
...
)
Next, we consider the implementation of the queue operations
...
So the functions to implement these operations are as follows:
template
bool queueType::isEmptyQueue() const
{
return (count == 0);
} //end isEmptyQueue
template
bool queueType::isFullQueue() const
{
return (count == maxQueueSize);
} //end isFullQueue

Implementation of Queues as Arrays

|

461

Initialize Queue
This operation initializes a queue to an empty state
...
Therefore, we initialize queueFront to 0, queueRear to maxQueueSize - 1,
and count to 0
...


[0] [1] [2]

[97] [98][99]

...
If the queue is nonempty, the
element of the queue indicated by the index queueFront is returned; otherwise, the
program terminates
...
If the queue is nonempty, the
element of the queue indicated by the index queueRear is returned; otherwise, the
program terminates
...
Because queueRear points to the last
element of the queue, to add a new element to the queue, we first advance queueRear to
the next array position, and then add the new element to the array position indicated by
queueRear
...
So the function addQueue is as follows:
template
void queueType::addQueue(const Type& newElement)
{
if (!isFullQueue())
{
queueRear = (queueRear + 1) % maxQueueSize; //use the
//mod operator to advance queueRear
//because the array is circular
count++;
list[queueRear] = newElement;
}
else
cout << "Cannot add to a full queue
...
Because
queueFront points to the array position containing the first element of the queue, to
remove the first queue element, we decrement count by 1 and advance queueFront to
the next queue element
...
The constructor gets the maxQueueSize
from the user, sets the variable maxQueueSize to the value specified by the user, and
creates an array of size maxQueueSize
...
The

Linked Implementation of Queues

|

463

constructor also initializes queueFront and queueRear to indicate that the queue is
empty
...
" << endl;
cout << "Creating an array of size 100
...
Therefore, when the queue
object goes out of scope, the destructor simply deallocates the memory occupied by the
array that stores the queue elements
...
(The
definitions of these functions are similar to those discussed for array-based lists and stacks
...
Also, the array implementation of the queue
requires the array to be treated in a special way together with the values of the indices
queueFront and queueRear
...
This section discusses the linked
implementation of a queue
...
Thus, we
need two pointers, queueFront and queueRear, to maintain the queue
...
S
...

//*************************************************************
//Definition of the node
template
struct nodeType
{
Type info;
nodeType *link;
};
template
class linkedQueueType: public queueADT
{
public:
const linkedQueueType& operator=
(const linkedQueueType&);
//Overload the assignment operator
...

//Postcondition: Returns true if the queue is empty,
//
otherwise returns false
...

//Postcondition: Returns true if the queue is full,
//
otherwise returns false
...

//Postcondition: queueFront = NULL; queueRear = NULL
Type front() const;
//Function to return the first element of the queue
...

//Postcondition: If the queue is empty, the program
//
terminates; otherwise, the first element of the
//
queue is returned
...

//Precondition: The queue exists and is not empty
...

void addQueue(const Type& queueElement);
//Function to add queueElement to the queue
...

//Postcondition: The queue is changed and queueElement is
//
added to the queue
...

//Precondition: The queue exists and is not empty
...

linkedQueueType();
//Default constructor
linkedQueueType(const linkedQueueType& otherQueue);
//Copy constructor
~linkedQueueType();
//Destructor
private:
nodeType *queueFront; //pointer to the front of the queue
nodeType *queueRear; //pointer to the rear of the queue
};

The UML diagram of the class linkedQueueType is left as an exercise for you
...
)
Next, we write the definitions of the functions of the class linkedQueueType
...
Memory to store the queue elements is
allocated dynamically
...
(The queue is full only if the
program runs out of memory
...
However, you must provide its
definition because it is included as an abstract function in the parent class queueADT
...
The queue is
empty if there are no elements in the queue
...
So this operation must remove all the elements,
if any, from the queue
...

The definition of this function is as follows:
template
void linkedQueueType::initializeQueue()
{
nodeType *temp;
while (queueFront!= NULL)
{

}

//while there are elements left
//in the queue

temp = queueFront; //set temp to point to the current node
queueFront = queueFront->link; //advance first to
//the next node
delete temp;
//deallocate memory occupied by temp

queueRear = NULL; //set rear to NULL
} //end initializeQueue

addQueue, front, back, and deleteQueue Operations
The addQueue operation adds a new element at the end of the queue
...

If the queue is nonempty, the operation front returns the first element of the queue and
so the element of the queue indicated by the pointer queueFront is returned
...

If the queue is nonempty, the operation back returns the last element of the queue and so
the element of the queue indicated by the pointer queueRear is returned
...
Similarly, if the queue is nonempty,
the operation deleteQueue removes the first element of the queue, and so we access the
pointer queueFront
...
The definition of the function
to implement the destructor is similar to the definition of the function initializeQueue
...
Implementing these operations is
left as an exercise for you, (see Programming Exercise 2 at the end of this chapter)
...
It uses the class
linkedQueueType to implement a queue
...
S
...

//***************************************************************
#include
#include "linkedQueue
...
initializeQueue();
x = 4;
y = 5;
queue
...
addQueue(y);
x = queue
...
deleteQueue();
queue
...
addQueue(16);
queue
...
addQueue(y - 3);
cout << "Queue Elements: ";

STL class queue (Queue Container Adapter)

|

469

while (!queue
...
front() << " ";
queue
...
The addQueue operation is similar to the operation
insertFirst
...
The deleteQueue operation can be
implemented as before
...
This correspondence suggests that
we can derive the class to implement the queue from the class linkedListType (see
Chapter 5)
...
However, the class unorderedLinkedList is derived from the
the class linkedListType and provides the definitions of the abstract functions of the
the class linkedListType
...

We leave it as an exercise for you to write the definition of the class linkedQueueType
that is derived from the class unorderedLinkedList (see Programming Exercise 7
at the end of this chapter)
...
Because a queue is an
important data structure, the Standard Template Library (STL) provides a class to implement queues in a program
...
The
class queue provided by the STL is implemented similar to the classes discussed in this
chapter
...


8

470 |

Chapter 8: Queues

TABLE 8-1

Operations on a queue object

Operation

Effect

size

Returns the actual number of elements in the queue
...


push(item)

Inserts a copy of item into the queue
...
This operation
is implemented as a value-returning function
...
This operation is implemented as
a value-returning function
...


In addition to the operations size, empty, push, front, back, and pop, the queue
container class provides relational operators to compare two queues
...

The program in Example 8-2 illustrates how to use the queue container class
...
S
...

//***************************************************************
#include
#include

//Line 1
//Line 2

using namespace std;

//Line 3

int main()
{
queue intQueue;

//Line 4
//Line 5
//Line 6

intQueue
...
push(18);
intQueue
...
push(33);

//Line
//Line
//Line
//Line

7
8
9
10

Priority Queues

|

471

cout << "Line 11: The front element of intQueue: "
<< intQueue
...
back() << endl;
intQueue
...
front() << endl;

//Line 14

cout << "Line 15: intQueue elements: ";

//Line 15

while (!intQueue
...
front() << " ";
intQueue
...
The details are left as an exercise for you
...
The use of a
queue structure ensures that the items are processed in the order they are received
...

However, there are certain situations when this First In First Out rule needs to be relaxed
somewhat
...

Therefore, you could use a queue to ensure that the patients are seen in the order they
arrive
...
In other words, these patients take priority over the patients who can wait to
be seen, such as those awaiting their routine annual checkup
...


8

472 |

Chapter 8: Queues

There are many other situations where some priority is assigned to the customers
...
In a priority queue, customers or jobs with higher priority are pushed to the front
of the queue
...
However, an effective way to implement a priority queue is to use a treelike structure
...
After
describing this algorithm, we discuss how to effectively implement a priority queue
...
This class template is contained in
the STL header file queue
...
The default priority criteria for the queue elements uses the lessthan operator, <
...
If you design your own class to implement the queue
elements, you can specify your priority rule by overloading the less-than operator, <, to
compare the elements
...
The implementation of comparison functions is discussed in Chapter 13
...
For example, physical simulators include wind tunnels used to experiment
with the design of car bodies and flight simulators are used to train airline pilots
...

You can also design computer models to study the behavior of real systems
...
) Simulating the behavior of an
expensive or dangerous experiment using a computer model is usually less expensive than
using the real system, and a good way to gain insight without putting human life in danger
...
For such systems, computer models can retain
descriptive accuracy
...
Let us consider one such problem
...
The theater currently has only one cashier
...
The manager wants to hire enough cashiers so that a customer does not
have to wait too long to buy a ticket, but does not want to hire extra cashiers on a trial
basis and potentially waste time and money
...
The manager wants someone
to write a program to simulate the behavior of the theater
...
For the
theater problem, some of the objects are the customers and the cashier
...
Actions are
implemented by writing algorithms, which in a programming language are implemented
with the help of functions
...
In C++, we can combine the data and the operations on that data into a single unit
with the help of classes
...
The member variables
of the class describe the properties of the objects, and the function members describe the
actions on that data
...
The main goal of a computer simulation is to either generate
results showing the performance of an existing system or predict the performance of a
proposed system
...
Because customers are served on a first-come, first-served basis and queues are an
effective way to implement a First In First Out system, queues are important data
structures for use in computer simulations
...
These simulations model the behavior of
systems, called queuing systems, in which queues of objects are waiting to be served by
various servers
...
We deal with a variety of queuing systems on a daily basis
...
Furthermore,
when you send a print request to a networked printer that is shared by many people, your
print request goes in a queue
...
Thus, the printer acts as the server when a queue of
documents is waiting to be printed
...
To describe
a queuing system, we use the term server for the object that provides the service
...
We will call the object receiving the service the customer, and the service time—
the time it takes to serve a customer—the transaction time
...
The customer at the front of the queue waits for the next available server
...


8

474 |

Chapter 8: Queues

When the first customer arrives, all servers are free and the customer moves to the first
server
...
To model a
queuing system, we need to know the number of servers, the expected arrival time of a
customer, the time between the arrivals of customers, and the number of events affecting
the system
...
The performance of the system depends
on how many servers are available, how long it takes to serve a customer, and how often a
customer arrives
...
This system can be modeled as a time-driven simulation
...
The simulation is
run for a fixed amount of time
...

For the simulation described in this section, we want to determine the average wait time
for a customer
...
When a customer arrives, he or she goes to the end of the queue and the
customer’s waiting time starts
...
On the other hand, if when
the customer arrives and either the queue is nonempty or all the servers are busy, the
customer must wait for the next available server and, therefore, this customer’s waiting
time starts
...
When a customer arrives, the timer is set to 0, which is incremented after each
clock unit
...
When a
server becomes free and the waiting customer’s queue is nonempty, the customer at the
front of the queue proceeds to begin the transaction
...
When the customer arrives at a server, the transaction time is set
to five and is decremented after each clock unit
...
Hence, the two objects needed to implement a time-driven
computer simulation of a queuing system are the customer and the server
...


Customer
Every customer has a customer number, arrival time, waiting time, transaction time, and
departure time
...
Let us call the class to implement the
customer object customerType
...
The basic operations that must be performed on an object of type
customerType are as follows: Set the customer’s number, arrival time, and waiting time;
increment the waiting time by one clock unit; return the waiting time; return the arrival time;
return the transaction time; and return the customer number
...
S
...

//************************************************************
class customerType
{
public:
customerType(int cN = 0, int arrvTime = 0, int wTime = 0,
int tTime = 0);
//Constructor to initialize the instance variables
//according to the parameters
//If no value is specified in the object declaration,
//the default values are assigned
...

//Instance variables are set according to the parameters
...

//Postcondition: The value of waitingTime is returned
...

//Postcondition: waitingTime = time;
void incrementWaitingTime();
//Function to increment the waiting time by one time unit
...

//Postcondition: The value of arrivalTime is returned
...

//Postcondition: The value of transactionTime is returned
...

//Postcondition: The value of customerNumber is returned
...


customerType
-customerNumber: int
-arrivalTime: int
-waitingTime: int
-transactionTime: int
+setCustomerInfo(int = 0, int = 0, int = 0, int = 0): void
+getWaitingTime() const: int
+setWaitingTime(int): void
+incrementWaitingTime(): void
+getArrivalTime() const: int
+getTransactionTime() const: int
+getCustomerNumber() const: int
+customerType(int = 0, int = 0, int = 0, int = 0)

FIGURE 8-11

UML diagram of the class customerType

The definitions of the member functions of the class customerType follow easily from
their descriptions
...

The function setCustomerInfo uses the values of the parameters to initialize
customerNumber, arrivalTime, waitingTime, and transactionTime
...
It uses the values of the parameters to initialize customerNumber,

Application of Queues: Simulation

|

477

arrivalTime, waitingTime, and transactionTime
...

customerType::customerType(int cN, int arrvTime,
int wTime, int tTime)
{
setCustomerInfo(cN, arrvTime, wTime, tTime);
}

The function getWaitingTime returns the current waiting time
...
Its definition is as follows:
void customerType::incrementWaitingTime()
{
waitingTime++;
}

The definitions of the functions setWaitingTime, getArrivalTime, getTransactionTime,
and getCustomerNumber are left as an exercise for you, (see Programming Exercise 8 a the
end of this chapter)
...
We use a string
variable to set the status of the server
...
Thus, three member variables are associated with a server: the
status, the transactionTime, and the currentCustomer
...
The following class, serverType, implements the server as an ADT:
//************************************************************
// Author: D
...
Malik
//
// class serverType
// This class specifies the members to implement a server
...

//Postcondition: currentCustomer is initialized by its
//
default constructor; status = "free"; and the
//
transaction time is initialized to 0
...

//Postcondition: Returns true if the server is free,
//
otherwise returns false
...

//Postcondition: status = "busy";
void setFree();
//Function to set the status of the server to "free
...

//Postcondition: transactionTime = t;
void setTransactionTime();
//Function to set the transaction time according to
//the transaction time of the current customer
...
transactionTime;
int getRemainingTransactionTime() const;
//Function to return the remaining transaction time
...

void decreaseTransactionTime();
//Function to decrease the transactionTime by 1 unit
...

//Postcondition: currentCustomer = cCustomer;
int getCurrentCustomerNumber() const;
//Function to return the customer number of the current
//customer
...


Application of Queues: Simulation

|

479

int getCurrentCustomerArrivalTime() const;
//Function to return the arrival time of the current
//customer
...

int getCurrentCustomerWaitingTime() const;
//Function to return the current waiting time of the
//current customer
...

int getCurrentCustomerTransactionTime() const;
//Function to return the transaction time of the
//current customer
...

private:
customerType currentCustomer;
string status;
int transactionTime;
};

Figure 8-12 shows the UML diagram of the class serverType
...
getTransactionTime();
}

transactionTime = time;

void serverType::decreaseTransactionTime()
{
transactionTime--;
}

We leave the definitions of the functions getRemainingTransactionTime,
setCurrentCustomer, getCurrentCustomerNumber, getCurrentCustomerArrivalTime,
getCurrentCustomerWaitingTime, and getCurrentCustomerTransactionTime as an
exercise for you, (see Programming Exercise 8 at the end of this chapter)
...
The next two sections describe
each of these classes
...
At any given time, a server is either free or busy
...
If all
the servers are busy, the customer must wait until one of the servers becomes free
...
Using dynamic arrays, depending
on the number of servers specified by the user, a list of servers is created during program
execution
...
The following class, serverListType,
implements the list of servers as an ADT:
//************************************************************
// Author: D
...
Malik
//
// class serverListType
// This class specifies the members to implement a list of
// servers
...

~serverListType();
//Destructor
//Postcondition: The list of servers is destroyed
...

//Postcondition: If a free server is found, returns its ID;
//
otherwise, returns -1
...

//Postcondition: The number of busy servers is returned
...


8

482 |

Chapter 8: Queues

//Postcondition: The server specified by serverID is set to
//
"busy", to serve the customer specified by cCustomer,
//
and the transaction time is set according to the
//
parameter tTime
...

//Postcondition: The server specified by serverID is set to
//
"busy", to serve the customer specified by cCustomer
...

//Postcondition: The transaction time of each busy server
//
is decremented by one unit
...
Moreover, if the actual parameter corresponding
//
to outFile is cout, a message indicating which customer
//
has been served is printed on the screen, together with the
//
customer's departing time
...

private:
int numOfServers;
serverType *servers;
};

Figure 8-13 shows the UML diagram of the class serverListType
...

The definitions of the constructor and destructor are straightforward
...
If a free server is found, it
returns the server’s ID; otherwise, the value -1 is returned, which indicates that all the
servers are busy
...
isFree())
{
serverID = i;
break;
}
}

return serverID;

The function getNumberOfBusyServers searches the list of servers and determines the
number of busy servers
...
The definition of this
function is as follows:
int serverListType::getNumberOfBusyServers() const
{
int busyServers = 0;
for (int i = 0; i < numOfServers; i++)
if (!servers[i]
...
This function is overloaded
...
One
function sets the server’s transaction time according to the parameter tTime; the other
function sets it by using the transaction time stored in the object cCustomer
...
The definitions
of these functions are as follows:
void serverListType::setServerBusy(int serverID,
customerType cCustomer, int tTime)
{
servers[serverID]
...
setTransactionTime(tTime);
servers[serverID]
...
getTransactionTime();

}

servers[serverID]
...
setTransactionTime(time);
servers[serverID]
...
Starting at the
first server, it searches the list of servers for busy servers
...
If the transactionTime reduces to zero, the
server is set to free
...
If the actual parameter
corresponding to outFile is cout, a message indicating which customer has been served
is printed on the screen, together with the customer’s departing time
...
The definition of this function is as follows:
void serverListType::updateServers(ostream& outF)
{
for (int i = 0; i < numOfServers; i++)
if (!servers[i]
...
decreaseTransactionTime();

}

}

if (servers[i]
...
getCurrentCustomerNumber()
<< "\n
departed at clock unit "
<< servers[i]
...
getCurrentCustomerWaitingTime()
+ servers[i]
...
setFree();
}

Waiting Customers Queue
When a customer arrives, he or she goes to the end of the queue
...
After
each time unit, the waiting time of each customer in the queue is incremented by 1
...
We will derive a class, waitingCustomerQueueType, from
the class queueType and add the additional operations to implement the customer
queue
...
S
...

//************************************************************
class waitingCustomerQueueType: public queueType
{
public:
waitingCustomerQueueType(int size = 100);
//Constructor
//Postcondition: The queue is initialized according to the
//
parameter size
...


};

void updateWaitingQueue();
//Function to increment the waiting time of each
//customer in the queue by one time unit
...
You can also derive it from the
class linkedQueueType, which implements the queue in a linked list
...


The definitions of the member functions are given next
...
The class waitingCustomerQueueType is derived
from the class queueType
...

The only way to access the elements of the queue is to use the deleteQueue operation
...

The addQueue operation inserts the element at the end of the queue
...
Given that each
deleteQueue operation is followed by an addQueue operation, how do we determine that
all the elements of the queue have been processed? We cannot use the isEmptyQueue or
isFullQueue operations on the queue because the queue will never be empty or full
...
Every element of the
original queue is removed, processed, and inserted into the temporary queue
...
We can
then copy the elements from the temporary queue back into the original queue
...

Also, if the queue is large, extra computer time is needed to copy the elements from the
temporary queue back into the original queue
...

In the second solution, before starting to update the elements of the queue, we can insert a
dummy customer with a waiting time of, say -1
...
If we do not process the customer
with the waiting time -1, this customer is removed from the queue and after processing all
the elements of the queue, the queue will contain no extra elements
...
We will use this solution to update the queue
...
setWaitingTime(-1);
int wTime = 0;
addQueue(cust);
while (wTime != -1)
{
cust = front();
deleteQueue();

}

}

wTime = cust
...
incrementWaitingTime();
addQueue(cust);

Main Program
To run the simulation, we first need to get the following information:


The number of time units the simulation should run
...

• The number of servers
...

• The approximate time between customer arrivals
...
By changing the values of
these parameters, we can observe the changes in the performance of the system
...
The definition of this function is as follows:
void setSimulationParameters(int& sTime, int& numOfServers,
int& transTime, int& tBetweenCArrival)
{
cout << "Enter the simulation time: ";
cin >> sTime;
cout << endl;
cout << "Enter the number of servers: ";
cin >> numOfServers;
cout << endl;
cout << "Enter the transaction time: ";
cin >> transTime;
cout << endl;

}

cout << "Enter the time between customers arrival: ";
cin >> tBetweenCArrival;
cout << endl;

When a server becomes free and the customer queue is nonempty, we can move the
customer at the front of the queue to the free server to be served
...
The waiting time of the customer
is added to the total waiting time
...
Remove the customer from the front of the queue
...
front();
customerQueue
...
Update the total waiting time by adding the current customer’s waiting
time to the previous total waiting time
...
getWaitingTime();

3
...

serverList
...
We use the Poisson distribution
from statistics, which says that the probability of y events occurring at a given time is
given by the formula:
P ðyÞ ¼

ly eÀl
; y ¼ 0; 1; 2;
...
Suppose that, on average, a
customer arrives every four minutes
...
Assuming an equal likelihood of each of the four minutes, the
expected value that a customer arrives in each of the four minutes is, therefore, 1 / 4 ¼ 0
...

Next, we need to determine whether the customer actually arrives at a given minute
...
One of the basic
assumptions of the Poisson distribution is that the probability of more than one outcome
occurring in a short time interval is negligible
...
Thus, we use e-l as the cutoff point to determine
whether a customer arrives at a given time unit
...
Then l ¼ 0
...
We can use an algorithm to generate a number between
0 and 1
...
25, we can assume that the customer
arrived at a particular time unit
...
If rNum> e-0
...

We now describe the function runSimulation to implement the simulation
...
The average transaction time is 5 minutes—that is, 5 time units
...
When the server becomes
free at time unit 97, the customer arriving at time unit 93 starts the transaction
...
Moreover, customers arriving at time units 96 and 100 are
in the queue
...
The general algorithm for this function is as follows:
1
...

2
...
1
...

2
...
If the customer’s queue is nonempty, increment the waiting
time of each customer by one time unit
...
3
...

2
...
If a server is free and the customer’s queue is nonempty,
remove a customer from the front of the queue and send
the customer to the free server
...
Print the appropriate results
...

Once you have designed the function runSimulation, the definition of the function
main is simple and straightforward because the function main calls only the function
runSimulation
...
)
When we tested our version of the simulation program, we generated the following
results
...

Sample Run:
Customer number 1 arrived at time unit 4
Customer number 2 arrived at time unit 8
From server number 1 customer number 1
departed at clock unit 9
Customer number 3 arrived at time unit 9
Customer number 4 arrived at time unit 12
From server number 1 customer number 2
departed at clock unit 14
From server number 1 customer number 3
departed at clock unit 19
Customer number 5 arrived at time unit 21
From server number 1 customer number 4
departed at clock unit 24
From server number 1 customer number 5
departed at clock unit 29
Customer number 6 arrived at time unit 37
Customer number 7 arrived at time unit 38
Customer number 8 arrived at time unit 41
From server number 1 customer number 6
departed at clock unit 42
Customer number 9 arrived at time unit 43
Customer number 10 arrived at time unit 44
From server number 1 customer number 7
departed at clock unit 47
Customer number 11 arrived at time unit 49
Customer number 12 arrived at time unit 51
From server number 1 customer number 8
departed at clock unit 52
Customer number 13 arrived at time unit 52
Customer number 14 arrived at time unit 53
Customer number 15 arrived at time unit 54
From server number 1 customer number 9
departed at clock unit 57
Customer number 16 arrived at time unit 59
From server number 1 customer number 10
departed at clock unit 62

8

490 |

Chapter 8: Queues

Customer number 17 arrived at time unit
From server number 1 customer number 11
departed at clock unit 67
Customer number 18 arrived at time unit
From server number 1 customer number 12
departed at clock unit 72
From server number 1 customer number 13
departed at clock unit 77
Customer number 19 arrived at time unit
From server number 1 customer number 14
departed at clock unit 82
From server number 1 customer number 15
departed at clock unit 87
Customer number 20 arrived at time unit
From server number 1 customer number 16
departed at clock unit 92
Customer number 21 arrived at time unit
From server number 1 customer number 17
departed at clock unit 97

66

71

78

90

92

The simulation ran for 100 time units
Number of servers: 1
Average transaction time: 5
Average arrival time difference between customers: 4
Total waiting time: 269
Number of customers that completed a transaction: 17
Number of customers left in the servers: 1
The number of customers left in queue: 3
Average waiting time: 12
...

2
...


4
...

6
...


A queue is a data structure in which the items are added at one end and
removed from the other end
...

The basic operations on a queue are as follows: Add an item to the queue,
remove an item from the queue, retrieve the first and last element of the
queue, initialize the queue, check whether the queue is empty, and check
whether the queue is full
...

The middle elements of a queue should not be accessed directly
...

Queues are restricted versions of arrays and linked lists
...


Consider the following statements:
queueType queue;
int x, y;

Show what is output by the following segment of code:
x = 4;
y = 5;
queue
...
addQueue(y);
x = queue
...
deleteQueue();
queue
...
addQueue(16);
queue
...
addQueue(y - 3);
cout << "Queue Elements: ";
while (!queue
...
front() << " ";
queue
...


Consider the following statements:
stackType stack;
queueType queue;
int x;

Suppose the input is:
15 28 14 22 64 35 19 32 7 11 13 30 -999

Show what is written by the following segment of code:
stack
...
addQueue(0);
cin >> x;
while (x != -999)
{
switch (x % 4)
{
case 0:
stack
...
isEmptyStack())
{
cout << "Stack Element = " << stack
...
pop();
}

8

492 |

Chapter 8: Queues

else
cout << "Sorry, the stack is empty
...
addQueue(x);
break;
case 3:
if (!queue
...
front()
<< endl;
queue
...
" << endl;
break;
} //end switch
cin >> x;
} //end while
cout << "Stack Elements: ";
while (!stack
...
top() << " ";
stack
...
isEmptyQueue())
{
cout << queue
...
deleteQueue();
}
cout << endl;
3
...
isEmptyQueue())
{
s
...
front());
q
...
isEmptyStack())
{
q
...
top());
s
...


493

What is the effect of the following statements? If a statement is invalid, explain
why it is invalid
...

a
...


queueType sales(-10);

c
...

5
...
addQueue(10);
queue
...
front() << endl;
queue
...
addQueue(2 * queue
...
addQueue(queue
...
addQueue(5);
queue
...
back() - 2);
linkedQueueType tempQueue;
tempQueue = queue;
while (!tempQueue
...
front() << " ";
tempQueue
...
front() << " " << queue
...


Suppose that queue is a queueType object and the size of the array implementing queue is 100
...

What are the values of queueFront and queueRear after adding an
element to queue?
b
...
Also, suppose that the value of queueFront
is 99 and the value of queueRear is 25
...


7
...
What are the values of queueFront and queueRear after removing an
element from queue?
Suppose that queue is a queueType object and the size of the array
implementing queue is 100
...

a
...


What are the values of queueFront and queueRear after adding an
element to queue?
b
...
Also, suppose that the value of queueFront
is 99 and the value of queueRear is 99
...


9
...
What are the values of queueFront and queueRear after removing an
element from queue?
Suppose that queue is implemented as an array with the special reserved
slot, as described in this chapter
...
If the value of queueFront is 50, what is the
position of the first queue element?
Suppose that queue is implemented as an array with the special reserved
slot, as described in this chapter
...
Also, suppose that the value of queueFront is 74
and the value of queueRear is 99
...


10
...


What are the values of queueFront and queueRear after adding an
element to queue?
b
...

Add the operation queueCount to the class queueType (the array implementation of queues), which returns the number of elements in the queue
...

Draw the UML diagram of the class queueADT
...

Draw the UML diagram of the class linkedQueueType
...


12
...


14
...

16
...


2
...


4
...
Also, write a program to
test these operations
...
Also, write a
program to test these operations
...
Write the definition of the class and the definitions of the function
members of this queue design
...

Write the definition of the function moveNthFront that takes as a parameter
a positive integer, n
...
The order of the remaining elements remains unchanged
...


After a call to the function moveNthFront,
queue = {34, 5, 11, 67, 43, 55}
...


6
...


Add this function to the class queueType
...

Write a program that reads a line of text, changes each uppercase letter to
lowercase, and places each letter both in a queue and onto a stack
...

The implementation of a queue in an array, as given in this chapter, uses the
variable count to determine whether the queue is empty or full
...

(See Exercise 13
...

Redefine the class linkedQueueType by addding the variable count to
keep track of the number of elements in the queue
...
Add the function
queueCount to return the number of elements in the queue
...

Write the definition of the class linkedQueueType, which is derived
from the class unorderedLinkedList, as explained in this chapter
...


8

496 |

8
...


Chapter 8: Queues

Write the definitions of the functions setWaitingTime, getArrivalTime,
getTransactionTime, and getCustomerNumber of the class
customerType defined in the section, ‘‘Application of Queues:
Simulation
...
Write the definitions of the functions getRemainingTransactionTime,
setCurrentCustomer, getCurrentCustomerNumber, getCurrentCustomerArrivalTime,
getCurrentCustomerWaitingTime, and getCurrentCustomerTransactionTime of the
class serverType defined in the section, ‘‘Application of Queues:
Simulation
...
Write the definition of the function runSimulation to complete the
design of the computer simulation program (see the section, ‘‘Application of Queues: Simulation’’)
...
Moreover, use a random number generator to decide whether a
customer arrived at a given time unit
...

a
...


Learn the various search algorithms


...


Discover how the sequential and binary search algorithms perform


...


Learn about hashing

498 |

Chapter 9: Searching and Hashing Algorithms

Chapter 3 described how to organize data into computer memory using an array and how
to perform basic operations on that data
...
The most important operation performed on a list is the search
algorithm
...

If the data is specially organized (for example, sorted), find the location in
the list where a new item can be inserted
...

The search algorithm’s performance, therefore, is crucial
...


Search Algorithms
Chapters 3 and 5 described how to implement the sequential search algorithm
...
The analysis of algorithms
enables programmers to decide which algorithm to use for a specific application
...

Associated with each item in a data set is a special member that uniquely identifies the
item in the data set
...
This unique
member of the item is called the key of the item
...
For instance,
when we search the data set for a particular item, we compare the key of the item for
which we are searching with the keys of the items in the data set
...
In the analysis of an algorithm, the key comparisons refer to comparing
the key of the search item with the key of an item in the list
...

In Chapter 3, we designed and implemented the class arrayListType to implement a
list and the basic operations in an array
...
The
sequential search works the same for both array-based and linked lists
...

Because we are interested in the performance of the sequential search (that is, the analysis
of this type of search), for easy reference and for the sake of completeness, we give the
sequential search algorithm for array-based lists (as described in Chapter 3)
...
If the search is
unsuccessful, -1 is returned
...

template
int arrayListType::seqSearch(const elemType& item) const
{
int loc;
bool found = false;
for (loc = 0; loc < length; loc++)
if (list[loc] == item)

9

500 |

Chapter 9: Searching and Hashing Algorithms

{
}

found = true;
break;

if (found)
return loc;
else
return -1;
} //end seqSearch
You can also write a recursive algorithm to implement the sequential search algorithm
...
)

SEQUENTIAL SEARCH ANALYSIS
This section analyzes the performance of the sequential search algorithm in both the
worst case and the average case
...
The statements in the for loop are the ones that are repeated several
times
...
Clearly, the loop
terminates as soon as the search item is found in the list
...
Also,
different programmers might implement the same algorithm differently, although the number of key comparisons would typically be the same
...

Therefore, when analyzing a search algorithm, we count the number of key comparisons
because this number gives us the most useful information
...

Suppose that L is list of length n
...

If the search item is not in the list, we then compare the search item with every element
in the list, making n comparisons
...

Suppose that the search item is in the list
...
If the search item is the first element of L,
we make only one key comparison
...
On the other hand, if the search
item is the last element in the list, the algorithm makes n comparisons
...
The best and worst cases are not likely to occur every time we apply the sequential
search on L, so it would be more helpful if we could determine the average behavior of
the algorithm
...


Search Algorithms |

501

To determine the average number of comparisons in the successful case of the sequential
search algorithm:
1
...

2
...

3
...

If the search item, called the target, is the first element in the list, one comparison is
required
...

Similarly, if the target is the kth element in the list, k comparisons are required
...
Suppose that there are n elements in the list
...
þ n
n
It is known that
1 þ 2 þ ÁÁÁ þ n ¼

nðn þ 1Þ
2

Therefore, the following expression gives the average number of comparisons made by
the sequential search in the successful case:
1 þ 2 þ
...
It, thus,
follows that if the list size is 1,000,000, on average, the sequential search makes 500,000
comparisons
...


Ordered Lists
A list is ordered if its elements are ordered according to some criteria
...
Several operations that can be performed on an ordered
list are similar to the operations performed on an arbitrary list
...

Therefore, to define an ordered list as an abstract data type (ADT), by using the
mechanism of inheritance, we can derive the class to implement the ordered lists from
the class arrayListType discussed in the previous section
...


9

502 |

Chapter 9: Searching and Hashing Algorithms

The following class, orderedArrayListType, defines an ordered list stored in an
array as an ADT:
template
class orderedArrayListType: public arrayListType
{
public:
orderedArrayListType(int size = 100);
//constructor

...

private:
//We will add the necessary members as needed
...

}

Binary Search
As you can see, the sequential search is not efficient for large lists because, on average, the
sequential search searches half the list
...
However, a binary search can be performed
only on ordered lists
...
In the next chapter,
we describe several sorting algorithms
...

First, the search item is compared with the middle element of the list
...
If the search item is less than the middle element of the list,
we restrict the search to the first half of the list; otherwise, we search the second half of
the list
...


list

FIGURE 9-1

[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10][11]
4
8
19 25 34 39 45 48 66 75 89 95

List of length 12

Search Algorithms |

503

Suppose that we want to determine whether 75 is in the list
...


[0]
list 4

[1]
8

[2]
19

[3]
25

[4]
34

search list
[5] [6] [7]
39
45
48

[8]
66

[9] [10] [11]
75
89
95

mid

FIGURE 9-2

Search list, list[0]
...

Because 75 6¼ list[5] and 75 > list[5], we restrict our search to the list
list[6]
...


[0]
list 4

FIGURE 9-3

[1]
8

[2]
19

[3]
25

[4]
34

[5]
39

[6]
45

[7]
48

search list
[8] [9] [10] [11]
66
75
89
95

Search list, list[6]
...
list[11], which is a list of
length = 6
...
To determine the middle element
of the list, we add the starting index, first, and the ending index, last, of the search list
and then divide by 2 to calculate its index
...

Initially, first = 0 and last = length – 1 (this is because an array index in C++ starts
at 0 and length denotes the number of elements in the list)
...
If the item is
found in the list, its location is returned; if the search item is not in the list, -1 is returned
...
The only exception is in the successful case; the last time through the loop only one
key comparison is made
...
You can also write
a recursive algorithm to implement the binary search algorithm
...
)

Example 9-1 further illustrates how the binary search algorithm works
...


list

FIGURE 9-4

[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11]
4
8
19
25
34
39
45
48
66
75
89
95

Sorted list for a binary search

The number of elements in this list is 12, so length = 12
...
Table 9-1 shows the values of first, last, and mid each time through the
loop
...


Search Algorithms |

505

Values of first, last, and mid and the number of comparisons for
search item 89

TABLE 9-1

Iteration

first

last

Mid

list[mid]

Number of comparisons

1

0

11

5

39

2

2

6

11

8

66

2

3

9

11

10

89

1(found is true)

The item is found at location 10, and the total number of comparisons is 5
...
Table 9-2 shows the values of first, last, and
mid each time through the loop
...

TABLE 9-2 Values of first, last, and mid and the number of comparisons for search

item 34
Iteration

first

last

mid

list[mid]

Number of comparisons

1

0

11

5

39

2

2

0

4

2

19

2

3

3

4

3

25

2

4

4

4

4

34

1 (found is true)

The item is found at location 4, and the total number of comparisons is 7
...

TABLE 9-3 Values of first, last, and mid and the number of comparisons for search

item 22
Iteration

first

last

mid

list[mid]

Number of comparisons

1

0

11

5

39

2

2

0

4

2

19

2

3

3

4

3

25

2

4

3

2

The loop stops (because first > last)

This is an unsuccessful search
...


9

506 |

Chapter 9: Searching and Hashing Algorithms

PERFORMANCE OF BINARY SEARCH
Suppose that L is a sorted list of size 1024 and we want to determine if an item x is in L
...
(For example, see Figures 9-2 and 9-3
...
Because
every iteration of the while loop makes two item (key) comparisons, that is, x is compared
twice with the elements of L, the binary search will make, at most, 22 comparisons to
determine whether x is in L
...


To better understand how fast binary search is compared with sequential search, suppose that
L is of size 1048576
...
Every
iteration of the while loop makes two key (that is, item) comparisons
...

Note that 40 ¼ 2 * 20 ¼ 2 * log2220 ¼ 2 * log2(1048576)
...
Moreover, suppose that n is a power of
2, that is, n ¼ 2m, for some nonnegative integer m
...
For example, after the first iteration, the search sublist is
of size about n /2 ¼ 2m-1
...
Also m ¼ log2n
...

Thus, the maximum number of comparisons to determine whether an element x is in L is
2(m + 1) ¼ 2(log2n + 1) ¼ 2log2n + 2
...
In the case of an unsuccessful search, it
can be shown that for a list of length n, a binary search makes approximately 2log2(n+1)
key comparisons
...


Insertion into an Ordered List
Suppose that you have an ordered list and want to insert an item in the list
...
Chapter 5 described how to insert an
item into an ordered linked list
...

To store the item in the ordered list, first we must find the place in the list where the item
is to be inserted
...
Because the list is sorted and
stored in an array, we can use an algorithm similar to the binary search algorithm to find
the place in the list where the item is to be inserted
...
(Note that we cannot use
the binary search algorithm as designed previously because it returns –1 if the item is not
in the list
...
) Therefore, the algorithm
to insert the item is: (The special cases, such as inserting an item in an empty list or in a
full list, are handled separately
...
Use an algorithm similar to the binary search algorithm to find the place
where the item is to be inserted
...
if the item is already in this list
output an appropriate message
else

use the function insertAt to insert the item in the list
...

template
void orderedArrayListType::insertOrd(const elemType& item)
{
int first = 0;
int last = length - 1;
int mid;
bool found = false;
if (length == 0)
//the list is empty
{
list[0] = item;
length++;
}
else if (length == maxSize)
cerr << "Cannot insert into a full list
...
"
<< "Duplicates are not allowed
...

If we add the binary search algorithm and the insertOrd algorithm to the class
orderedArrayListType, the definition of this class is as follows:
template
class orderedArrayListType: public arrayListType
{
public:
void insertOrd(const elemType&);
int binarySearch(const elemType& item) const;
orderedArrayListType(int size = 100);
};

Because the class orderedArrayListType is derived from the class arrayListType,
and the list elements of an orderedArrayListType are ordered, we must override the
functions insertAt and insertEnd of the class arrayListType in the class
orderedArrayListType
...
We leave the details of these functions as an exercise for you
...
We
leave the details of this function also as an exercise
...

TABLE 9-4

Number of comparisons for a list of length n

Algorithm

Successful search

Unsuccessful search

Sequential search

(n + 1) / 2 ¼ O (n)

n ¼ O (n)

Binary search

2log2n – 3 ¼ O (log2n)

2log2(n+1) ¼ O (log2n)

Lower Bound on Comparison-Based
Search Algorithms
Sequential and binary search algorithms search the list by comparing the target element
with the list elements
...
Earlier sections of this chapter showed that a sequential search is of

Hashing

|

509

the order n, and a binary search is of the order log2n, where n is the size of the list
...

Theorem: Let L be a list of size n > 1
...
If
SRH(n) denotes the minimum number of comparisons needed, in the worst case, by
using a comparison-based algorithm to recognize whether an element x is in L, then
SRH(n) ! log2(n + 1)
...

From these results, it follows that if we want to design a search algorithm that is of an
order less than log2n, it cannot be comparison based
...

In a binary search, the data must be sorted; in a sequential search, the data does not need
to be in any particular order
...
The obvious question is: Can we construct a search algorithm that is of order
less than log2n? Recall that both search algorithms, sequential and binary, are comparisonbased algorithms
...
Therefore, if we want to construct a search algorithm that is of order less than log2n, it cannot
be comparison based
...

The previous section showed that for comparison-based algorithms, a binary search
achieves the lower bound
...
The search algorithm that we now describe,
called hashing, also requires the data to be specially organized
...
To determine whether a particular item with a
key, say X, is in the table, we apply a function h, called the hash function, to the key X; that
is, we compute h(X), read as h of X
...
Suppose that the size of the hash table,
HT, is m
...
Thus, to determine whether the item with key X is in the
table, we look at the entry HT [h(X)] in the hash table
...
Before continuing with this discussion, let us consider the following questions:



How do we choose a hash function?
How do we organize the data with the help of the hash table?

9

510 |

Chapter 9: Searching and Hashing Algorithms

First, we discuss how to organize the data in the hash table
...
In the first
approach, the data is stored within the hash table, that is, in an array
...
Each approach has its own advantages and disadvantages, and we discuss both
approaches in detail
...

The hash table HT is, usually, divided into, say b buckets HT [0], HT [1],
...

Each bucket is capable of holding, say r items
...
Generally, r ¼ 1 and so each bucket can hold one item
...

EXAMPLE 9-2
Suppose there are six students a1, a2, a3, a4, a5, a6 in the Data Structures class and their IDs
are a1: 197354863; a2: 933185952; a3: 132489973; a4: 134152056; a5: 216500306; and a6:
106500306
...

Suppose that HT denotes the hash table and HT is of size 13 indexed 0, 1, 2,
...

Define the function h: {k1, k2, k3, k4, k5, k6} ! {0, 1, 2,
...

(Note that % denotes the mod operator
...
’’ Then
HT [4] ‹ 197354863

HT [5] ‹ 132489973

HT [9] ‹ 216500306

HT [10] ‹ 933185952

HT [12] ‹ 134152056

HT [3] ‹ 106500306

Hashing

|

511

We consider now a slight variation of Example 9-2
...

We want to store each student’s data into HT in this order
...

Suppose that HT denotes the hash table and HT is of size 13 indexed 0, 1, 2,
...

Define the function h: {k1, k2, k3, k4, k5, k6, k7, k8} ! {0, 1, 2,
...
Now
h (k1) ¼197354864 % 13 ¼ 5

h (k4) ¼ 134152056 % 13 ¼ 12 h (k7) ¼ 216510306 % 13 ¼ 12

h (k2) ¼ 933185952 % 13 ¼ 10 h (k5) ¼ 216500306 % 13 ¼ 9
h (k3) ¼ 132489973 % 13 ¼ 5

h (k8) ¼ 197354865 % 13 ¼ 6

h (k6) ¼ 106500306 % 13 ¼ 3

As before, suppose HT [b] ‹ a means ‘‘store the data of the student with ID a into
HT [b]
...

However, HT [5] is already occupied by the data of the student with ID 197354864
...
Later in this section, we discuss some
ways to handle collisions
...
Let X be
a key and h(X) ¼ t
...
Let X1 and X2 be
two nonidentical keys
...
If r ¼ 1, that is, the
bucket size is 1, an overflow and a collision occur at the same time
...

Minimize the number of collisions
...

Suppose that HTSize denotes the size of the hash table, that is, the size of the array
holding the hash table
...
Thus, each bucket can hold
one item and, therefore, overflow and collision occur simultaneously
...
Here we describe some of the
commonly used hash functions
...
Because the middle bits of a square usually depend on all the
characters, it is expected that different keys will yield different hash addresses with high
probability, even if some of the characters are the same
...
The parts are then added, in some convenient
way, to obtain the hash address
...
This integer is then divided by the size of the hash table to get the remainder, giving
the address of X in HT
...
The following C++ function uses the division method
to compute the address of the key
...
However,
in reality, collisions are unavoidable because usually a hash function always maps a larger
domain onto a smaller range
...
Collision resolution techniques are classified into two categories: open
addressing (also called closed hashing), and chaining (also called open hashing)
...
In chaining, the data is organized
in linked lists and the hash table is an array of pointers to the linked lists
...


Open Addressing
As described previously, in open addressing, the data is stored within the hash table
...
Open addressing can be implemented in several ways
...

LINEAR PROBING
Suppose that an item with key X is to be inserted in HT
...
Suppose that h(X) ¼ t
...
If HT[t] is empty, we store this item into this array slot
...
In linear probing, starting at
location t, we search the array sequentially to find the next available array slot
...
This can be easily
accomplished by using the mod operator
...
, (t + j) % HTSize
...

The next array slot is given by
(h(X) + j) % HTSize
where j is the jth probe
...
Then we know that
h (197354864) ¼ 5 ¼ h (132489973)

h (134152056) ¼ 12 ¼ h (216510306) h (106500306) ¼ 3

h (933185952) ¼ 10

h (216500306) ¼ 9

h (197354865) ¼ 6

Using the linear probing, the array position where each student’s data is stored is:
ID

h(ID)

(h(ID) + 1) % 13

197354864

5

933185952

10

132489973

5

134152056

12

216500306

9

106500306

3

216510306

12

0

197354865

6

7

6

(h(ID) + 2) % 13

9

514 |

Chapter 9: Searching and Hashing Algorithms

As before, suppose HT [b] ‹ a means ‘‘store the data of the student with ID a into
HT [b]
...
key == key)
found = true;
else
hIndex = (hIndex + 1) % HTSize;
if (found)
cerr << "Duplicate items are not allowed
...

However, linear probing causes clustering; that is, more and more new keys would
likely be hashed to the array slots that are already occupied
...


0

FIGURE 9-5

1

2

3

4

5

6

7

8

9

10 11 12 13 14 15 16 17 18 19

Hash table of size 20

Initially, all the array positions are available
...
Suppose that after storing some of
the items, the hash table is as shown in Figure 9-6
...
Slot 9 will be occupied next
if, for the next key, the hash address is 6, 7, 8, or 9
...
Similarly, in this hash table, the probability that array position
14 will be occupied next is 5/20
...


0

FIGURE 9-7

1

2

3

4

5

6

7

8

9

10 11 12 13 14 15 16 17 18 19

Hash table of size 20 with certain positions occupied

In this hash table, the probability that the array position 14 will be occupied next is 9/20,
whereas the probability that the array positions 15, 16, or 17 will be occupied next is 1/20
...
Linear probing,
therefore, causes clustering
...

One way to improve linear probing is to skip array positions by a fixed constant, say c,
rather than 1
...
Similarly, if c ¼ 2 and h(X) ¼ 2k + 1, that is, h(X) is odd, only the odd-numbered
array positions are visited
...

RANDOM PROBING
This method uses a random number generator to find the next available slot
...
All
insertions and searches use the same sequence of random numbers
...
Also suppose that r1 ¼ 2, r2 ¼ 5, and r3 ¼ 8
...
Similarly, the probe sequence of X2 has the elements
35, 37, 40, and 43
...
, hs
...

QUADRATIC PROBING
Suppose that an item with key X is hashed at t, that is, h(X) ¼ t and 0 t HTSize – 1
...
In quadratic probing, starting at position
t, we linearly search the array at locations (t + 1) % HTSize, (t + 22 ) % HTSize ¼ (t + 4) %
HTSize, (t + 32) % HTSize ¼ (t + 9) % HTSize,
...
That is, the probe
sequence is: t, (t + 1) % HTSize (t + 22 ) % HTSize, (t + 32) % HTSize,
...


EXAMPLE 9-6
Suppose that the size of the hash table is 101 and for the keys X1, X2, and X3, h(X1) ¼ 25,
h(X2) ¼ 96, and h(X3) ¼ 34
...
The probe sequence for X2 is 96, 97, 100, 4, 11, and so on
...
)
The probe sequence for X3 is 34, 35, 38, 43, 50, 59, and so on
...

Although quadratic probing reduces primary clustering, we do not know if it probes all
the positions in the table
...
However,
when HTSize is a prime, quadratic probing probes about half the table before repeating
the probe sequence
...

Suppose that HTSize is a prime and for 0

i
HTSize,

ðt þ i2 Þ%HTSize ¼ ðt þ j2 Þ%HTSize:
This implies that HTSize divides (j2 – i2), that is, HTSize divides (j – i) (j + i)
...

Now because 0 < j À i < HTSize, it follows that HTSize does not divide (j À i)
...
This implies that j + i ! HTSize, so j ! (HTSize / 2)
...

Thus, it follows that if the size of HTSize is a prime at least twice the number of items, we
can resolve all the collisions
...

(This can occur when the table is actually half full; in practice, it seldom happens unless
the table is nearly full
...


Hashing

|

517

Note that
22
32
42
i2

¼ 1 þ ð2 Á 2 À 1Þ
¼ 1 þ 3 þ ð2 Á 3 À 1Þ
¼ 1 þ 3 þ 5 þ ð2 Á 4 À 1Þ

...


...
þ ð2 Á i À 1Þ;

i ! 1:

Thus, it follows that
ðt þ i2 Þ % HTSize ¼ ðt þ 1 þ 3 þ 5 þ 7 þ
...
, (t þ i2) % HTSize
...
" << endl;

9

518 |

Chapter 9: Searching and Hashing Algorithms

else
cerr << "Error: The table is full
...
" << endl;

Both random and quadratic probings eliminate primary clustering
...
The same probe sequence is used for both keys
because random probing and quadratic probing are functions of the home positions, not
the original key
...
This is called secondary clustering
...
This is called double hashing
...

If the size of the hash table is a prime p, then we can define g as follows:
g(k) ¼ 1+(k % (p – 2))

EXAMPLE 9-7
Suppose that the size of the hash table is 101 and for the keys X1 and X2, h(X1) ¼ 35 and
h(X2) ¼ 83
...
Then the probe sequence for X1
is 35, 38, 41, 44, 47, and so on
...

(Notice that (83 + 3 * 6) % 101 ¼ 101 % 101 ¼ 0
...
We want to store each student’s data in this order
...
, 18
...

Then p – 2 ¼ 17
...
Now h(115) ¼ 115 % 19 ¼ 1
...

Next consider k ¼ 153
...
However, HT [1] is already
occupied
...
Now g(153) ¼
1 + (153 % 17) ¼ 1 + 0 ¼ 1
...
Therefore, probe sequence
of 153 is given by (h(153) + i Á g(153)) % 19 ¼ (1 + i Á 1) % 19, i ¼ 0, 1, 2, 3,
...
Because HT[2] is empty, the data of the student
with ID 153 is stored in HT[2]
...
Now h(586) ¼ 586 % 19 ¼ 16
...

Consider k ¼ 206
...
Because HT [16] is already occupied,
we compute g(206)
...
So the probe sequence
of 206 is, 16, 0, 3, 6,
...
Because HT [0] is empty, the data
of the student with ID 206 is stored in HT [0]
...
If a
collision occurs for an ID, then the following table shows the probe sequence of that ID
...


586

16

206

16

3

16, 0, 3, 6, 9,

Note that (16 + 3) % 19 ¼ 0

985

16

17

16, 14, 12, 10,
...


As before, suppose HT [b] ‹ a means ‘‘store the data of the student with ID a into
HT [b]
...
Clearly, we first
must find the index of R in HT
...
Let us further assume that after inserting R,
another item, R0 , was inserted in HT, and the home position of R and R0 is the same
...
Suppose that we delete R simply by marking the array slot
containing R as empty
...
This
gives the impression that R0 is not in the table, which, of course, is incorrect
...

One way to solve this problem is to create a special key to be stored in the key of the
items to be deleted
...
However, during the search, the search should not terminate at
this location
...

Another solution is to use another array, say indexStatusList of int, of the same size
as the hash table as follows: Initialize each position of indexStatusList to 0, indicating
that the corresponding position in the hash table is empty
...
When an item is deleted
from the hash table at position, say k, we set indexStatusList[k] to -1
...

For example, suppose that you have the hash table as shown in Figure 9-8
...
Suppose that the
entries at positions 3 and 6 are removed
...


indexStatusList
[0] 1
[0]
[1] 1
[1]
[2] 0
[2]
[3] -1
[3]
[4]
[4] 0
[5]
[5] 1
[6]
[6] -1
[7]
[7] 0
[8]
[8] 1
[9]
[9] 0

FIGURE 9-9

HashTable
Mike
Gina
Goldy
Ravi
Danny
Sheila

Hash table and indexStatusList after removing the entries at positions 3 and 6

Hashing

|

521

Hashing: Implementation Using Quadratic Probing
This section briefly describes how to design a class, as an ADT, to implement hashing
using quadratic probing
...
One is used to store
the data, and the other, indexStatusList as described in the previous section, is used to
indicate whether a position in the hash table is free, occupied, or used previously
...
S
...
It uses quadratic probing to resolve collisions
...
The first
//parameter specifies the initial hash index of the item to
//be inserted
...

//Postcondition: If an empty position is found in the hash
//
table, rec is inserted and the length is incremented by
//
one; otherwise, an appropriate error message is
//
displayed
...
The parameter hashIndex
//specifies the initial hash index of rec
...

bool isItemAtEqual(int hashIndex, const elemType& rec) const;
//Function to determine whether the item specified by the
//parameter rec is the same as the item in the hash table
//at position hashIndex
...

void retrieve(int hashIndex, elemType& rec) const;
//Function to retrieve the item at position hashIndex
...

void remove(int hashIndex, const elemType& rec);
//Function to remove an item from the hash table
...


void print() const;
//Function to output the data
...

~hashT();
//destructor
//Postcondition: Array HTable and indexStatusList are deleted
...

The definition of the function insert using quadratic probing is as follows:
template
void hashT::insert(int hashIndex, const elemType& rec)
{
int pCount;
int inc;
pCount = 0;
inc = 1;
while (indexStatusList[hashIndex] == 1
&& HTable[hashIndex] != rec && pCount < HTSize / 2)
{
pCount++;
hashIndex = (hashIndex + inc) % HTSize;
inc = inc + 2;
}
if (indexStatusList[hashIndex] != 1)
{
HTable[hashIndex] = rec;
indexStatusList[hashIndex] = 1;
length++;
}

Hashing

}

|

523

else if(HTable[hashIndex] == rec)
cerr << "Error: No duplicates are allowed
...
"
<< "Unable to resolve the collision
...
Therefore, for
each j, where 0 j HTSize – 1, HT [j] is a pointer to a linked list
...



...


...
The item
with this key is then inserted in the linked list (which might be empty) pointed to by
HT [t]
...
(A new item can be inserted at the beginning of the linked list because the
data in a linked list is in no particular order
...
As
usual, first we calculate h(X)
...
Then the linked list pointed to by HT [t]
is searched sequentially
...
We then adjust the pointers at the appropriate locations and
deallocate the memory occupied by R
...
Furthermore, the size of the hash table no
longer needs to be greater than the number of items
...
However,
with a good hash function, the average length of a linked list is still small and so the search
is efficient
...
If the hash function is efficient, few keys are hashed to the
same home position
...
If the item size is large, it saves a considerable amount of space
...

Further suppose that each pointer requires one word of storage
...
A total of 12,000 words of storage space, therefore, is required to implement
chaining
...

DISADVANTAGES OF CHAINING
If the item size is small, a considerable amount of space is wasted
...
Chaining then requires a total of
3000 words of storage
...
Also, if the
table size is three times the number of items, then in quadratic probing the keys are
reasonably spread out
...


Hashing Analysis
Let
¼

Number of records in the table
HTSize

The parameter a is called the load factor
...


Quick Review |

TABLE 9-5

525

Number of comparisons in hashing
Successful search

Unsuccessful search

&
'
1
1

2


(
)
1
1

2
ð1 À Þ2

Quadratic probing

À log2 ð1 À Þ


1


Chaining



Linear probing


2



QUICK REVIEW
1
...

3
...


5
...

7
...

9
...

11
...

13
...


A list is a set of elements of the same type
...

A one-dimensional array is a convenient place to store and process lists
...
It continues to compare the search item
with the elements in the list until either the item is found or no more
elements are left in the list with which it can be compared
...

For a list of length n, in a successful search, on average, the sequential search
makes (n + 1) / 2 ¼ O(n) comparisons
...

A binary search is much faster than a sequential search
...

For a list of length n, in a successful search, on average, the binary search
makes 2 log2n – 3 ¼ O(log2n) key comparisons
...
Suppose that the elements of L are sorted
...

The binary search algorithm is the optimal worst-case algorithm for solving
search problems by using the comparison method
...

In hashing, the data is organized with the help of a table, called the hash
table, denoted by HT
...


9

526 |

15
...

17
...

19
...

21
...

23
...


25
...

27
...


29
...
The function h is an arithmetic function,
and h(X) gives the address of the item in the hash table
...

Two keys X1 and X2, such that X1 6¼ X2, are called synonyms if h(X1) ¼ h(X2)
...
If bucket t is full, we say that an overflow has
occurred
...
If h(X1) ¼ h(X2), we say that a
collision has occurred
...

Collision resolution techniques are classified into two categories: open addressing (also called closed hashing) and chaining (also called open hashing)
...

In chaining, the data is organized in linked lists, and the hash table is an
array of pointers to the linked lists
...

In linear probing, we assume that the array is circular so that if the lower
portion of the array is full, we can continue the search in the top portion of the
array
...
, (t + j) % HTSize
...

Linear probing causes clustering, called primary clustering
...

In rehashing, if a collision occurs with the hash function h, we use a series
of hash functions
...
, (t + i2) %
HTSize
...
, (t + i2) % HTSize
...
However,
if two nonidentical keys, say X1 and X2, are hashed to the same home
position, that is, h(X1) ¼ h(X2), the same probe sequence is followed for
both keys
...
If the hash function
causes a cluster at a particular home position, the cluster remains under these
probings
...


Exercises

30
...

32
...

34
...

36
...

38
...
This is called double hashing
...

In open addressing, when an item is deleted, its position in the array cannot
be marked as empty
...
The item with this key is then inserted in the linked list
(which might be empty) pointed to by HT [t]
...

In chaining, to delete an item, say R, from the hash table, first we search the
hash table to find where in the linked list R exists
...

Let a ¼ (Number of records in the table / HTSize)
...

In linear probing, the average number of comparisons in a successful search is
(1/2){1 + (1 / (1 – a))} and in an unsuccessful search is (1/2){1 + (1 / (1 – a)2)}
...

In chaining, the average number of comparisons in a successful search is
(1 + a / 2) and in an unsuccessful search is a
...


Mark the following statements as true or false
...

b
...

c
...

d
...

Consider the following list: 63, 45, 32, 98, 46, 57, 28, 100
a
...


Using the sequential search as described in this chapter, how many comparisons are required to find whether the following items are in the list? (Recall
that by comparisons we mean item comparisons, not index comparisons
...

b
...

d
...


4
...

Consider the following list: 2, 10, 17, 45, 49, 55, 68, 85, 92, 98, 110
Using the binary search as described in this chapter, how many comparisons
are required to find whether the following items are in the list? Show the
values of first, last, and mid and the number of comparisons after each
iteration of the loop
...

49
c
...

99
Suppose that the size of the hash table is 150 and the bucket size is 5
...

Explain how collision is resolved using quadratic probing
...
Also, suppose that a new item is to be
inserted in the table and its hash address is 30
...

Suppose that the size of the hash table is 101
...
Using modular arithmetic, find the
indices in the hash table if:
a
...


6
...

8
...


10
...

b
...

Suppose that 50 keys are to be inserted into an initially empty hash table
using quadratic probing
...
Suppose
hash table, HT, is of the size 13, indexed 0,1,2,
...
Show how these
students’ IDs, in the order given, are inserted in HT using the hashing function
h(k) ¼ k % 13, where k is a student ID
...
Suppose hash table, HT, is of the size 15, indexed 0,
1, 2,
...
Show how these IDs are inserted in HT using the hashing
function h(k) ¼ k % 13, where k is an ID
...


11
...


13
...


15
...


17
...


19
...


21
...


23
...
Suppose hash
table, HT, is of the size 19, indexed 0, 1, 2,
...
Show how these students’
IDs, in the order given, are inserted in HT using the hashing function h(k) ¼ k %
19
...

Suppose there are six workers, in a workshop, with IDs 147, 169, 580, 216,
974, and 124
...
, 12
...
Use linear probing to resolve collision
...
Suppose hash table, HT, is of the size 7, indexed 0, 1, 2,
...
Show
how these workers’ IDs, in the order given, are inserted in HT using the
hashing function h(k) ¼ k % 7
...

Suppose there are seven students with IDs 5701, 9302, 4210, 9015, 1553,
9902, and 2104
...
,
18
...
Use double hashing to resolve
collision, where the second hash function is given by g(k) ¼ (k+1) % 17
...
Why wouldn’t you mark the
position of the item to be deleted as empty?
What are the advantages of open hashing?
Give a numerical example to show that collision resolution by quadratic
probing is better than chaining
...

Suppose that the size of the hash table is 1001 and the table has 850 items
...

On average, how many comparisons are made to determine whether an
item is in the list if:
Linear probing is used
...
Quadratic probing is used
...
Chaining is used
...
If, on average, three
key comparisons are needed to determine whether an item is in the table,
what should be the size of the hash table if:
a
...


a
...

c
...

Quadratic probing is used
...


|

529

9

530 |

Chapter 9: Searching and Hashing Algorithms

PROGRAMMING EXERCISES
1
...


3
...


(Recursive sequential search) The sequential search algorithm given in
Chapter 3 is nonrecursive
...

(Recursive binary search) The binary search algorithm given in this chapter
is nonrecursive
...
Also, write a version of the sequential search algorithm
that can be applied to sorted lists
...
Moreover, write a test program
to test your algorithm
...
Therefore, it usually works the same for both sorted and unsorted
lists
...
For example, if the search
item is not in the list, you can stop the search as soon as you find an element in
the list that is larger than the search item
...
Add this
function to the class orderedArrayListType and write a program to test it
...

a
...


c
...

Use any sorting algorithm to sort list
...

Search the list for some items, as follows:

Use the binary search algorithm to search the list
...
)
ii
...
(Use the sequential search algorithm for a sorted list
...
Print the number of comparisons for Steps c
...
ii
...

Write a program to test the function insertOrd that inserts an item into an
array-based ordered list
...
The item to be removed is passed as a parameter to this function
...
Add this function to the class
orderedArrayListType and write a program to test it
...


5
...


Programming Exercises

7
...


|

531

Write the definitions of the functions search, isItemAtEqual, retrieve,
remove, and print, the constructor, and the destructor for the class
hashT, as described in the section, ‘‘Hashing: Implementation Using Quadratic Probing,’’ of this chapter
...

a
...
Design the class stateData to keep track of the information for
a state
...
Also, overload the relational operators to compare two states by their
name
...

b
...
Use the state’s name as the key to determine the hash
address
...

Test your program by searching for and removing certain states from the
hash table
...
length();
for (int k = 0; k < 15 - len; k++)
name = name + ' '; //increase the length of the name
//to 15 characters
for (int k = 0; k < 5; k++)
{
sum = sum + static_cast(name[i]) * 128 * 128
+ static_cast(name[i + 1]) * 128
+ static_cast(name[i + 2]);
i = i + 3;
}
}

return sum % HTSize;

9

This page intentionally left blank

10
CHAPTER

S ORTING A LGORITHMS
I N T H I S C H A P T E R , YO U W I L L :


...


Explore how to implement selection sort, insertion sort, Shellsort, quicksort, mergesort,
and heapsort


...


Learn how priority queues are implemented

534 |

Chapter 10: Sorting Algorithms

Chapter 9 discussed the search algorithms on lists
...
By contrast, a binary search is very fast for array-based lists, but
it requires the data to be in order
...


Sorting Algorithms
There are several sorting algorithms in the literature
...
To compare the performance of these algorithms,
we also provide the analysis of these algorithms
...
We will specify whether the algorithm being developed
is for array-based lists or linked lists
...
(For example, for an array-based list, these are the members of the
class arrayListType
...

Suppose that the sorting algorithm selection sort (described in the next section) is to be
applied to array-based lists
...

};

Selection Sort: Array-Based Lists
In selection sort, a list is sorted by selecting elements in the list, one at a time, and moving
them to their proper positions
...
The first time we locate the smallest item in the entire list, the
second time we locate the smallest item in the list starting from the second element in the
list, and so on
...


Selection Sort: Array-Based Lists

|

535

Suppose you have the list shown in Figure 10-1
...

list
[0] 16
[1] 30
[2] 24
unsorted [3] 7
list
[4] 62
[5] 45
[6] 5
[7] 55
smallest
(a)

FIGURE 10-2

16
5
30
30
24
24
7 swap 7 unsorted
62
62
list
45
45
5
16
55
55
(b)
(c)

Elements of list during the first iteration

Initially, the entire list is unsorted
...
The smallest
item is at position 6, as shown in Figure 10-2(a)
...
So we swap 16 (that is, list[0]) with 5 (that is, list[6]), as
shown in Figure 10-2(b)
...

Figure 10-3 shows the elements of list in the second iteration
...
list[7]
...
The smallest element is at position 3, as shown in Figure 10-3(a)
...
So we swap 7 (that is, list[3]) with 30 (that is, list[1]), as shown in
Figure 10-3(b)
...

Now the unsorted list is list[2]
...
So we repeat the preceding process of
finding the (position of the) smallest element in the unsorted portion of the list and
moving it to the beginning of the unsorted portion of the list
...

In the unsorted portion of the list:
1
...

2
...

Initially, the entire list, list[0]
...
After executing
Steps 1 and 2 once, the unsorted list is list[1]
...
After executing
Steps 1 and 2 a second time, the unsorted list is list[2]
...

We can keep track of the unsorted portion of the list and repeat Steps a and b with the help
of a for loop as follows:
for (index = 0; index < length - 1; index++)
{
1
...
list[length - 1]
...
Swap the smallest element with list[index]
...

}

The

first

time

through

the

loop,

we

locate

the

smallest

element

in

list[0]
...
The

second

time

through

the

loop,

we

locate

the

smallest

element

in

list[1]
...
This process continues until the length of the unsorted list is 1
...
) It, therefore, follows that to implement selection sort, we need to

implement Steps 1 and 2
...
list[last]:
template
int arrayListType::minLocation(int first, int last)
{
int minIndex;
minIndex = first;

Selection Sort: Array-Based Lists

|

537

for (int loc = first + 1; loc <= last; loc++)
if( list[loc] < list[minIndex])
minIndex = loc;
return minIndex;
} //end minLocation

Given the locations in the list of the elements to be swapped, the following C++
function, swap, swaps those elements:
template
void arrayListType::swap(int first, int second)
{
elemType temp;
temp = list[first];
list[first] = list[second];
list[second] = temp;
}//end swap

We can now complete the definition of the function selectionSort:
template
void arrayListType::selectionSort()
{
int minIndex;

}

for (int loc = 0; loc < length - 1; loc++)
{
minIndex = minLocation(loc, length - 1);
swap(loc, minIndex);
}

You can add the functions to implement selection sort in the definition of the
class arrayListType as follows:
template
class arrayListType
{
public:
//Place the definitions of the function given earlier here
...

private:
//Place the definitions of the members given earlier here
...
S
...

//**************************************************************
#include
#include "arrayListType
...
insert(num);
cin >> num;
}

//Line
//Line
//Line
//Line
//Line

10
11
12
13
14

cout << "Line 15: The list before sorting:" << endl; //Line 15
list
...
selectionSort();
cout << "Line 19: The list after sorting:" << endl;
list
...

Line 8: Enter numbers ending with -999
34 67 23 12 78 56 36 79 5 32 66 -999
Line 15: The list before sorting:
34 67 23 12 78 56 36 79 5 32 66

Selection Sort: Array-Based Lists

|

539

Line 19: The list after sorting:
5 12 23 32 34 36 56 66 67 78 79

For the most part, the preceding output is self-explanatory
...
Similarly, the statements in Lines 16 and 20 call the function print of the class arrayListType
...

1
...
You can
easily implement this form of selection sort by altering the if statement in the
function minLocation, and passing the appropriate parameters to the corresponding function and the function swap, when these functions are called in
the function selectionSort
...


Selection sort can also be applied to linked lists
...
See Programming Exercise
1 at the end of this chapter
...
A sorting algorithm makes key comparisons and also moves
the data
...
Let us look at the performance of
selection sort
...
The function swap does three item assignments
and is executed n À 1 times
...

The key comparisons are made by the function minLocation
...
Also, the function minLocation
is executed n À 1 times (by the function selectionSort)
...
The second time, the function minLocation finds the index of the smallest
element in the sublist of length n À 1 and so makes n À 2 comparisons, and so on
...


1
0

540 |

Chapter 10: Sorting Algorithms

Insertion Sort: Array-Based Lists
The previous section described and analyzed the selection sort algorithm
...
This section describes the sorting algorithm called insertion sort, which tries to
improve—that is, reduce—the number of key comparisons
...
Consider the list
given in Figure 10-4
...
In this list, the elements list[0], list[1], list[2],
and list[3] are in order
...
list[3] is sorted, as shown in Figure 10-5(a)
...
Because
list[4] < list[3], we need to move the element list[4] to its proper location
...

To move list[4] into list[2], first we copy list[4] into temp, a temporary
memory space—see Figure 10-5(c)
...
After copying list[3] into list[4] and list[2] into list[3], the
list is as shown in Figure 10-5(e)
...
Figure 10-5(f)
shows the resulting list
...
list[4] is sorted and list[5]
...
We repeat
this process on the resulting list by moving the first element of the unsorted list into the
sorted list in the proper place
...
Elements in the upper sublist are
sorted; elements in the lower sublist are to be moved to the upper sublist in their
proper places one at a time
...
Initially, firstOutOfOrder is
initialized to 1
...
move list[location - 1] one array slot down
b
...
We initialize firstOutOfOrder to 1
(see Figure 10-6)
...

temp = list[firstOutOfOrder] = 7
location = firstOutOfOrder = 1

Next, we execute the do
...


1
0

542 |

Chapter 10: Sorting Algorithms

list[1] = list[0] = 13
location = 0

(copy list[0] into list[1])
(decrement location)

The do
...
We copy temp into
list[location]—that is, into list[0]
...


list

[0]
7

[1]
13

[2]
15

[3]
8

[4]
12

[5]
30

[6]
3

[7]
20

firstOutOfOrder
temp 7
firstOutOfOrder 1

FIGURE 10-7

list after the first iteration of insertion sort

Now suppose that we have the list given in Figure 10-8(a)
...
list[3], or the elements list[0], list[1], list[2], and
list[3], are in order
...
Because list[4] < list[3],
the element list[4], which is 12, needs to be moved to its proper location
...
Then we copy
list[2] into list[3] and again decrement location by 1
...
At this point, the list is as shown in Figure 10-8(b)
...
while loop terminates
...
That is, list[2] = temp = 12
...

Suppose that we have the list given in Figure 10-9
...
list[4], or the elements list[0], list[1], list[2], list[3], and
list[4], are in order
...
Because list[5] > list[4], the if
statement evaluates to false
...
Note that this is the case when the
firstOutOfOrder element is already at the proper place
...

We can repeat this process for the remaining elements of list to sort list
...
Therefore, this section describes insertion
sort for linked lists
...


first
10

FIGURE 10-10

7

25

8

12

20

Linked list

In Figure 10-10, first is a pointer to the first node of the linked list
...
However, if the list is stored in a linked list, we can traverse the list in only
one direction starting at the first node because the links are only in one direction, as
shown in Figure 10-10
...
Suppose that firstOutOfOrder is a
pointer to the node that is to be moved to its proper location, and lastInOrder is a
pointer to the last node of the sorted portion of the list
...
(We assume that the nodes are in the usual info-link form as
described in Chapter 5
...
If the
info of firstOutOfOrder is smaller than the info of first, then the node
firstOutOfOrder is to be moved before the first node of the list; otherwise, we search
the list starting at the second node to find the location where to move
firstOutOfOrder
...
The pointer trailCurrent points to the node just before
current
...
Of course, we also handle any special cases such as an
empty list, a list with only one node, or a list in which the node firstOutOfOrder is
already in the proper place
...
We consider several
cases
...
So we adjust the necessary links, and
Figure 10-13 shows the resulting list
...


first
8

10

20

lastInOrder

FIGURE 10-14

30

15

18

firstOutOfOrder

Linked list and pointers lastInOrder and firstOutOfOrder

Because firstOutOfOrder->info is greater than first->info, we search the list to
find the place where firstOutOfOrder is to be moved
...
For this list, these pointers
end up at the nodes as shown in Figure 10-15
...
So no adjustment of the links is necessary
...


first
8

10

20

30

lastInOrder

FIGURE 10-16

15

18

firstOutOfOrder

Linked list and pointers lastInOrder and firstOutOfOrder

Because firstOutOfOrder->info is greater than first->info, we search the list to
find the place where firstOutOfOrder is to be moved
...
For this list, these pointers end
up at the nodes as shown in Figure 10-17
...
So we
adjust the necessary links and obtain the list as shown in Figure 10-18
...
" << endl;
else if (first->link == NULL)
cout << "The list is of length 1
...
" << endl;
else
while (lastInOrder->link != NULL)
{
firstOutOfOrder = lastInOrder->link;
if (firstOutOfOrder->info < first->info)
{
lastInOrder->link = firstOutOfOrder->link;
firstOutOfOrder->link = first;
first = firstOutOfOrder;
}

1
0

548 |

Chapter 10: Sorting Algorithms

else
{
trailCurrent = first;
current = first->link;
while (current->info < firstOutOfOrder->info)
{
trailCurrent = current;
current = current->link;
}
if (current != firstOutOfOrder)
{
lastInOrder->link = firstOutOfOrder->link;
firstOutOfOrder->link = current;
trailCurrent->link = firstOutOfOrder;
}
else
lastInOrder = lastInOrder->link;

}
} //end while
} //end linkedInsertionSort

We leave it as exercise for you to write a program to test insertion sort
...


Analysis: Insertion Sort
Suppose that the list is of length n
...
This is the best case
...
) Now suppose that the list is sorted, but in the reverse order
...
This is the worst case
...
)
Table 10-1 summarizes the average-case behavior of selection and insertion sort
...

TABLE 10-1

Average-case behavior of the selection sort and insertion sort for a list of

length n
Algorithm

Number of comparisons

Number of swaps/item
assignments

Selection sort

(1/2)n (n – 1) = O(n2)

3(n – 1) ¼ O(n)

Insertion sort

(1/4)n2 + O(n) ¼ O(n2)

(1/4)n2 + O(n) ¼ O(n2)

Shellsort

|

549

Shellsort
In the previous sections, we described selection sort and insertion sort
...

Selection sort makes more comparisons because it makes many redundant comparisons
...
In fact, the number of item movements in insertion sort is considerably
more than selection sort because it moves items one position at a time, so to move an
item to its final position, it might require many movements
...
The
modified insertion sort that we present next was introduced in 1959 by D
...
Shell and is
known as the Shellsort algorithm
...

In Shellsort, the elements of the list are viewed as sublists at a particular distance
...
For
example, suppose that you have a list of 15 elements, as shown in Figure 10-19
...
Note that several
elements have moved closer to their final position
...
In the next iteration, we sort the elements at a distance of 4, as shown in
Figure 10-19(b)
...
Figure 10-19(c) shows the elements before and after the final sorting phase
...
The sequence 1,
4, 7 is called the increment sequence
...
The literature provides a discussion
of various increment sequences, and some have been found to be useful
...
For example, if the number of increments is
about one-half of the previous increment, then we need at most 20 increments to sort a list
of 1 million elements
...

D
...
Knuth recommended the increment sequence 1, 4, 13, 40, 121, 364, 1093, 3280,
...
In fact, the ith increment ¼
3 • (i – 1)th increment + 1
...
However, for large lists, it is difficult to get a better performance
by more than 20% than the increment sequence recommended by Knuth
...
For example, the increment
sequence, 1, 2, 4, 8, 16, 32, 64, 128, 256,
...
We will use the increment sequence suggested by Knuth in the Shellsort
algorithm we implement
...
In
the intervalInsertionSort, the sublist starts at the variable begin, and the increment
between successive elements is given by the variable inc instead of 1
...

The analysis of the Shellsort is difficult to obtain
...
Empirical studies suggest that for large
lists of size n, the number of moves is in the range of n1
...
6n1
...


Lower Bound on Comparison-Based Sort Algorithms |

551

Lower Bound on Comparison-Based
Sort Algorithms
The previous sections discussed selection sort and insertion sort, and noted that the averagecase behavior of these algorithms is O(n2)
...
Before discussing
any additional sorting algorithms, let us discuss the best-case scenario for the comparisonbased sorting algorithms
...
Let L be a list of n distinct elements, where n > 0
...
Because each comparison of the keys
has two outcomes, the comparison tree is a binary tree
...
The node is labeled as j:k, representing the
comparison of L[j] with L[k]
...
Figure 10-20 shows the comparison tree for a list of length 3
...
)

1:2
L[1]
L[1]>L[2]

2:3
L[2]
L[2]
L[2]>L[3]

1,2,3

1:3
L[1]1,3,2

FIGURE 10-20

2:3
L[2]>L[3]
3,2,1

1:3
L[1]>L[3]
3,1,2

L[1]2,1,3

L[1]>L[3]
2,3,1

Comparison tree for sorting three items

We call the top node in the figure the root node
...
A sequence of branches from a node, x, to another node, y, is
called a path from x to y
...
This uniqueness follows because the sort algorithm only moves the data and makes
comparisons
...
For a list of n elements, n > 0, there are n! different
permutations
...
Thus,
the comparison tree must have at least n! leaves
...
We state the
following result without proof
...
Any sorting algorithm that sorts L by
comparison of the keys only, in its worst case, makes at least O(nlog2n) key comparisons
...
The remainder of this chapter discusses sorting algorithms that, on average, are of
the order O(nlog2n)
...
The sorting algorithms selection sort and insertion sort, discussed earlier in
this chapter, are O(n2)
...
The first algorithm is quicksort
...
The list is partitioned into
two sublists, and the two sublists are then sorted and combined into one list in such a way
so that the combined list is sorted
...
Partition the list into two sublists, say lowerSublist and upperSublist
...
Quicksort lowerSublist
...
Quicksort upperSublist
...
Combine the sorted lowerSublist and sorted upperSublist
...
In other words, we use recursion to implement
quicksort
...
The algorithm for linked lists can be
developed in a similar manner and is left as an exercise for you
...

In quicksort, the list is partitioned in such a way that combining the sorted
lowerSublist and upperSublist is trivial
...
Because all the sorting work occurs during the
partition, we first describe the partition procedure in detail
...
The pivot is used to divide the list into two sublists: the lowerSublist and
the upperSublist
...
For example, consider the
list in Figure 10-21
...
However, the pivot is chosen so that, it
is hoped, the lowerSublist and upperSublist are of nearly equal size
...
The partition
procedure that we describe partitions this list using the pivot as the middle element,
in our case 50, as shown in Figure 10-22
...
Thus, after sorting lowerSublist and
upperSublist, combining the two sorted sublists is trivial
...
)
1
...

Suppose that the index smallIndex points to the last element smaller
than the pivot
...

2
...
Increment smallIndex
...
Swap the current element with the array element pointed to by
smallIndex
...
Swap the first element, that is, the pivot, with the array element
pointed to by smallIndex
...


1
0

554 |

Chapter 10: Sorting Algorithms

Step 1 determines the pivot and moves the pivot in the first array position
...

(Suppose the name of the array containing the list elements is list
...
The variable smallIndex contains the index of the last element
of lowerSublist and the variable index contains the index of the next element that
needs to be moved either in lowerSublist or in upperSublist
...

Next we illustrate Step 2
...


[0]
32

[1]
55

[2]
87

[3]
13

[4]
78

[5]
96

[6]
52

[7]
48

[8]
22

[9] [10] [11] [12] [13]
11
58
66
88
45

pivot

FIGURE 10-24

list before sorting

For the list in Figure 10-24, the pivot is at position 6
...
(Notice that in Figure 10-25, 52 is
swapped with 32
...


52
13
pivot

lower
Sublist
32
48

22

96

upper
Sublist
87
55

78

58

66

88

45

index

smallIndex

FIGURE 10-26

11

List after a few iterations of Step 2

As shown in Figure 10-26, the next element of the list that needs to be moved in a sublist
is indicated by index
...
To do so, we first advance smallIndex to the next
array position and then swap list[smallIndex] with list[index]
...
(Notice that 11 is swapped with 96
...


upper
Sublist

lower
Sublist
52
13
pivot

32

48

22

11

87

55

smallIndex

FIGURE 10-28

78

96

58

66

88

45

index

List before moving 58 into a sublist

For the list in Figure 10-28, list[index] is 58, which is greater than the pivot
...
This is accomplished
by leaving 58 at its position and increasing the size of upperSublist, by one, to the
next array position
...


556 |

Chapter 10: Sorting Algorithms

52 13
pivot

lower
Sublist
32 48 22

11

87

upper
Sublist
55 78 96

58

66

88

45

smallIndex

FIGURE 10-29

List after moving 58 into upperSublist

After moving the elements that are less than the pivot into lowerSublist and elements
that are greater than the pivot into upperSublist (that is, after completely executing
Step 2), the resulting list is as shown in Figure 10-30
...
This
is accomplished by swapping 52 with 45
...


45

lowerSublist
13
32
48
22

11

52

55

78

upperSublist
96
58
66

88

87

pivot

FIGURE 10-31

List after swapping 52 with 45

As shown in Figure 10-31, the preceding algorithm, Steps 1, 2, and 3, partitions the list
into two sublists such that the elements less than the pivot are in lowerSublist and
elements greater than or equal to the pivot are in upperSublist
...
In fact, upperSublist is
between the two indices smallIndex and index
...
After rearranging the elements of the list, the function returns the location of the

Quicksort: Array-Based Lists

|

557

pivot so that we can determine the starting and ending locations of the sublists
...
Thus, to partition a list, we need to pass only the starting and
ending indices of the list
...
The following function, swap, accomplishes this task:
template
void arrayListType::swap(int first, int second)
{
elemType temp;

}

temp = list[first];
list[first] = list[second];
list[second] = temp;

Once the list is partitioned into lowerSublist and upperSublist, we again apply the
quicksort method to sort the two sublists
...

Therefore, this section gives the recursive version of quicksort
...

Given the starting and ending indices of a list, the following function, recQuickSort,
implements the recursive version of quicksort:

1
0

558 |

Chapter 10: Sorting Algorithms

template
void arrayListType::recQuickSort(int first, int last)
{
int pivotLocation;

}

if (first < last)
{
pivotLocation = partition(first, last);
recQuickSort(first, pivotLocation - 1);
recQuickSort(pivotLocation + 1, last);
}

Finally, we write the quicksort function, quickSort, that calls the function
recQuickSort of the original list:
template
void arrayListType::quickSort()
{
recQuickSort(0, length -1);
}

We leave it as an exercise for you to write a program to test quicksort
...


Analysis: Quicksort
Table 10-2 summarizes the behavior of quicksort for a list of length n
...
)
TABLE 10-2

Analysis of quicksort for a list of length n
Number of comparisons

Number of swaps

Average case

1
...
69nlog2n + O(n) ¼ O(nlog2n)

Worst case

(1/2)(n2 – n) ¼ O(n2)

(1/2)n2 + (3/2)n – 2 ¼ O(n2)

Mergesort: Linked List-Based Lists
In the previous section, we described quicksort and stated that the average-case behavior
of quicksort is O(nlog2n)
...
This
section describes a sorting algorithm whose behavior is always O(nlog2n)
...
Mergesort
also partitions the list into two sublists, sorts the sublists, and then combines the sorted
sublists into one sorted list
...


Mergesort: Linked List-Based Lists

|

559

We leave it for you to develop mergesort for array-based lists, which can be done by
using the techniques described for linked lists
...
As discussed earlier,
quicksort first selects an element in the list, called pivot, and then partitions the list so
that the elements in one sublist are less than pivot and the elements in the other sublist
are greater than or equal to pivot
...
For example, consider the list whose elements are as follows:
list: 35

28

18

45

62

48

30

38

Mergesort partitions this list into two sublists as follows:
first sublist: 35 28 18 45
second sublist: 62 48 30 38

The two sublists are sorted using the same algorithm (that is, mergesort) used on the
original list
...
That is, suppose that the lists
are now as follows:
first sublist: 18 28 35 45
second sublist: 30 38 48 62

Next, mergesort combines, that is, merges, the two sorted sublists into one sorted list
...


35

28

18

45

62

48

30

38

divide
35

28

18

45

62

28

18
28

18

62

35

18

48

62

28

35

48

FIGURE 10-32

Mergesort algorithm

38
merge

62

30
merge

45

30
merge

18

30

merge

45

28

30

35

38

38

divide
48

merge
18

1
0

38
30

divide
45

merge

merge
28

45

divide

divide
35

30

divide

divide
35

48

45 48 62

38

48

62

38

560 |

Chapter 10: Sorting Algorithms

From Figure 10-32, it is clear that in mergesort, most of the sorting work is done in
merging the sorted sublists
...
Divide the list into two sublists
...
Mergesort the first sublist
...
Mergesort the second sublist
...
Merge the first sublist and the second sublist
...
In other words, we use
recursion to implement mergesort
...

Mergesort both sublists
...


Divide
Because data is stored in a linked list, we do not know the length of the list
...
Therefore, to divide the list into two
sublists, we need to find the middle node of the list
...


head
65

FIGURE 10-33

18

85

95

25

20

45

75

30

Unsorted linked list

To find the middle of the list, we traverse the list with two pointers—say, middle and
current
...
Because this list
has more than two nodes, we initialize current to the third node
...

Also, if the list has only two nodes, we set current to NULL
...


Mergesort: Linked List-Based Lists

|

561

head
65

18

middle

FIGURE 10-34

85

95

25

20

45

75

30

current

middle and current before traversing the list

Every time we advance middle by one node, we advance current by one node
...
That is, for the most part, every time middle advances by one node, current
advances by two nodes
...
For example, for the list in Figure 10-34, when current
becomes NULL, middle points to the node with info 25 (see Figure 10-35)
...
First, using the link of middle, we assign
a pointer to the node following middle
...
Figure
10-36 shows the resulting sublists
...
Recall that, in mergesort, most of the sorting work is
done in merging the sorted sublists
...

Sorted sublists are merged into a sorted list by comparing the elements of the sublists, and
then adjusting the references of the nodes with the smaller info
...
Suppose that first1 points to the first
node of the first sublist, and first2 points to the first node of the second sublist
...
We set newHead to point to the first node of the merged list
...

The pointer of the first node of the sublist with the smaller node then advances to the
next node of that sublist
...


first1

newHead
18

25

65

85

30

45

95

75

lastMerged
first2
20

FIGURE 10-38

Sublists after setting newHead and lastMerged and advancing first1

In Figure 10-38, first1 points to the first node of the first sublist that is yet to be
merged with the second sublist
...
For the sublists shown in Figure
10-38, after adjusting the necessary links, we have Figure 10-39
...
Every time we
move a node to the merged list, we advance either first1 or first2 to the next node
...
If first1 becomes NULL, the first
sublist is exhausted first, so we attach the remaining nodes of the second sublist at the
end of the partially merged list
...


1
0

564 |

Chapter 10: Sorting Algorithms

Following this discussion, we can now write the C++ function, mergeList, to merge
the two sorted sublists
...

template
nodeType* unorderedLinkedList::
mergeList(nodeType*
nodeType*
{
nodeType *lastSmall; //pointer to
//the merged
nodeType *newHead;
//pointer to

first1,
first2)
the last node of
list
the merged list

if (first1 == NULL)
//the first sublist is empty
return first2;
else if (first2 == NULL)
//the second sublist is empty
return first1;
else
{
if (first1->info < first2->info) //compare the first nodes
{
newHead = first1;
first1 = first1->link;
lastSmall = newHead;
}
else
{
newHead = first2;
first2 = first2->link;
lastSmall = newHead;
}
while (first1 != NULL && first2 != NULL)
{
if (first1->info < first2->info)
{
lastSmall->link = first1;
lastSmall = lastSmall->link;
first1 = first1->link;
}
else
{
lastSmall->link = first2;
lastSmall = lastSmall->link;
first2 = first2->link;
}
} //end while
if (first1 == NULL) //first sublist is exhausted first
lastSmall->link = first2;

Mergesort: Linked List-Based Lists

|

565

else
//second sublist is exhausted first
lastSmall->link = first1;
return newHead;
}
}//end mergeList

Finally, we write the recursive mergesort function, recMergeSort, which uses the
divideList and mergeList functions to sort a list
...

template
void unorderedLinkedList::recMergeSort(nodeType* &head)
{
nodeType *otherHead;
if (head != NULL) //if the list is not empty
if (head->link != NULL) //if the list has more than one node
{
divideList(head, otherHead);
recMergeSort(head);
recMergeSort(otherHead);
head = mergeList(head, otherHead);
}
} //end recMergeSort

We can now give the definition of the function mergeSort, which should be included as
a public member of the class unorderedLinkedList
...
) The function mergeSort calls the function recMergeSort and
passes first to this function
...
The
definition of the function mergeSort is as follows:
template
void unorderedLinkedList::mergeSort()
{
recMergeSort(first);
if (first == NULL)
last = NULL;
else
{
last = first;
while (last->link != NULL)
last = last->link;
}
} //end mergeSort

We leave it as an exercise for you to write a program to test mergesort
...


1
0

566 |

Chapter 10: Sorting Algorithms

Analysis: Mergesort
Suppose that L is a list of n elements, where n > 0
...
Moreover, each sublist can also be divided into two sublists of
the same size
...

Suppose that m ¼ 3, that is, n ¼ 23 ¼ 8
...
The first call to
the function recMergeSort divides the original list into two sublists, each of size 4
...
Each of these
recursive calls divides each sublist, of size 4, into two sublists, each of size 2
...
The next set of recursive calls divides each sublist, of size 2, into
sublists of size 1
...
It follows that the exponent 3 in
23 indicates the level of the recursion, as shown in Figure 10-40
...
Note that the number of recursion levels is
m
...

Consider the function mergeList, which merges two sorted lists into a sorted list
...
The initial call
to the function recMergeSort, at level 0, produces two sublists, each of size n / 2
...
At level 1, we merge two sets of sorted lists, where each sublist is
of size n / 4
...
Thus, at level 1 of the recursion, the number of comparisons is
2(n / 2 – 1) ¼ n – 2 ¼ O(n)
...
Each of these calls merge two sublists, each of size n / 2k + 1,
which requires a maximum of n / 2k À 1 comparisons
...
It now follows that
the maximum number of comparisons at each level of the recursion is O(n)
...
Now n ¼ 2m implies that m ¼ log2n
...

If W(n) denotes the number of key comparisons in the worst case to sort L, then W(n) ¼
O(n log2n)
...
In the average case,
during the merge process one of the sublists will exhaust before the other list
...
On average, it can be shown that the number of
comparisons for mergesort is given by the following equation: If n is a power of 2, A(n) ¼
n log2n À 1
...
This is also a good approximation when n is not a power of 2
...
As noted before, in mergesort, all the comparisons are made in the method
mergeList, which merges two sorted sublists
...
Hence,
W (n) ¼ W (s) + W (t ) + s + t À 1
Note that s ¼ n / 2 and t ¼ n / 2
...
Then s ¼ 2mÀ1 and t ¼ 2mÀ1
...
Hence,
W (n) ¼ W (n / 2) + W (n / 2) + n – 1 ¼ 2 W (n / 2) + n – 1, n > 0
Also,
W (1) ¼ 0
It is known that when n is a power of 2, W (n) is given by the following equation:
W (n) ¼ n log2n À (n À 1) ¼ O (n log2n)

Heapsort: Array-Based Lists
In an earlier section, we described the quicksort algorithm for contiguous lists, that is,
array-based lists
...

However, in the worst case, quicksort is of the order O(n2)
...
This algorithm is of order O(n log2n) even in
the worst case, therefore overcoming the worst case of the quicksort
...

Recall that, in C++ the array index starts at 0
...

Consider the list in Figure 10-41
...
For example, consider
list[3], which is 50
...
Clearly, list[3] is larger than list[7] and list[8]
...
Therefore, to facilitate the discussion of heapsort, we typically view data in
the form of a complete binary tree as described next
...


85
70
50
20

FIGURE 10-42

80
40

10 35

75
15

62

30
58

Complete binary tree corresponding to the list in Figure 10-41

In Figure 10-42, the first element of the list, which is 85, is the root node of the tree
...
Thus, in general, for the node k,
which is the k À 1th element of the list, its left child is the 2kth (if it exists) element of the
list, which is at position 2k À 1 in the list, and the right child is the 2k + 1st (if it exists)
element of the list, which is at position 2k in the list
...
Also note that in Figure 10-42, the
elements 20, 10, 35, 15, 62, 58, and 30 are called leaves as they have no children
...
Note that even though we will draw a complete binary

Heapsort: Array-Based Lists

|

569

tree to illustrate heapsort, the data gets manipulated in an array
...

The first step in heapsort is to convert the list into a heap, called buildHeap
...


Build Heap
This section describes the build heap algorithm
...
Let
index = length / 2 – 1
...
Thus, elements list[index + 1]

...

First, we convert the subtree with the root node list[index] into a heap
...
We then convert the subtree with the root node
list[index - 1] into a heap, and so on
...

Compare list[b] with list[c] to determine the larger child
...
Suppose that largerIndex indicates the larger
child
...
)
Compare list[a] with list[largerIndex]
...

Suppose that list[a] < list[largerIndex] and we swap the elements list[a] with
list[largerIndex]
...
If this is the case, then we repeat Steps 1
and 2 at the subtree with root node list[largerIndex] and continue this process until
either the heap in the subtrees is restored or we arrive at an empty subtree
...

Consider the list in Figure 10-43
...


list

FIGURE 10-43

[0]
15

[1]
60

Array list

[2]
72

[3]
70

[4]
56

[5]
32

[6]
62

[7]
92

[8]
45

[9] [10]
30
65

1
0

570 |

Chapter 10: Sorting Algorithms

Figure 10-44 shows the complete binary tree corresponding to the list in Figure 10-43
...

This list has 11 elements, so the length of the list is 11
...

Now list[4] = 56
...
In the previous list, both list[9] and list[10] exist
...
Find the larger of list[9] and list[10], that is, the largest child of
list[4]
...

2
...
If the larger child is
larger than the parent, swap the larger child with the parent
...

3
...

Figure 10-45(a) shows the resulting binary tree
...
(Notice
that Step 3 does not execute here either
...
Figure 10-45(c) shows the resulting binary tree
...
)

Heapsort: Array-Based Lists

|

571

Next, we consider the subtree with the root node list[1], that is, 60, see 10-45(c)
...
Because list[1] = 60 < list[3] = 92 (the larger child),
we swap list[1] with list[3], to obtain the tree as given in Figure 10-46(a)
...
Thus, we must restore the heap in this subtree
...
We then
obtain the binary tree as given in Figure 10-46(b)
...

Finally, we consider the tree with the root node list[0], that is, 15
...


92

92

15
32

65

70
60

72

45 30

62

(a) Binary tree after applying
Steps 1 and 2 at list[0]

FIGURE 10-47

92

70

56

72
32

65

15
60

45 30

1
0

56

70
62

32

65

60
15

72

45 30

62

56

(b) Binary tree after applying (c) Binary tree after restoring
Steps 1 and 2 at list[1]
the heap at list[3]

Binary tree while building heap at list[0]

We see that the subtree with the root node list[1], that is, 15, is no longer in a heap
...
(This requires us to repeat
Steps 1 and 2 at the subtree with root node list[1]
...
We then get the binary tree of
Figure 10-47(b)
...
To do so, we apply Steps 1 and 2 at the subtree with root node
list[3]
...

Figure 10-47(c) shows the resulting binary tree
...

Thus, in general, starting at the lowest level from right to left, we look at a subtree and
convert the subtree into a heap as follows: If the root node of the subtree is smaller than
the larger child, we swap the root node with the larger child
...

Suppose low contains the index of the root node of the subtree and high contains the
index of the last item in the list
...
The preceding discussion translates into the following C++ algorithm:
int largeIndex = 2 * low + 1;

//index of the left child

while (largeIndex <= high)
{
if ( largeIndex < high)
if (list[largeIndex] < list[largeIndex + 1])
largeIndex = largeIndex + 1; //index of the larger child
if (list[low] > list[largeIndex])
//the subtree is already in
//a heap
break;
else
{
swap(list[low], list[largeIndex]);
//Line swap**
low = largeIndex; //go to the subtree to further
//restore the heap
largeIndex = 2 * low + 1;
} //end else
}//end while

The swap statement at the line marked Line swap** swaps the parent with the larger
child
...
The while
loop moves the parent node to a place in the tree so that the resulting subtree with the
root node list[low] is in a heap
...
Then each time through the loop, the larger child is compared with
temp
...

Next, we describe the function heapify, which restores the heap in a subtree by
making one item assignment each time through the loop
...

template
void arrayListType::heapify(int low, int high)
{
int largeIndex;
elemType temp = list[low]; //copy the root node of the subtree
largeIndex = 2 * low + 1;

//index of the left child

while (largeIndex <= high)
{
if (largeIndex < high)
if (list[largeIndex] < list[largeIndex + 1])
largeIndex = largeIndex + 1; //index of the largest
//child
if (temp > list[largeIndex]) //subtree is already in a heap
break;
else
{
list[low] = list[largeIndex]; //move the larger child
//to the root
low = largeIndex; //go to the subtree to restore the heap
largeIndex = 2 * low + 1;
}
}//end while
list[low] = temp; //insert temp into the tree, that is, list
} //end heapify

Next, we use the function heapify to implement the buildHeap function to convert
the list into a heap
...

Suppose the list is in a heap
...


1
0

574 |

Chapter 10: Sorting Algorithms

92
70

72

15

32

65

60
45 30

56

(a) A heap

FIGURE 10-48

72

56
70
62

65

60
15

45 30

70

72
32
92

(b) Binary tree after moving
the root node to the end

15

62
32

65

60

62

45 30

56

92

(c) Binary tree after the statement
heapify(list, 0, 9);
executes

Heapsort

Because this is a heap, the root node is the largest element of the tree, that is, the largest
element of the list
...
We swap the root node of
the tree, that is, the first element of the list, with the last node in the tree (which is the last
element of the list)
...

Because the largest element is now in its proper place, we consider the remaining elements of
the list, that is, elements list[0]
...
The complete binary tree representing this
list is no longer a heap, and so we must restore the heap in this portion of the complete binary
tree
...
A call to this function is as follows:
heapify(list, 0, 9);

We thus obtain the binary tree as shown in Figure 10-48(c)
...
list[9]
...
list[8]
...

The following C++ function describes this algorithm:
template
void arrayListType::heapSort()
{
elemType temp;
buildHeap();
for (int lastOutOfOrder = length - 1; lastOutOfOrder >= 0;
lastOutOfOrder--)
{
temp = list[lastOutOfOrder];
list[lastOutOfOrder] = list[0];
list[0] = temp;
heapify(0, lastOutOfOrder - 1);
}//end for
}//end heapSort

Priority Queues (Revisited) |

575

We leave as an exercise for you to write a program to test heapsort; see Programming
Exercise 11 at the end of this chapter
...
In the worst case, the number of key
comparisons in heapsort to sort L (the number of comparisons in heapSort and the
number of comparisons in buildHeap) is 2nlog2n + O(n)
...
On average, the number
of comparisons made by heapsort to sort L is of O(nlog2n)
...
39nlog2n + O(n) and
the number of swaps is 0
...
Because each swap is three assignments, the
number of item assignments in the average case of quicksort is at least 1
...

It now follows that for the key comparisons, the average case of quicksort is somewhat
better than the worst case of heapsort
...
However,
the worst case of quicksort is of O(n2)
...


Priority Queues (Revisited)
Chapter 8 introduced priority queues
...
Chapter 8 stated that we would
discuss the implementation of priority queues after describing heapsort
...

In a heap, the largest element of the list is always the first element of the list
...
To ensure that the largest element of the priority queue is always the first element of
the queue, we can implement priority queues as heaps
...
The next two sections describe these algorithms
...
Insert the new element in the first available position in the list
...
)
2
...
So to restore the heap:
while (the parent of the new entry is smaller than the new entry)
swap the parent with the new entry
...

REMOVE AN ELEMENT FROM THE PRIORITY QUEUE
Assuming the priority queue is implemented as a heap, to remove the first element of the
priority queue, we perform the following steps:

1
...

2
...

3
...

The other operations for priority queues can be implemented in the same way as
implemented for queues
...


PROGRAMMING EXAMPLE:

Election Results

The presidential election for the student council of your local university is about to
be held
...

The university has four major divisions, and each division has several departments
...
Each department in each division handles its own voting and reports the
votes received by each candidate to the election committee
...

For this program, we assume that six candidates are seeking the student council’s
president post
...

The data are provided in two files
...
txt, consists of the names of
the candidates seeking the president’s post
...
In the second file, voteData
...
txt consists of the candidate’s name, the region
number, and the number of votes received by the candidate in that region
...
For example, the input file containing the voting data looks like
the following:
Greg Goldy 2 34
Mickey Miller 1 56
Lisa Fisher 2 56

...


...

Input
Output
PROBLEM
ANALYSIS
AND
ALGORITHM
DESIGN

Two files: one containing the candidates’ names and the other containing the
voting data as described previously
...


From the output, it is clear that the program must organize the voting data by
region and calculate the total votes both received by each candidate and polled for
the election
...

The main component of this program is a candidate
...
Every candidate has a name

and receives votes
...
In Example 1-12 (Chapter 1), we designed the class personType to
implement the name of a person
...
Now that we have discussed operator overloading (see Chapter 2), we can redesign the class personType and define the
relational operators so that the names of two people can be compared
...
Because every candidate is a person, we derive
the class candidateType from the class personType
...

Therefore, the class personType has two data members: a data member,
firstName, to store the first name and a data member, lastName, to store the
last name
...
The definition of the class
personType is given next:
//*************************************************************
// Author: D
...
Malik
//
// This class specifies the members to implement a person's
// name
...

friend istream& operator>>(istream&, personType&);
friend ostream& operator<<(ostream&, const personType&);
public:
const personType& operator=(const personType&);
//Overload the assignment operator
...

//Postcondition: firstName = first; lastName = last
string getFirstName() const;
//Function to return the first name
...

string getLastName() const;
//Function to return the last name
...

personType(string first = "", string last = "");
//constructor with parameters
//Set firstName and lastName according to the parameters
...

bool operator==(const personType& right) const;
bool operator!=(const personType& right) const;
bool operator<=(const personType& right) const;

Programming Example: Election Results

|

579

bool operator<(const personType& right) const;
bool operator>=(const personType& right) const;
bool operator>(const personType& right) const;
protected:
string firstName; //variable to store the first name
string lastName; //variable to store the last name
};

We only give the definitions of the functions to overload the operators == and >> and
leave others as an exercise for you; see Programming Exercise 13 at the end of this
chapter
...
firstName
&& lastName == right
...
firstName >> pName
...
Every candidate has a first and a last name, and receives
votes
...
We also need a data member to store the total
number of votes received by each candidate
...
Because the data members of the
class personType are protected, these data members can be accessed directly in
the class candidateType
...
Therefore, we declare a list of six candidates of type
candidateType
...
In Chapter 9, we derived the class
orderedArrayList from the class arrayListType and included the binary search
algorithm
...
This list of
candidates will be sorted and searched
...

Data in the file containing the candidates’ data consists of only the names of the
candidates
...
That is, we overload the
assignment operator twice: once for objects of the type candidateType, and another
for objects of the types candidateType and personType
...
S
...

//*************************************************************
#include
#include "personType
...

//Postcondition: Votes for the region specified by the
//
parameter are updated by adding the votes specified
//
by the parameter votes
...

//Postcondition: Votes for the region specified by the
//
parameter are set to the votes specified by the
//
parameter votes
...

//Postcondition: The votes in each region are added and
//
assigned to totalVotes
...

//Postcondition: The value of totalVotes is returned
...

candidateType();
//Default constructor
...

//Overload the relational operators
...

To set the votes of a particular region, the region number and the number of votes are
passed as parameters to the function setVotes
...
Therefore, to
set the value of the correct array component, 1 is subtracted from the region
...
The votes are then added to the region’s
previous value
...
For example, two candidates are the same if they have
the same name
...
We only
give the definition of the function to overload the operator == and leave others as an
exercise for you; see Programming Exercise 13
...
firstName
&& lastName == right
...


Programming Example: Election Results

MAIN
PROGRAM

|

583

Now that the class candidateType has been designed, we focus on designing the
main program
...
The first thing that the program should
do is read each candidate’s name from the file candData
...
Next, we sort candidateList
...
txt, which holds
the voting data
...

Thus, the general algorithm is as follows:
1
...

3
...

5
...

Sort candidateList
...

Calculate the total votes received by each candidate
...

following

statement

creates

the

object

candidateList

of

type

orderedArrayListType
...
Every component of the array list
is an object of the type candidateType
...
To save

584 |

Chapter 10: Sorting Algorithms

space, whenever needed, we will draw the object candidateList as shown in
Figure 10-50
...
Therefore, we write a function to accomplish this task
...
txt is opened in the function main
...

Because the data member list of the object candidateList is a protected data
member, it cannot be accessed directly
...

The definition of the function fillNames is as follows:
void fillNames(ifstream& inFile,
orderedArrayListType& cList)
{
string firstN;
string lastN;
candidateType temp;

Programming Example: Election Results

}

|

585

for (int i = 0; i < NO_OF_CANDIDATES; i++)
{
inFile >> firstN >> lastN;
temp
...
insertAt(i, temp);
}

After a call to the function fillNames, Figure 10-51 shows the object
candidateList
...
Because candidateList is an object of the type orderedArrayListType,

all sorting algorithms discussed in this chapter are available to it
...
The following statement accomplishes this task:
candidateList
...


1
0

586 |

Chapter 10: Sorting Algorithms

candidateList
list
list[0]

Sheila

Bower

0

0

0

0

0

list[1]

Danny

Dillion

0

0

0

0

0

list[2]

Lisa

Fisher

0

0

0

0

0

list[3]

Greg

Goldy

0

0

0

0

0

list[4]

Peter

Lamba

0

0

0

0

0

list[5]

Mickey

Miller

0

0

0

0

0

length
maxSize

6
6

after

the

10-52 Object candidateList
selectionSort(); executes

FIGURE

Process
Voting
Data

statement

candidateList
...
Each entry in the file
voteData
...
txt, we locate the row in the array
list (of the object candidateList) corresponding to the specific candidate, and
update the entry specified by regionNumber
...
Moreover, list is a private data member of candidateList
...
We can use the member function retrieveAt to make a copy
of the candidate whose votes need to be updated
...
Suppose the next entry read is
Lisa Fisher 2 35

This entry says that Lisa Fisher received 35 votes from region 2
...


Programming Example: Election Results

|

587

candidateList
list
list[0]

Bower

0

0

50

0

0

list[1]

Danny

Dillion

10

0

56

0

0

list[2]

Lisa

Fisher

76

13

0

0

0

list[3]

Greg

Goldy

0

45

0

0

0

list[4]

Peter

Lamba

80

0

0

0

0

list[5]

Mickey

Miller

100

0

0

20

0

length
maxSize

FIGURE 10-53

Sheila

6
6

Object candidateList before processing the entry Lisa Fisher 2 35

We make a copy of the row corresponding to Lisa Fisher (see Figure 10-54)
...
(Here region = 2
and votes = 35
...
updateVotesByRegion(region, votes);

After this statement executes, the object temp is as shown in Figure 10-55
...
updateVotesByRegion(region,

votes); executes

588 |

Chapter 10: Sorting Algorithms

Now we copy the object temp into list (see Figure 10-56)
...
Also, the function binarySearch is a member of the
class orderedArrayListType, so we can use this function to search the array
list
...

Add Votes

After processing the voting data, the next step is to find the total votes received by
each candidate
...
Now
votesByRegion is a private data member of candidateType and list is a
protected data member of candidateList
...
The following function does this:
void addVotes(orderedArrayListType& cList)
{
candidateType temp;
for (int i = 0; i < NO_OF_CANDIDATES; i++)

Programming Example: Election Results

|

589

{

}

}

cList
...
calculateTotalVotes();
cList
...


candidateList
list
list[0]

Bower

23

70

133

267

493

list[1]

Danny

Dillion

25

71

156

97

349

list[2]

Lisa

Fisher

110

158

0

0

268

list[3]

Greg

Goldy

75

34

134

0

243

list[4]

Peter

Lamba

285

56

0

46

387

list[5]

Mickey

Miller

120

141

156

67

476

length
maxSize

FIGURE 10-57

Sheila

6
6

candidateList after a call to the function addVotes

Print To complete the program, we include a function to print the heading, the first four
Heading lines of the output
...
Suppose that
the variable sumVotes holds the total votes polled for the election, the variable
largestVotes holds the largest number of votes received by a candidate, and the
variable winLoc holds the index of the winning candidate in the array list
...
The algorithm for this
function is as follows:
1
...

2
...
Retrieve the candidate’s data into temp
...
Print the candidate’s name and relevant data
...
Retrieve the total votes received by the candidate and update
sumVotes
...
getTotalVotes())
{
largestVotes = temp
...
Output the final lines of the output
...

PROGRAM LISTING (MAIN PROGRAM)
//**************************************************************
// Author: D
...
Malik
//
// Program: Election Results
// Given candidates' voting this program determines the winner
// of the election
...

//**************************************************************
#include
#include
#include
#include
#include




"candidateType
...
h"

using namespace std;
const int NO_OF_CANDIDATES = 6;

Programming Example: Election Results

|

591

void fillNames(ifstream& inFile,
orderedArrayListType& cList);
void processVotes(ifstream& inFile,
orderedArrayListType& cList);
void addVotes(orderedArrayListType& cList);
void printHeading();
void printResults(orderedArrayListType& cList);
int main()
{
orderedArrayListType
candidateList(NO_OF_CANDIDATES);
ifstream inFile;
inFile
...
txt");
fillNames(inFile, candidateList);
candidateList
...
close();
inFile
...
txt");
processVotes(inFile, candidateList);
addVotes(candidateList);
printHeading();
printResults(candidateList);
}

return 0;

//Place the definitions of the functions fillNames, addVotes,
//printHeading here
...


Sample Output (After you have written the definitions of the functions of the
classes personType and candidateType, and the definitions of the function
processVotes and printResults, and run your program, it should produce the
following output; see Programming Exercise 13
...
txt
Greg Goldy
Mickey Miller
Lisa Fisher
Peter Lamba
Danny Dillion
Sheila Bower
voteData
...

2
...


4
...


6
...


8
...

10
...

12
...

For a list of length n, where n > 0, selection sort makes (1/2)n(n – 1) key
comparisons and 3(n – 1) item assignments
...

Empirical studies suggest that for large lists of size n, the number of moves
in Shellsort is in the range of n1
...
6n1
...

Let L be a list of n distinct elements
...

Both quicksort and mergesort sort a list by partitioning the list
...
The algorithm then rearranges the elements so that the elements in
one of the sublists are less than the pivot, and the elements in the second
sublist are greater than or equal to the pivot
...

On average, the number of key comparisons in quicksort is O(nlog2n)
...

Mergesort partitions the list by dividing it in the middle
...

The number of key comparisons in mergesort is O(nlog2n)
...


14
...


Chapter 10: Sorting Algorithms

A heap is a list in which each element contains a key, such that the key in
the element at position k in the list is at least as large as the key in the
element at position 2k + 1 (if it exists) and 2k + 2 (if it exists)
...
After we convert the array into a heap, the sorting phase
begins
...
In the worst case, the
number of key comparisons in heapsort to sort L is 2nlog2n + O(n)
...


EXERCISES
1
...


3
...


5
...


7
...


Sort the following list using selection sort as discussed in this chapter
...

26, 45, 17, 65, 33, 55, 12, 18
Sort the following list using selection sort as discussed in this chapter
...

36, 55, 17, 35, 63, 85, 12, 48, 3, 66
Assume the following list of keys: 5, 18, 21, 10, 55, 20
The first three keys are in order
...
To move 5 to its proper position using
insertion sort as described in this chapter, exactly how many key comparisons are executed?
Assume the following list of keys: 28, 18, 21, 10, 25, 30, 12, 71, 32, 58, 15
This list is to be sorted using insertion sort as described in this chapter for
array-based lists
...

Recall insertion sort for array-based lists as discussed in this chapter
...

Consider the following list of keys: 80, 57, 65, 30, 45, 77, 27, 4, 90, 54, 45,
2, 63, 38, 81, 28, 62
...

Show the list during each increment, as in this chapter
...
Use the increment sequence 1, 4, 7
...
Explain how
mergesort differs from quicksort in partitioning the list
...
Use
pivot as the middle element of the list
...


9
...


Give the resulting list after one call to the partition procedure
...
Give the resulting list after two calls to the partition procedure
...


11
...
Use
pivot as the median of the first, last, and middle elements of the list
...
Give the resulting list after one call to the partition procedure
...
Show the final form of the array
...


12
...


14
...


16
...


47, 78, 81, 52, 50, 82, 58, 42, 65, 80, 92, 53, 63, 87, 95, 59, 34, 37, 7, 20
Suppose that the following list was created by the function buildHeap
during the heap creation phase of heapsort
...
(Use the heapify
procedure as given in this chapter
...
If L
is already sorted in the reverse order, show that the number of comparisons
is (1/2)(n2 – n) and the number of item assignments is (1/2)(n2 +3n) – 2
...
If L
is already sorted, show that the number of comparisons is (n – 1) and the
number of item assignments is 0
...

Write the definition of the class unorderedLinkedList that implements
the searching (described in Chapter 5) and sorting algorithms for linked lists
as discussed in this chapter
...

2
...

4
...


Write and test a version of selection sort for linked lists
...

Write a program to test insertion sort for linked lists as given in this chapter
...
Also write a program to test Shellsort given in this chapter
...

Use insertion sort to sort the array
...

b
...
Print the number of comparisons and the number of item
movements
...
Test your program on a list of 1,000 elements and on a list of 10,000
elements
...

Write and test a version of quicksort for linked lists
...
A
...
Hoare) Let L be a list of size n
...

Write and implement a C++ function, kThSmallestItem, that uses a
version of quicksort to determine the kth smallest item in L without
completely sorting L
...


6
...

8
...


Sort the array using pivot as the middle element of the array
...

c
...
However,
when the size of any sublist reduces to less than 20, sort the sublist using
insertion sort
...
Sort the array using pivot as the median of the first, last, and middle
elements of the array
...

e
...

Write a program to test mergesort for linked lists as given in this chapter
...

a
...

a
...


10
...

12
...

c
...

a
...

b
...

c
...

d
...

In the Programming Example Election Results, the class candidateType
contains the function calculateTotalVotes
...

The function updateVotesByRegion (of the class candidateType)
updates only the number of votes for a particular region
...
By doing so, the function addVotes in the main program is no
longer needed
...

In the Programming Example Election Results, the object candidateList
of the type orderedArrayListType is declared to process the voting data
...
To update the candidate’s votes, we
copied each candidate’s data from candidateList into a temporary object
of the type candidateType, updated the temporary object, and then
replaced the candidate’s data with the temporary object
...
In this exercise, you
are to modify the Programming Example Election Results to simplify
the accessing of a candidate’s data as follows: Derive a class
candidateListType from the class orderedArrayListType
...


13
...


15
...

//Postcondition: The name of the candidate, the region,
//and the number of votes are passed as parameters
...

void printResult() const;
//Function to output the voting data
...

Write the definitions of the member functions of the class
candidateListType
...


11
CHAPTER

B INARY T REES

AND

I N T H I S C H A P T E R , YO U W I L L :


...


Explore various binary tree traversal algorithms


...


Discover how to insert and delete items in a binary search tree


...


Learn about AVL (height-balanced) trees


...
You have already seen
how to store and process data in an array
...
However, we know that
storing data in an array has its limitations
...
To speed up item insertion
and deletion, we can use linked lists
...
However, one
of the drawbacks of linked lists is that they must be processed sequentially
...
As you know, a sequential search is good only for very small lists
because the average search length of a sequential search is half the size of the list
...

We first introduce some definitions to facilitate our discussion
...
T has a special node called the root node
...
T has two sets of nodes, LT and RT, called the left subtree and right
subtree of T, respectively
...
LT and RT are binary trees
...
Suppose that T is a binary tree with a root node
A
...
Now LA and
RA are binary trees
...
B
is called the left child of A; C is called the right child of A
...

In the diagram of a binary tree, each node of the binary tree is represented as a circle and
the circle is labeled by the node
...
The
left child of the root node (if any) is drawn below and to the left of the root node
...
Children are connected to the parent by an arrow from the parent to the child
...

Because the root node, B, of LA is already drawn, we apply the same procedure to draw
the remaining parts of LA
...
If a node has no left child, for example,
when we draw an arrow from the node to the left child, we end the arrow with three
lines
...


Binary Trees

|

601

The diagram in Figure 11-1 is an example of a binary tree
...
The left subtree of the root node, which we denoted by LA, is the set LA ¼ {B,
D, E, G} and the right subtree of the root node, which we denote by RA, is the set RA ¼
{C, F, H}
...
The root node of RA is C, and so on
...
Because
three lines at the end of an arrow mean that the subtree is empty, it follows that the left
subtree of D is empty
...
Similarly, for node F,
the left child is H and node F has no right child
...

EXAMPLE 11-1
Figure 11-2 shows binary trees with one, two, or three nodes
...

In the binary tree of Figure 11-2(b), the root node is A, LA ¼ {B}, and RA ¼ empty
...


602 |

Chapter 11: Binary Trees and B-Trees

In the binary tree of Figure 11-2(c), the root node is A, LA ¼ empty, RA ¼ {C}
...

In the binary tree of Figure 11-2(d), the root node is A, LA ¼ {B}, RA ¼ {C}
...
The root node of RA ¼ C, LC ¼ empty,
RC ¼ empty
...
See Figure 11-3
...
Thus, every node, other than storing its own information, must keep track of its
left subtree and right subtree
...
The pointer llink points to the root node of the left subtree; the pointer rlink
points to the root node of the right subtree
...

A pointer to the left child is stored in llink
...


Binary Trees

|

603

Furthermore, a pointer to the root node of the binary tree is stored outside the binary tree
in a pointer variable, usually called the root, of type binaryTreeNode
...


root
A

B

D

E
G

FIGURE 11-4

C

F
H

Binary tree

For simplicity, we will continue to draw binary trees as before
...
As before, three lines at the
end of an arrow mean that the subtree is empty
...

A node in the binary tree is called a leaf if it has no left and right children
...
U is called a parent of V if there is a branch from U to V
...
, Xn such that
i
...
Xi-1 is the parent of Xi for all i ¼ 1, 2,
...
That is, there is a branch
from X0 to X1, X1 to X2,
...
, Xn-1 to Xn
...

Definition: The level of a node in a binary tree is the number of branches on the path
from the root to the node
...

Definition: The height of a binary tree is the number of nodes on the longest path
from the root to a leaf
...
We next describe the
C++ function height to find the height of the binary tree
...


1
1

604 |

Chapter 11: Binary Trees and B-Trees

If the binary tree is empty, the height is 0
...
To
find the height of the binary tree, we first find the height of the left subtree and the height
of the right subtree
...
To find the height of the left (right) subtree, we apply the
same procedure because the left (right) subtree is a binary tree
...
Suppose height(p) denotes the
height of the binary tree with root p
...
The following function implements this algorithm:
template
int height(binaryTreeNode *p) const
{
if (p == NULL)
return 0;
else
return 1 + max(height(p->llink), height(p->rlink));
}

The definition of the function height uses the function max to determine the larger of
two integers
...

Similarly, we can implement algorithms to find the number of nodes and number of leaves
in a binary tree
...
A
binary tree is a dynamic data structure; that is, memory for its nodes is allocated and
deallocated during program execution
...
To
make an identical copy of a binary tree, we need to create as many nodes as there are in
the binary tree to be copied
...

Given a pointer to the root node of a binary tree, we next describe the function copyTree,
which makes a copy of a given binary tree
...

template
void copyTree(binaryTreeNode* &copiedTreeRoot,
binaryTreeNode* otherTreeRoot)
{
if (otherTreeRoot == NULL)
copiedTreeRoot = NULL;

Binary Tree Traversal

|

605

else
{
copiedTreeRoot = new binaryTreeNode;
copiedTreeRoot->info = otherTreeRoot->info;
copyTree(copiedTreeRoot->llink, otherTreeRoot->llink);
copyTree(copiedTreeRoot->rlink, otherTreeRoot->rlink);
}
}//end copyTree

Binary Tree Traversal
The item insertion, deletion, and lookup operations require that the binary tree be
traversed
...
As you can see from the diagram of a
binary tree, the traversal must start at the root node because there is a pointer to the root
node
...

Visit the subtrees first
...


Inorder Traversal
In an inorder traversal, the binary tree is traversed as follows:
1
...

2
...

3
...


Preorder Traversal
In a preorder traversal, the binary tree is traversed as follows:
1
...

2
...

3
...


Postorder Traversal
In a postorder traversal, the binary tree is traversed as follows:
1
...

2
...

3
...


1
1

606 |

Chapter 11: Binary Trees and B-Trees

Clearly, each of these traversal algorithms is recursive
...
The listing of the nodes produced by the preorder traversal of a
binary tree is called the preorder sequence
...

Before giving the C++ code for each of these traversals, let us illustrate the inorder
traversal of the binary tree in Figure 11-5
...
The section, ‘‘Binary Tree Traversal and
Functions as Parameters,’’ located later in this chapter, explains how to modify the binary
tree traversal algorithms so that by using a function, the user can specify the action to be
performed on a node when the node is visited
...
Therefore, we start the traversal at A
...
Traverse the left subtree of A; that is, traverse LA ¼ {B, D}
...
Visit A
...
Traverse the right subtree of A; that is, traverse RA ¼ {C}
...

1
...
Now LA is a
binary tree with the root node B
...

1
...
Traverse the left subtree of B; that is, traverse LB ¼ empty
...
2
...

1
...
Traverse the right subtree of B; that is, traverse RB ¼ {D}
...
1 before going to Step 1
...

1
...
Because the left subtree of B is empty, there is nothing to traverse
...
1 is completed, so we proceed to Step 1
...

1
...
Visit B
...
Clearly, the first node
printed is B
...
2, so we proceed to Step 1
...


Binary Tree Traversal

|

607

1
...
Traverse the right subtree of B; that is, traverse RB ¼ {D}
...
Because RB is a binary
tree, we apply the inorder traversal criteria to RB
...
3
...
Traverse the left subtree of D; that is, traverse LD ¼ empty
...
3
...
Visit D
...
3
...
Traverse the right subtree of D; that is, traverse RD ¼ empty
...
3
...
Because the left subtree of D is empty, there is nothing to
traverse
...
3
...
3
...

1
...
2
...
That is, output D on an output device
...
3
...
3
...

1
...
3
...
Step 1
...
3 is completed
...
3
...
1, 1
...
3 are completed, Step 1 is
completed, and so we go to Step 2
...
Visit A
...
This completes Step 2,
so we proceed to Step 3
...
Traverse the right subtree of A; that is, traverse RA ¼ {C}
...
Because RA is a binary tree, we apply
the inorder traversal criteria to RA
...
1
...

3
...
Visit C
...
3
...

3
...
Because the left subtree of C is empty, there is nothing to traverse
...
1 is completed
...
2
...
That is, output C on an output device
...
2, so we proceed to Step 3
...

3
...
Because the right subtree of C is empty, there is nothing to
traverse
...
3 is completed
...

Clearly, the inorder traversal of the previous binary tree outputs the nodes in the
following order:
Inorder sequence: B D A C
Similarly, the preorder and postorder traversals output the nodes in the following order:
Preorder sequence: A B D C
Postorder sequence: D B C A

1
1

608 |

Chapter 11: Binary Trees and B-Trees

As you can see from the walk-through of the inorder traversal, after visiting the left
subtree of a node we must come back to the node itself
...
Therefore, before going to a child, we must
somehow save a pointer to the parent node
...
(Later we discuss how to write nonrecursive traversal
functions
...
For example, if the root points to the root node of the
binary tree, a call to the function inorder is as follows:
inorder(root);

Similarly, we can write the functions to implement the preorder and postorder traversals
...

template
void preorder(binaryTreeNode *p) const
{
if (p != NULL)
{
cout << p->info << " ";
preorder(p->llink);
preorder(p->rlink);
}
}
template
void postorder(binaryTreeNode *p) const
{
if (p != NULL)
{
postorder(p->llink);
postorder(p->rlink);
cout << p->info << " ";
}
}

Binary Tree Traversal

|

609

Implementing Binary Trees
The previous sections described various operations that can be performed on a binary
tree, as well as the functions to implement these operations
...
Before designing the class to implement a binary tree
as an ADT, let us list various operations that are typically performed on a binary tree:










Determine whether the binary tree is empty
...

Insert an item in the binary tree
...

Find the height of the binary tree
...

Find the number of leaves in the binary tree
...

Copy the binary tree
...
However, because the nodes of a binary tree are in no particular order, these
algorithms are not very efficient on arbitrary binary trees
...
Therefore, we
will discuss these algorithms when we discuss special binary trees
...
The definition of the node is the same as before
...

//*************************************************************
// Author: D
...
Malik
//
// class binaryTreeType
// This class specifies the basic operations to implement a
// binary tree
...

bool isEmpty() const;
//Returns true if the binary tree is empty;
//otherwise, returns false
...

void preorderTraversal() const;
//Function to do a preorder traversal of the binary tree
...

int treeHeight() const;
//Returns the height of the binary tree
...

int treeLeavesCount() const;
//Returns the number of leaves in the binary tree
...

//Postcondition: root = NULL;
binaryTreeType(const binaryTreeType& otherTree);
//copy constructor
binaryTreeType();
//default constructor
~binaryTreeType();
//destructor
protected:
binaryTreeNode

*root;

private:
void copyTree(binaryTreeNode* &copiedTreeRoot,
binaryTreeNode* otherTreeRoot);
//Makes a copy of the binary tree to which
//otherTreeRoot points
...

void destroy(binaryTreeNode* &p);
//Function to destroy the binary tree to which p points
...


Binary Tree Traversal

|

611

void preorder(binaryTreeNode *p) const;
//Function to do a preorder traversal of the binary
//tree to which p points
...


};

int height(binaryTreeNode *p) const;
//Function to return the height of the binary tree
//to which p points
...

int nodeCount(binaryTreeNode *p) const;
//Function to return the number of nodes in the binary
//tree to which p points
int leavesCount(binaryTreeNode *p) const;
//Function to return the number of leaves in the binary
//tree to which p points

Notice that the definition of the class binaryTreeType contains the statement to
overload the assignment operator, copy constructor, and destructor
...
Recall that for classes with
pointer data members, the three things that we must do are explicitly overload the
assignment operator, include the copy constructor, and include the destructor
...
These functions are used to implement the public
member functions of the class and the user need not know their existence
...
Suppose that you have the
following statement:
binaryTreeType myTree;

The following statement does an inorder traversal of myTree:
myTree
...

Next, we give the definitions of the member functions of the class binaryTreeType
...
So the definition of the function isEmpty is as
follows:
template
bool binaryTreeType::isEmpty() const
{
return (root == NULL);
}

1
1

612 |

Chapter 11: Binary Trees and B-Trees

The default constructor initializes the binary tree to an empty state; that is, it sets
the pointer root to NULL
...
See Programming Exercises 1 and 2 at the end of this chapter
...

The definition of the function copyTree is the same as before; here this function is a
member of the class binaryTreeType:
template
void binaryTreeType::copyTree
(binaryTreeNode* &copiedTreeRoot,
binaryTreeNode* otherTreeRoot)
{
if (otherTreeRoot == NULL)
copiedTreeRoot = NULL;
else
{
copiedTreeRoot = new binaryTreeNode;
copiedTreeRoot->info = otherTreeRoot->info;
copyTree(copiedTreeRoot->llink, otherTreeRoot->llink);
copyTree(copiedTreeRoot->rlink, otherTreeRoot->rlink);
}
} //end copyTree

To destroy a binary tree, for each node, first we destroy its left subtree, then its right subtree,
and then the node itself
...
The definition of the function destroy is as follows:
template
void binaryTreeType::destroy(binaryTreeNode* &p)
{
if (p != NULL)
{
destroy(p->llink);
destroy(p->rlink);
delete p;
p = NULL;
}
}

To implement the function destroyTree, we use the function destroy and pass the
root node of the binary tree to the function destroy
...
Because the class binaryTreeType has
pointer data members, which creates dynamic memory, we must provide the definition

Binary Tree Traversal

|

615

of the copy constructor to avoid the shallow copying of data
...

//copy constructor
template
binaryTreeType::binaryTreeType
(const binaryTreeType& otherTree)
{
if (otherTree
...
root);
}

The definition of the destructor is quite straightforward
...
The definition of the destructor uses the function destroy to
accomplish this task
...
To assign the value of
one binary tree to another binary tree, we make an identical copy of the binary tree to be
assigned by using the function copyTree
...
root == NULL) //otherTree is empty
root = NULL;
else
copyTree(root, otherTree
...

Consider the binary tree in Figure 11-6
...
To do so, we can
use any of the previous traversal algorithms to visit each node and compare the search
item with the data stored in the node
...
We need to visit each node in the
binary tree until either the item is found or we have traversed the entire binary tree
because no criteria exist to guide our search
...

On the other hand, consider the binary tree in Figure 11-7
...
Suppose that we want to determine
whether 58 is in this binary tree
...
We
compare 58 with the data in the root node; that is, we compare 58 with 60
...

Therefore, if 58 is in the binary tree, it must be in the left subtree of the root node
...
We now apply the
same criteria at this node
...
At this node we find item 58
...
If the binary tree is nicely constructed, the search is
very similar to the binary search on arrays
...
(In the following definition, by the term key of the node we mean the key of the data
item that uniquely identifies the item
...
T has a special node called the root node
...
T has two sets of nodes, LT and RT, called the left subtree and right
subtree of T, respectively
...
The key in the root node is larger than every key in the left subtree
and smaller than every key in the right subtree
...
LT and RT are binary search trees
...

Insert an item in the binary search tree
...

Find the height of the binary search tree
...

Find the number of leaves in the binary search tree
...

Copy the binary search tree
...
The height of a binary search tree is
determined the same way as the height of a binary tree
...
Therefore, we can

1
1

618 |

Chapter 11: Binary Trees and B-Trees

inherit all of these operations from the binary tree
...

The following class defines a binary search tree as an ADT by extending the definition of
the binary tree:
//*************************************************************
// Author: D
...
Malik
//
// This class specifies the basic operations to implement a
// binary search tree
...

//Postcondition: Returns true if searchItem is found in the
//
binary search tree; otherwise, returns false
...

//Postcondition: If no node in the binary search tree has the
//
same info as insertItem, a node with the info insertItem
//
is created and inserted in the binary search tree
...

//Postcondition: If a node with the same info as deleteItem
//
is found, it is deleted from the binary search tree
...

//Postcondition: The node to which p points is deleted from
//
the binary search tree
...


Search
The function search searches the binary search tree for a given item
...
Because the pointer
root points to the root node of the binary search tree, we must begin our search at the
root node
...
The pointer current is
initialized to root
...
If they are the same, we stop the search and return true
...
We repeat this process for the next node
...
Thus, the general algorithm is as follows:
if root is NULL

Cannot search an empty tree, returns false
...
" << endl;
else
{
current = root;
while (current != NULL && !found)
{
if (current->info == searchItem)
found = true;
else if (current->info > searchItem)
current = current->llink;
else
current = current->rlink;
}//end while
}//end else
return found;
}//end search

1
1

620 |

Chapter 11: Binary Trees and B-Trees

Insert
After inserting an item in a binary search tree, the resulting binary tree must also be a binary
search tree
...
The search algorithm is similar to the
search algorithm of the function search
...
Because duplicate items are not allowed,
our search must end at an empty subtree
...
The item to be inserted, insertItem, is passed as a
parameter to the function insert
...
Create a new node and copy insertItem into the new node
...

b
...

else
{
current = root;
while (current is not NULL)

//search the binary tree

{
trailCurrent = current;
if (current->info is the same as the insertItem)

Error: Cannot insert duplicate
exit
else if(current->info > insertItem)
Follow llink of current
else

Follow rlink of current
}
//insert the new node in the binary tree
if (trailCurrent->info > insertItem)

make the new node the left child of trailCurrent
else

make the new node the right child of trailCurrent
}

This pseudocode algorithm translates into the following C++ function:
template
void bSearchTreeType::insert(const elemType& insertItem)
{
binaryTreeNode *current; //pointer to traverse the tree
binaryTreeNode *trailCurrent; //pointer behind current
binaryTreeNode *newNode; //pointer to create the node

Binary Search Trees

|

621

newNode = new binaryTreeNode;
assert(newNode != NULL);
newNode->info = insertItem;
newNode->llink = NULL;
newNode->rlink = NULL;
if (root == NULL)
root = newNode;
else
{
current = root;
while (current != NULL)
{
trailCurrent = current;
if (current->info == insertItem)
{
cerr << "The insert item is already in the list-";
cerr << "duplicates are not allowed
...
To
help you better understand the delete operation, before describing the function to
delete an item from the binary search tree, let us consider the binary search tree
given in Figure 11-8
...
The delete operation has four cases, as follows:
Case 1: The node to be deleted has no left and right subtrees; that is, the node to be
deleted is a leaf
...

Case 2: The node to be deleted has no left subtree; that is, the left subtree is empty, but it
has a nonempty right subtree
...

Case 3: The node to be deleted has no right subtree; that is, the right subtree is empty,
but it has a nonempty left subtree
...

Case 4: The node to be deleted has nonempty left and right subtrees
...


Binary Search Trees

|

623

Figure 11-9 illustrates these four cases
...
We
search the binary tree and arrive at the node containing 45
...
After deleting this node, Figure 11-9(a)
shows the resulting binary search tree
...
In
this case, the node to be deleted has no left subtree
...
Figure 11-9(b) shows the resulting binary tree
...
The
node containing 80 has no right child and is the right child of its parent
...
Figure 11-9(c) shows
the resulting binary tree
...

The node with info 50 has a nonempty left subtree and a nonempty right subtree
...
To be specific, suppose that
we reduce it to Case 3—that is, the node to be deleted has no right subtree
...
This is done by
first going to the left child of 50 and then locating the rightmost node of the left subtree
of 50
...
Because the binary search tree is
finite, we eventually arrive at a node that has no right subtree
...
In this case, we swap
48 with 50
...

We now apply Case 3 to delete the node
...
) After deleting 50 from the binary
search tree in Figure 11-8, the resulting binary tree is as shown in Figure 11-9(d)
...

From this discussion, it follows that to delete an item from a binary search tree, we must
do the following:
1
...

2
...

We accomplish the second step by a separate function, which we will call deleteFromTree
...

The preceding examples show that whenever we delete a node from a binary tree, we adjust
one of the pointers of the parent node
...
For example, suppose that the node to be deleted is 35, which is the right child
of its parent node
...
A call to the function deleteFromTree is as follows:
deleteFromTree(trailCurrent->rlink);

Of course, if the node to be deleted is the root node, then the call to the function
deleteFromTree is as follows:
deleteFromTree(root);

Binary Search Trees

|

625

We now define the C++ function deleteFromTree:
template
void bSearchTreeType::deleteFromTree
(binaryTreeNode* &p)
{
binaryTreeNode *current;//pointer to traverse the tree
binaryTreeNode *trailCurrent; //pointer behind current
binaryTreeNode *temp;
//pointer to delete the node
if (p == NULL)
cerr << "Error: The node to be deleted is NULL
...
The function deleteNode first searches
the binary search tree to find the node containing the item to be deleted
...
If the node containing
deleteItem is found in the binary search tree, the function deleteNode calls the
function deleteFromTree to delete the node
...

template
void bSearchTreeType::deleteNode(const elemType& deleteItem)
{
binaryTreeNode *current; //pointer to traverse the tree
binaryTreeNode *trailCurrent; //pointer behind current
bool found = false;
if (root == NULL)
cout << "Cannot delete from the empty tree
...
" << endl;
else if (found)
{
if (current == root)
deleteFromTree(root);
else if (trailCurrent->info > deleteItem)
deleteFromTree(trailCurrent->llink);
else
deleteFromTree(trailCurrent->rlink);
}//end if

}
}//end deleteNode

Binary Search Tree: Analysis

|

627

Binary Search Tree: Analysis
This section provides an analysis of the performance of binary search trees
...
Suppose that we want to determine whether an item,
x, is in T
...
Let us first
consider the worst case
...
That is, T is one of the forms shown in
Figure 11-10
...



...



...



...
Therefore, in the successful case, on average, the search algorithm makes
(n + 1) / 2 key comparisons
...

Let us now consider the average-case behavior
...
Because there are n items, there are n! possible orderings of the keys
...
Let S(n) denote the number of
comparisons in the average successful case, and U(n) denote the number of comparisons
in the average unsuccessful case
...
Furthermore, the number of comparisons
required to insert x in T is the same as the number of comparisons made in the unsuccessful
search, reflecting that x is not in T
...
þ Uðn À 1Þ
n

ðEquation 11-1Þ

It is also known that

SðnÞ ¼


1
1 þ UðnÞ À 3
n

ðEquation 11-2Þ

1
1

628 |

Chapter 11: Binary Trees and B-Trees

Solving Equations (11-1) and (11-2), it can be shown that U(n) % 2
...
39log2n
...

Theorem: Let T be a binary search tree with n nodes, where n> 0
...
39log2n ¼ O(log2n) and the number
of key comparisons is approximately 2
...


Nonrecursive Binary Tree Traversal Algorithms
The previous sections described how to do the following:





Traverse a binary tree using the inorder, preorder, and postorder methods
...

Insert an item in a binary tree
...


The traversal algorithms—inorder, preorder, and postorder—discussed earlier are recursive
...


Nonrecursive Inorder Traversal
In the inorder traversal of a binary tree, for each node, the left subtree is visited first, then the
node, and then the right subtree
...
For example, in the binary tree in Figure 11-11, the
leftmost node is the node with info 28
...
We then back up to the parent node, visit the node, and then move to the right
node
...
Moreover, the nodes must be
backtracked in the order they were traversed
...
This can be done by using a stack
...
The general algorithm is as follows:
1
...
while (current is not NULL or stack is nonempty)
if (current is not NULL)
{

push current into the stack;
current = current->llink;
}
else
{

pop stack into current;
visit current;
//visit the node
current = current->rlink;

//move to the right child

}

The following function implements the nonrecursive inorder traversal of a binary tree:
template
void binaryTreeType::nonRecursiveInTraversal() const
{
stackType* > stack;
binaryTreeNode *current;
current = root;
while ((current != NULL) || (!stack
...
push(current);
current = current->llink;
}
else
{
current = stack
...
pop();
cout << current->info << " ";
current = current->rlink;
}
}

cout << endl;

1
1

630 |

Chapter 11: Binary Trees and B-Trees

Nonrecursive Preorder Traversal
In a preorder traversal of a binary tree, for each node, first the node is visited, then the left
subtree is visited, and then the right subtree is visited
...

The general algorithm is as follows:
1
...
while (current is not NULL or stack is nonempty)
if (current is not NULL)
{
visit current node;
push current into stack;
current = current->llink;
}
else

{
pop stack into current;
current = current->rlink; //prepare to visit the
//right subtree

}
The following function implements the nonrecursive preorder traversal algorithm:
template
void binaryTreeType::nonRecursivePreTraversal() const
{
stackType*> stack;
binaryTreeNode *current;
current = root;
while ((current != NULL) || (!stack
...
push(current);
current = current->llink;
}
else
{
current = stack
...
pop();
current = current->rlink;
}
}

cout << endl;

Nonrecursive Binary Tree Traversal Algorithms |

631

Nonrecursive Postorder Traversal
In a postorder traversal of a binary tree, for each node, first the left subtree is visited, then the
right subtree is visited, and then the node is visited
...
Because—for
each node—the left and right subtrees are visited before visiting the node, we must indicate
to the node whether the left and right subtrees have been visited
...
Therefore, after
returning from a left subtree, we must tell the node that the right subtree needs to be visited,
and after visiting the right subtree we must tell the node that it can now be visited
...
Whenever the stack is popped, the integer
value associated with that pointer is popped as well
...

The general algorithm is as follows:
1
...
v = 0;
3
...
if (current is not NULL)
a
...
push 1 into stack;
c
...
while (stack is not empty)
if (current is not NULL and v is 0)
{

push current and 1 into stack;
current = current->llink;
}
else
{
pop stack into current and v;
if (v == 1)
{
push current and 2 into stack;
current = current->rlink;
v = 0;
}
else
visit current;
}

We use two (parallel) stacks: one to save a pointer to a node and another to save the
integer value (1 or 2) associated with this pointer
...


Binary Tree Traversal and Functions
as Parameters
Suppose that you have stored employee data in a binary search tree, and at the end of the
year pay increases or bonuses are to be awarded to each employee
...
The preceding sections discussed various ways to traverse a binary tree
...
How do we use a traversal algorithm to visit each node and update the data in each
node? One way to do so is to first create another binary search tree in which the data in
each node is the updated data of the original binary search tree, and then destroy the old
binary search tree
...
Another solution is to write separate traversal algorithms to
update the data
...
However, if the user can write an appropriate
function to update the data of each employee and then pass this function as a parameter
to the traversal algorithms, we can considerably enhance the program’s flexibility
...

In C++, a function name without any parentheses is considered a pointer to the function
...
For example, consider the following statements:
void fParamFunc1(void (*visit) (int));
void fParamFunc2(void (*visit) (elemType&));

//Line 1
//Line 2

The statement in Line 1 declares fParamFunc1 to be a function that takes as a parameter
any void function that has one value parameter of type int
...

We can now rewrite the inorder traversal function of the class binaryTreeType
...
To further illustrate
function overloading, we will overload the inorder traversal functions
...

//The parameter visit, which is a function, specifies the
//action to be taken at each node
...

//The parameter visit, which is a function, specifies the
//action to be taken at each node
...

Example 11-3 further illustrates how functions are passed as parameters to other functions
...
For illustration purposes, we show how to use only the inorder
traversal function
...
The traversal functions are included in
the class binaryTreeType, which are then inherited by the class bSearchTreeType
...
S
...

//**************************************************************

1
1

634 |

Chapter 11: Binary Trees and B-Trees

#include
#include "binarySearchTree
...
insert(num);
cin >> num;
}

//Line
//Line
//Line
//Line
//Line

12
13
14
15
16

cout << endl << "Line 17: Tree nodes in inorder: "; //Line 17
treeRoot
...
treeHeight()
<< endl << endl;
//Line 19
cout << "Line 20: ******* Update Nodes *******"
<< endl;
treeRoot
...
inorderTraversal(print);
cout << endl <<"Line 24: Tree Height: "
<< treeRoot
...

Line 10: Enter numbers ending with -999
56 87 23 65 34 45 12 90 66 -999
Line 17: Tree nodes in inorder: 12 23 34 45 56 65 66 87 90
Line 19: Tree Height: 4
Line 20: ******* Update Notes *******
Line 22: Tree nodes in inorder after the update:
24 46 68 90 112 130 132 174 180
Line 24: Tree Height: 4

This program works as follows
...
The statements in Lines
11 through 16 build the binary search tree
...

The parameter to the function inorderTraversal, in Line 18, is the function print
(defined at Line 27)
...

The statement in Line 19 outputs the height of the binary search tree
...
In Line 21, the actual parameter of the function inorderTraversal
is the function update (defined at Line 31)
...
Therefore, the statement in Line 21 updates the data of each node of the binary
search tree by doubling the value
...


AVL (Height-Balanced) Trees
In the previous sections, you learned how to build and manipulate a binary search tree
...
The shape of the binary search tree depends on the data set
...
On the other
hand, if the tree is nicely built, the search would be fast
...
Therefore, we want the height of the binary search tree to be as small
as possible
...
AVL
´
´
trees are due to the mathematicians G
...
Adelson-Velskii and E
...
Landis and are so named
in their honor
...

We begin by defining the following terms
...
The heights of the left and right subtrees of the root are equal
...
The left and right subtrees of the root are perfectly balanced binary trees
...


FIGURE 11-12

Perfectly balanced binary tree

Let T be a binary tree and x be a node in T
...

It can be proved that, if T is a perfectly balanced binary tree of height h, then the number of
nodes in T is 2h À1
...
Moreover, perfectly balanced binary trees are a too stringent refinement
...
The heights of the left and right subtrees of the root differ by at most 1
...
The left and right subtrees of the root are AVL trees
...


(b) Non-AVL trees

(a) AVL trees

FIGURE 11-13

AVL and non-AVL trees

Let x be a node in a binary tree
...


AVL (Height-Balanced) Trees

Proposition: Let T be an AVL tree and x be a node in T
...


|

637

1, where

Let x be a node in the AVL tree T
...
If xl > xh, we say that x is left high
...

2
...

3
...
In this case, xh = xl + 1
...

Let x be a node in the AVL tree T
...
If x is left high, bf(x) ¼ À1
...
If x is equal high, bf(x) ¼ 0
...
If x is right high, bf(x) ¼ 1
...
We say that the node x violates the
balance criteria if |xhÀ xl| > 1, that is, the heights of the left and right subtrees of x
differ by more than 1
...
Thus, every node must keep track of its balance factor
...
Hence, the definition of a node in the AVL tree is as follows:
template
struct AVLNode
{
elemType info;
int bfactor; //balance factor
AVLNode *llink;
AVLNode *rlink;
};

Because an AVL tree is a binary search tree, the search algorithm for an AVL tree is the
same as the search algorithm for a binary search tree
...
However, item insertion and deletion operations on AVL
trees are somewhat different from the ones discussed for binary search trees
...
Next, we describe these operations
...
Because an AVL tree is a binary search tree, to find the place

1
1

638 |

Chapter 11: Binary Trees and B-Trees

for the new item we can search the AVL tree using a search algorithm similar to the
search algorithm designed for binary search trees
...
Because duplicates are not allowed, in
this case we can output an appropriate error message
...
Then, the search ends at an empty subtree and we insert
the item in that subtree
...
Thus, we must restore the tree’s balance criteria
...
The nodes on this path (back to the
root node) are visited and either their balance factor is changed, or we might have to
reconstruct part of the tree
...

In Figures 11-14 to 11-17, for each node, we show only the data stored in the node
...


Consider the AVL tree of Figure 11-14 (a)
...


>
50
=
40

Insert 90
=
40

>
70

Balance
criteria
violated
here

>
50
>
70

=
80

80

=
=
90

(a)

FIGURE 11-14

(b)

>
50
=
40

=
80
=
70

=
90
(c)

AVL tree before and after inserting 90

We search the tree starting at the root node to find the place for 90
...
We insert a node with info 90 and obtain the tree of Figure 11-14(b)
...
So we backtrack and go to node
80
...
Because the new node was inserted into the
(empty) right subtree of 80, we change its balance factor to 1 (not shown in the figure)
...
Prior to insertion, bf(70) was 1
...
In this case, we reconstruct this subtree (this is called rotating the
tree at root node 70)
...
The

binary tree of Figure 11-14 is an AVL tree
...
Let us insert 75 into the AVL tree of
Figure 11-15(a)
...
The dotted arrows show the
path traversed
...

After inserting 75, we backtrack
...
The subtree with root node 80 is an AVL tree
...

Clearly, the subtree with root node 70 is not an AVL tree
...
In this case, we first reconstruct the subtree at root node 80, and then
reconstruct the subtree at root node 70 to obtain the tree shown in Figure 11-15(c)
...
’’)
Notice that in Figures 11-14(c) and 11-15(c), after reconstructing the subtree at the node,
the subtree no longer grew in height
...


1
1

640 |

Chapter 11: Binary Trees and B-Trees

Next, consider the AVL tree of Figure 11-16
...

>
50

Insert 95

=
40
=
30

=
75
<
60

=
35

=
55

=
40

=
80
=
78

Balance criteria
violated at this
node

>
50

=
30

=
90

=
50

=
75
=
35

<
60

80
=
78

(a)

FIGURE 11-16

90

=

=
30

<
60
=
45

=
95

(b)

>
80

=
40

=

=
55

=
75

=
78

>
90

=
55

=
95

(c)

AVL tree before and after inserting 95

We search the tree and insert 95, as shown in Figure 11-16(b)
...
When backtracking the path,
we simply adjust the balance factor of these nodes (if needed)
...
Prior to
insertion, bf(50) was 1, that is, its right subtree was higher than its left subtree
...
So we construct
the tree at node 50
...

Before discussing the general algorithms for reconstructing (rotating) a subtree, let us consider
one more case
...
Let us insert 88 in this tree
...
As before, we now backtrack to the root node
...
When we visit node 80, we discover that at this node we
need to reconstruct the subtree
...
As before, after reconstructing the subtree, the entire tree is balanced
...

The examples described previously indicate that if part of the tree requires reconstruction,
then after reconstructing that part of the tree, we can ignore the remaining nodes on the
path back to the root node
...
) Also, after inserting the node, the
reconstruction can occur at any node on the path back to the root node
...
There are two
types of rotations: left rotation and right rotation
...

If it is a left rotation, then certain nodes from the right subtree of x move to its left subtree; the
root of the right subtree of x becomes the new root of the reconstructed subtree
...

Case 1: Consider Figure 11-18
...
The dotted rectangle
shows an item insertion in T1, causing the height of the subtree T1 to increase by 1
...

We note the following in this tree
...

Every key in subtree T2 is larger than the key in node a
...


1
1

642 |

Chapter 11: Binary Trees and B-Trees

Therefore,
1
...

2
...

3
...

Case 2: This case is a mirror image of Case 1
...


root
a

b

p Rotate left at
root (node a)
a

b
h T1

T3 h
h T2

FIGURE 11-19

T3 h

h T1

h T2

Double rotation
at root (node c)

b

Left rotation at a

Case 3: Consider Figure 11-20
...
The
heights of the subtrees are shown in the figure
...
The new
item is inserted either in T2 or T3
...

All keys in T3 are larger than the key in node b
...

All keys in T2 are larger than the key in node a
...

The balance criteria is violated at the root node, c, of the tree
...


This is an example of double rotation
...
If the balance factor of the node, where the tree is to be
reconstructed, and the balance factor of the higher subtree are opposite, that node
requires a double rotation
...
Now
the tree at node a is right high and so we make a left rotation at a
...
Figure 11-20 shows the resulting tree
(which is to the right of the tree after insertion)
...


root

root
w

c

p

Rotate
right
at root

c

b

p
a

Rotate
left
at p

w

a

b

1
1

c

a

b
T4

T3

T1
T2

FIGURE 11-21

T3

T1

T4

T2

Left rotation at a followed by a right rotation at c

T1

T2

T3

T4

644 |

Chapter 11: Binary Trees and B-Trees

Case 4: This is a mirror image of Case 3
...


root
a
p

b

Double
rotation
at root
a

c

c

w
b
h T1

h
T4 h

T1

T 2 h-1 T 3

T4 h

T 2 h-1 T 3

FIGURE 11-22

Double rotation: First rotate right at c, then rotate left at a

Using these four cases, we now describe what type of rotation might be required at a node
...
Then, the subtree
with root node x requires either a single or a double rotation
...
Suppose that the balance factor of the node x and the balance factor of
the root node of the higher subtree of x have the same sign, that is, both
positive or both negative
...

(Prior to insertion, the right subtree of x was higher than its left
subtree
...
)
b
...

(Prior to insertion, the left right subtree of x was higher than its
right subtree
...
)
2
...
To be specific, suppose that the
balance factor of node x prior to insertion was À1 and suppose that y is
the root node of the left subtree of x
...
That is, after insertion, the right subtree of node y grew in
height
...
First, we make a left
rotation at y (because y is right high)
...

The other case, which is a mirror image of this case, is handled similarly
...


AVL (Height-Balanced) Trees

|

645

The following C++ functions implement the left and right rotations of a node
...

template
void rotateToLeft(AVLNode* &root)
{
AVLNode *p; //pointer to the root of the
//right subtree of root
if (root == NULL)
cerr << "Error in the tree" << endl;
else if (root->rlink == NULL)
cerr << "Error in the tree:"
<<" No right subtree to rotate
...
" << endl;
else
{
p = root->llink;
root->llink = p->rlink; //the right subtree of p becomes
//the left subtree of root
p->rlink = root;
root = p;
//make p the new root node
}
}//end rotateRight

Now that we know how to implement both rotations, we next write the C++ functions,
balanceFromLeft and balanceFromRight, which are used to reconstruct the tree at a
particular node
...
These functions use the functions rotateToLeft and
rotateToRight to reconstruct the tree, and also adjust the balance factors of the nodes
affected by the reconstruction
...
The
function balanceFromRight has similar conventions
...
" << endl;
break;
case 1:
w = p->rlink;
switch (w->bfactor) //adjust the balance factors
{
case -1:
root->bfactor = 1;
p->bfactor = 0;
break;
case 0:
root->bfactor = 0;
p->bfactor = 0;
break;
case 1:
root->bfactor = 0;
p->bfactor = -1;
}//end switch
w->bfactor = 0;
rotateToLeft(p);
root->llink = p;
rotateToRight(root);
}//end switch;
}//end balanceFromLeft

AVL (Height-Balanced) Trees

|

647

For the sake of completeness, we also give the definition of the function balanceFromRight:
template
void balanceFromRight(AVLNode* &root)
{
AVLNode *p;
AVLNode *w;
p = root->rlink;

//p points to the left subtree of root

switch (p->bfactor)
{
case -1:
w = p->llink;
switch (w->bfactor) //adjust the balance factors
{
case -1:
root->bfactor = 0;
p->bfactor = 1;
break;
case 0:
root->bfactor = 0;
p->bfactor = 0;
break;
case 1:
root->bfactor = -1;
p->bfactor = 0;
}//end switch
w->bfactor = 0;
rotateToRight(p);
root->rlink = p;
rotateToLeft(root);
break;
case 0:
cerr << "Error: Cannot balance from the left
...
The function insertIntoAVL
inserts the new item in the AVL tree
...

The following steps describe the function insertIntoAVL:
1
...

3
...


Create a node and copy the item to be inserted into the newly created node
...

Insert the new node in the tree
...
If necessary, adjust the balance factors
of the nodes, or reconstruct the tree at a node on the path
...
(Recall that recursion automatically takes
care of the backtracking
...
The function insertIntoAVL also
uses a reference bool parameter, isTaller, to indicate to the parent whether the subtree
grew in height or not
...
" << endl;
else if (root->info > newNode->info) //newItem goes in
//the left subtree
{
insertIntoAVL(root->llink, newNode, isTaller);
if (isTaller) //after insertion, the subtree grew in height
switch (root->bfactor)
{
case -1:
balanceFromLeft(root);
isTaller = false;
break;
case 0:
root->bfactor = -1;
isTaller = true;
break;

AVL (Height-Balanced) Trees

|

649

case 1:
root->bfactor = 0;
isTaller = false;
}//end switch
}//end if
else
{
insertIntoAVL(root->rlink, newNode, isTaller);
if (isTaller) //after insertion, the subtree grew in
//height
switch (root->bfactor)
{
case -1:
root->bfactor = 0;
isTaller = false;
break;
case 0:
root->bfactor = 1;
isTaller = true;
break;
case 1:
balanceFromRight(root);
isTaller = false;
}//end switch
}//end else
}//insertIntoAVL

Next, we illustrate the insertIntoAVL function and build an AVL tree from scratch
...
Each figure shows the item to be inserted as well as the
balance factor of each node
...


1
1

650 |

Chapter 11: Binary Trees and B-Trees

Figure 11-23 shows how items are inserted into an initially empty AVL tree
...
Next, we insert 30 into the AVL tree
...

After insertion, the balance factor of node 40 is –1; see Figure 11-23(b)
...
See Figure 11-23(c)
...
The tree is reconstructed at node 40 by making a single
right rotation
...
See Figure 11-23(d)
...

Next, we insert 50
...
The insertion of 50 requires the tree to be
reconstructed at 40
...

Next, we insert 80; see Figure 11-23(f)
...

Next, we insert 15; see Figure 11-23(g)
...
We need to only adjust the balance factor of nodes
20, 30, and 50
...
The insertion of node 28 also does not require
any part of the tree to be reconstructed
...

Next, we insert 25
...
Figure 11-23(i)
shows both rotations in sequence
...

The following function creates a node, stores the info in the node, and calls the function
insertIntoAVL to insert the new node in the AVL tree:
template
void insert(const elemT &newItem)
{
bool isTaller = false;
AVLNode *newNode;
newNode = new AVLNode;
newNode->info = newItem;
newNode->bfactor = 0;
newNode->llink = NULL;
newNode->rlink = NULL;
}

insertIntoAVL(root, newNode, isTaller);

We leave it as an exercise for you to design the class to implement AVL trees as an ADT
...
)

1
1

652 |

Chapter 11: Binary Trees and B-Trees

Deletion from AVL Trees
To delete an item from an AVL tree, first we find the node containing the item to be
deleted
...

Case 2: The node to be deleted has no right child, that is, its right subtree is empty
...

Case 4: The node to be deleted has a left child and a right child
...
Let us first discuss Case 4
...
As in the case of
deletion from a binary search tree, we reduce Case 4 to Case 2
...
Then, the data of y is copied into x and now the node
to be deleted is y
...

To delete the node, we adjust one of the pointers of the parent node
...
As in the case of insertion into
an AVL tree, we traverse the path (from the parent node) back to the root node
...
The following steps describe
what to do to a node on the path back to the root node
...
) Let p be a node on the path back to the root node
...

1
...
The variable shorter is set to false
...
Suppose that the balance factor of p is not equal and the taller subtree of
p is shortened
...

3
...
Further suppose that q points to the root of the taller
subtree of p
...

b
...

c
...
A double
rotation is required at p (a single rotation at q and then a single
rotation at p)
...


a
...
Let Th be an AVL tree of height h such that
Th has the fewest number of nodes
...
Then
jTh j ¼ jThl j þ jThr j þ 1;
where |Th| denotes the number of nodes in Th
...

To be specific, suppose Thl is of height h À 1 and Thr is of height h À 2
...
Similarly, Thr is an AVL
tree of height h À 2 that has the fewest number of nodes among all AVL trees of height
h À 2
...
Hence,
jTh j ¼ jThÀ1 j þ jThÀ2 j þ 1:
Clearly,
jT0 j ¼ 1
jT1 j ¼ 2
Let Fh+2 ¼ |Th | + 1
...
The solution to Fh is given by
pffiffiffi
h
1þ 5
Fh % pffiffiffi ; where  ¼
:
2
5
Hence,
pffiffiffi!hþ2
hþ2
1 1þ 5
:
jTh j % pffiffiffi ¼ pffiffiffi
2
5
5
From this, it can be concluded that
h % ð1:44Þ log2 jTh j:

1
1

654 |

Chapter 11: Binary Trees and B-Trees

This implies that, in the worst case, the height of an AVL tree with n nodes is
approximately (1
...
Because the height of a perfectly balanced binary tree with
n nodes is log2n, it follows that, in the worst case, the time to manipulate an AVL tree is
no more than 44% of the optimum time
...
It can be shown that the average search time of an AVL tree is about
4% more than the optimum
...
That program used an (unordered) linked list to keep track of the
video inventory in the store
...
In this
chapter, you learned how to organize data into a binary tree
...
Moreover, in general, item insertion and deletion in a binary search
tree is faster than in a linked list
...
As in Chapter 5, we
leave the design of the customer list in a binary tree as an exercise for you
...
Because
the linked list was unordered, to see whether a particular video was in stock, the
sequential search algorithm used the equality operator for comparison
...
We, therefore, overload all of the relational operators
...
However, we give
its definition, without the documentation, here for easy reference and for the sake of
completeness
...
Because here we are overloading all of the relational operators, we give
only the definitions of these member functions
...

bool videoType::operator==(const videoType& right) const
{
return (videoTitle == right
...
videoTitle);
}
bool videoType::operator <(const videoType& right) const
{
return (videoTitle < right
...
videoTitle);
}
bool videoType::operator >(const videoType& right) const
{
return (videoTitle > right
...
videoTitle);
}
VIDEO LIST

The video list is maintained in a binary search tree
...
The definition of the
class videoBinaryTree is as follows:
#include
#include
#include
#include



"binarySearchTree
...
h"

using namespace std;
class videoBinaryTree:public bSearchTreeType
{
public:
bool videoSearch(string title);
//Function to search the list to see whether a particular
//title, specified by the parameter title, is in stock
...

bool isVideoAvailable(string title);
//Function to determine whether at least one copy of a
//particular video is in stock
...

void videoCheckOut(string title);
//Function to check out a video, that is, rent a video
...

void videoCheckIn(string title);
//Function to check in a video returned by a customer
...

bool videoCheckTitle(string title);
//Function to determine whether a particular video is in
//stock
...


Programming Example: Video Store (Revisited) |

657

void videoUpdateInStock(string title, int num);
//Function to update the number of copies of a video by
//adding the value of the parameter num
...

//Postcondition: copiesInStock = copiesInStock + num
void videoSetCopiesInStock(string title, int num);
//Function to reset the number of copies of a video
...

//Postcondition: copiesInStock = num
void videoPrintTitle();
//Function to print the titles of all the videos in stock
...

//Postcondition: If the video is found, the parameter found
//
is set to true, false otherwise
...


};

void inorderTitle(binaryTreeNode *p);
//Function to print the titles of all the videos in stock
...
We only give the definitions of the functions searchVideoList,
inorderTitle, and videoPrintTitle
...
)
The function searchVideoList uses a search algorithm similar to the search
algorithm for a binary search tree given earlier in this chapter
...
It also returns a pointer to the node containing the
search item
...
So the user cannot directly use this function in a program
...
The function searchVideoList is used only to
implement other functions of the class videoBinaryTree
...
setVideoInfo(title, "", "", "", "", "", 0);
if (root == NULL) //the tree is empty
cout << "Cannot search an empty list
...
Notice that this function outputs only the video titles
...
printTitle();
inorderTitle(p->rlink);
}
}

The function videoPrintTitle uses the function inorderTitle to print the titles
of all the videos in the store
...
Here we give only the listing of this
program
...
h, and so on
...
S
...

//****************************************************************
#include
#include
#include
#include
#include
#include




"binarySearchTree
...
h"
"videoBinaryTree
...
open("videoDat
...
close();
displayMenu();
cout << "Enter your choice: ";
cin >> choice;
//get the request
cin
...
videoSearch(title))
cout << "Title found
...
"
<< endl;
break;
case 2:
cout << "Enter the title: ";
getline(cin, title);
cout << endl;
if (videoList
...
isVideoAvailable(title))
{
videoList
...
"
<< endl;
}
else
cout << "The video is not in the store
...
videoSearch(title))
{
videoList
...
" << endl;
break;
case 4:
cout << "Enter the title: ";
getline(cin, title);
cout << endl;

Programming Example: Video Store (Revisited) |

661

if (videoList
...
isVideoAvailable(title))
cout << "The video is currently in stock
...
" << endl;
}
else
cout << "The video is not in the store
...
videoPrintTitle();
break;
case 6:
videoList
...
" << endl;
}//end switch
displayMenu();
cout << "Enter your choice: ";
cin >> choice; //get the next request
cin
...
get(ch);
newVideo
...
insert(newVideo);
getline(infile, title);
}//end while
}//end createVideoList
void displayMenu()
{
cout << "Select one of the following: " << endl;
cout << "1: To check whether a particular video is in "
<< "the store" << endl;
cout << "2: To check out a video" << endl;
cout << "3: To check in a video" << endl;
cout << "4: To see whether a particular video is in stock"
<< endl;
cout << "5: To print the titles of all the videos" << endl;
cout << "6: To print a list of all the videos" << endl;
cout << "9: To exit" << endl;
}

B-Trees
In the previous sections of this chapter, we discussed how to build binary search trees and
in particular AVL trees to effectively organize data dynamically and effectively search the
data
...
In this
section, we discuss B-trees in which the leaves are on the same level and are not too far
from the root
...
Each node has the following form:
n

P0

K1

P1

K2

K2


...
, Pn are pointers to the subtrees of the node, K1,
K2,
...
< Kn, and n m – 1
...
All keys, if any, in the node to which Pi points are less than Ki + 1
...
All keys, if any, in the node to which Pi points are greater than Ki
...
The subtrees, if any, to which each Pi points are m-way search trees
...


80

40

8

FIGURE 11-24

16

50

90

45 48

81

85

125

92 95

130 155

A 5-way search tree

Definition: (B-tree of order m) A B-tree of order m is an m-way search tree that is
either empty, or has the following properties:
1
...

2
...
(Note that Øm/2ø denotes the ceiling of m/2
...
The root has at least 2 children if it is not a leaf, and at most m children
...


75

35

2

FIGURE 11-25

19

28

40

55

50

82

65 68

78

80

110

90 95

120 170

A B-tree of order 5

Note that the 5-way search tree in Figure 11-24 is not a B-tree of order 5
...
In the remainder of this section, we
discuss how to implement some of these operations
...
Just like types (data types), constant expressions

1
1

664 |

Chapter 11: Binary Trees and B-Trees

can also be passed as parameters to templates
...


...

private:
int maxSize;
int length;
elemType listElem[size];
};

This class template contains an array data member
...
To create a list of 100
components of int elements, we use the following statement:
listType intList;

Next, we give the definitions of the B-tree node and the class that implements the
properties of a B-tree
...
We use an array to store the records and an array to store the pointers to the
subtrees
...
The following class implements the
basic properties of a B-tree as an ADT:
//*************************************************************
// Author: D
...
Malik
//
// class bTree
// This class specifies the basic operations to implement a
// B-tree
...

//Postcondition: Returns true if searchItem is found in the
//
B-tree; otherwise, returns false
...

//Postcondition: If insertItem is not in the the B-tree, it
//
is inserted in the B-tree
...

bTree();
//constructor
//Add additional members as needed
...
If the item is found in the
binary search tree, it returns true; otherwise, it returns false
...
Because, usually, there is more than one item in a node, we must search the array
containing the data
...
If item is found, the function searchNode
returns true and the location in the array where item is found
...
The definitions of these functions are as follows:
template
void bTree::searchNode
(bTreeNode *current,
const recType& item,
bool& found, int& location)
{
location = 0;
while (location < current->recCount
&& item > current->list[location])
location++;
if (location < current->recCount
&& item == current->list[location])
found = true;
else
found = false;
} //end searchNode

1
1

666 |

Chapter 11: Binary Trees and B-Trees

template
bool bTree::search(const recType& searchItem)
{
bool found = false;
int location;
bTreeNode *current;
current = root;
while (current != NULL && !found)
{
searchNode(current, item, found, location);

}

if (!found)
current = current->children[location];

return found;
} //end search

Note that the function searchNode searches the node sequentially
...
We leave it as an exercise for you to modify the definition of the function
searchNode so that it uses a binary search algorithm to search the node; see Programming Exercise 16 at the end of this chapter
...
We only discuss the inorder traversal algorithm and leave the others as an exercise
...

Insert Algorithm: Search the tree to see if the key is already in the tree
...
If the key is not in the tree, the search terminates at a
leaf
...
If the leaf is full, split the node into
two nodes and the median key is moved to the parent node
...
) The
splitting can propogate upward even as far as the root, causing the tree to increase in height
...

Figures 11-26 to 11-29 shows the insertion of items into an initially empty B-tree of
order 5
...
The insertion of 16
requires to split the root node, causing the height of the tree to increase; see Figure 11-26(b)
...
The next item inserted is 100; see
Figure 11-26(d)
...

However, the right child of the root node is full
...
Because the parent node is not full, we can
insert 82 in the parent node; see Figure 11-26(d)
...


Insert:
73, 54,
98

Insert:
37

30 82

5

95 98 100

40 54 70 73

5 16

16

30 54 82

37 40

95 98 100

(b)

(a)

FIGURE 11-27

70 73

Insertion of 73, 54, 98, and 37

Note that 73, 54, and 98 are inserted without splitting any node; see Figure 11-27(a)
...
The item 37 is to be
inserted in the right child of 30
...
So we split the right child of 30, insert 37, and move the median key,
which is 54, to the parent node
...

Figure 11-28 shows the insertion of 25, 62, 81, 150, and 79 into the B-tree of
Figure 11-27(b)
...

However, the insertion of 79 requires the splitting of a node
...
However, the right child of 54 is full; see Figure 11-28(a)
...

Because the parent node is not full, the median key 73 is inserted into the parent node; see
Figure 11-28(b)
...


Insert: 200

73

30

5

FIGURE 11-29

16

25

54

37

40

82

62 70

79

81

100

95 98

150

200

Insertion of 200

The item 200 is to be inserted in the right child of 82; see Figure 11-28(b)
...
So we split the right child of 82, insert 200 in the node, and move
the median key, which is 100, to the parent node
...
So we split the parent node and move the
median key, which is 73, to the new root node; see Figure 11-29
...

From the previous discussion, it follows that to implement the insertion algorithm, we
need algorithms to split a node, insert an item into a node, and move the median key to
the parent node
...
To trigger the recursion, we will write another
function, insertBTree
...
After inserting an item,
it returns true if the height of the tree is to be increased
...
The
function insert adjusts the root of the B-tree
...

In pseudocode, the algorithm is as follows:
if (current is NULL)
{
Either the B-tree is empty or the search ends at an empty subtree
...

The function insertNode inserts an item in the node
...
The function has four parameters: current—a pointer to the node in
which to insert the new item, insertItem—the item to be inserted, rightChild—a
pointer to the right subtree of the item to be inserted, and insertPosition—the
position in the array where to insert the item
...
It returns the median key and a pointer to the second half of the node
...

template
void bTree::splitNode
(bTreeNode *current,
const recType& insertItem,
bTreeNode* rightChild,
int insertPosition,
bTreeNode* &rightNode,
recType &median)
{
rightNode = new bTreeNode;
int mid = (bTreeOrder - 1)

/ 2;

if (insertPosition <= mid) //new item goes in the first
//half of the node
{
int index = 0;
int i = mid;

1
1

672 |

Chapter 11: Binary Trees and B-Trees

while (i < bTreeOrder - 1)
{
rightNode->list[index] = current->list[i];
rightNode->children[index + 1] =
current->children[i + 1];
index++;
i++;
}
current->recCount = mid;
insertNode(current, insertItem, rightChild,
insertPosition);
(current->recCount)--;
median = current->list[current->recCount];
rightNode->recCount = index;
rightNode->children[0] =
current->children[current->recCount + 1];

}
else //new item goes in the second half of the node
{
int i = mid + 1;
int index = 0;

while (i < bTreeOrder - 1)
{
rightNode->list[index] = current->list[i];
rightNode->children[index + 1] =
current->children[i + 1];
index++;
i++;
}
current->recCount = mid;
rightNode->recCount = index;
median = current->list[mid];
insertNode(rightNode, insertItem, rightChild,
insertPosition - mid - 1);
rightNode->children[0] =
current->children[current->recCount + 1];

}
} //splitNode

We leave it as an exercise for you to include the functions to insert an item in a B-tree and
the functions to search and traverse a B-tree in the class BTree, and write a program to
perform these operations on a B-tree; see Programming Exercise 15 at the end of this chapter
...
The following cases arise:

B-Trees

|

673

1
...

2
...
If
the node containing the deleteItem is not a leaf, its immediate predecessor (or successor) is in a leaf
...

We consider the cases to delete an item from a leaf
...


If the leaf contains more than the minimum number of keys, delete
the deleteItem from the leaf
...
)
b
...
(Note that the sibling
nodes and the leaf have the same parent node
...


If one of the sibling nodes has more than the minimum number
of keys, move one of the keys from that sibling node to the
parent and one key from the parent to the leaf, and then delete
deleteItem
...
If the adjacent siblings have only the minimum number of keys,
then combine one of the siblings with the leaf and the median
key from the parent
...

Next, we illustrate how the deletion process works
...


60

10

1 3 5

FIGURE 11-30

25

15 18 20

70

30 32

62 68

1
1

90

75 80

92 95

A B-tree of order 5

Let us delete 18 from this B-tree
...


674 |

Chapter 11: Binary Trees and B-Trees

60

10

1 3 5

FIGURE 11-31

25

15 20

70

30 32

62 68

90

75 80

92 95

Deleting 18 from a B-tree of order 5

Next, let us delete 30
...


Before
deleting: 30

60

10 25

1 3 5

15 18 20

70

30 32

62 68

90

75 80

92 95

(a)
After
deleting: 30

60

10 20

1 3 5

15 18

70

25 32

62 68

90

75 80

92 95

(b)

FIGURE 11-32

B-tree before and after deleting 30

The leaf containing 30 has only the minimum number of keys
...
So we move 20 from the adjacent
sibling to the parent node and then move 25 from the parent node to the leaf; see
Figure 11-32(b)
...
Figure 11-33 shows the process of deleting 70
...

Combine it with the
left sibling and the
parent
...
So we swap 70 with its immediate predecessor,
which is 68; see Figure 11-33(b)
...
However, this process does not leave the minimum number of keys in
the parent node, which is 90
...
Note that the deletion of
70 resulted in reducing the height of the B-tree
...


1
1

676 |

Chapter 11: Binary Trees and B-Trees

QUICK REVIEW
1
...

3
...

5
...

7
...

9
...

11
...


A binary tree is either empty or it has a special node called the root node
...

The node of a binary tree has two links in it
...

A node U is called the parent of a node V if there is a branch from U to V
...
, Xn such that (a) X ¼ X0, Xn ¼ Y and (b) Xi-1 is the parent of Xi
for all i ¼ 1, 2,
...
That is, there is a branch from X0 to X1, X1 to
X2,
...
, Xn-1 to Xn
...

The level of the root node of a binary tree is 0; the level of the children of
the root node is 1
...

In an inorder traversal, the binary tree is traversed as follows: (a) Traverse
the left subtree; (b) visit the node; (c) traverse the right subtree
...

In a postorder traversal, the binary tree is traversed as follows: (a) Traverse
the left subtree; (b) traverse the right subtree; (c) visit the node
...

ii
...

iii
...

iv
...

To delete a node from a binary search tree that has both left and right
nonempty subtrees, first its immediate predecessor is located, then the
predecessor’s info is copied into the node, and finally the predecessor is
deleted
...


13
...


i
...


The heights of the left and right subtrees of the root are equal
...


Quick Review |

15
...

ii
...

Let x be a node in a binary tree
...

Let T be an AVL tree and x be a node in T
...

Let x be a node in the AVL tree T
...


16
...

18
...
In this case, xl ¼ xh + 1
...

If xl ¼ xh, we say the x is equal high
...

If xh > xl, we say the x is right high
...

The balance factor of x, written bf(x), is defined as bf(x) ¼ xh À xl
...
Then,
a
...

20
...

b
...

c
...

Let x be a node in a binary tree
...

Every node x in the AVL tree T, in addition to the data and pointers to the
left and right subtrees, must keep track of its balance factor
...
Suppose that the rotation occurs at node, say x
...
Similarly, if it is a right rotation at x, certain nodes from the left
subtree of x move to its right subtree; the root of the left subtree of x
becomes the new root of the reconstructed subtree
...
(Note that Øm/2ø denotes the ceiling of m/2
...

To insert an item into a B-tree, search the tree to see if the record is already
in the tree
...
If
the record is not in the tree, the search terminates at a leaf
...
If the leaf is full, split the node into
two nodes and the median record is moved to the parent node
...

a
...


22
...


24
...


1
1

678 |

Chapter 11: Binary Trees and B-Trees

EXERCISES
1
...


A binary tree must be nonempty
...
The level of the root node is 0
...
If a tree has only one node, the height of this tree is 0 because the
number of levels is 0
...
The inorder traversal of a binary tree always outputs the data in
ascending order
...

There are 14 different binary trees with four nodes
...

The binary tree of Figure 11-34 is to be used for Exercises 3 through 8
...


A
B

C

F

E

G

D

FIGURE 11-34
3
...

5
...

7
...


Binary tree for Exercises 3 through 8

Find LA, the node in the left subtree of A
...

Find RB, the node in the right subtree of B
...

List the nodes of this binary tree in a preorder sequence
...


Exercises

|

679

The binary tree of Figure 11-35 is to be used for Exercises 9 through 13
...

10
...

12
...


14
...


Binary tree for Exercises 9 through 13

List the path from the node with info 80 to the node with info 79
...
List the nodes that are visited
by the function insert to insert 35
...

Delete node 52 and redraw the binary tree
...

Delete nodes 80 and 58 in that order
...

Suppose that you are given two sequences of elements corresponding to the
inorder sequence and the preorder sequence
...

The nodes in a binary tree in preorder and inorder sequences are as follows:
preorder:
inorder:

16
...

Given the preorder sequence and the postorder sequence, show that it
might not be possible to reconstruct the binary tree
...


Chapter 11: Binary Trees and B-Trees

Insert 100 in the AVL tree of Figure 11-36
...
What is the balance factor at the root node after the insertion?

50

30

25

FIGURE 11-36
18
...
The resulting tree must be an
AVL tree
...


AVL tree for Exercise 18

Insert 42 in the AVL tree of Figure 11-38
...
What is the balance factor at the root node after the insertion?

Exercises

|

681

50

30

25
20

FIGURE 11-38
20
...
Show the AVL tree after each insertion
...


35 75

8 20

FIGURE 11-39

37 40 60

90 95 99

B-tree of order 5 for Exercises 21 to 23

Insert the keys 72, 30, and 50, in this order, into the B-tree of order 5 of
Figure 11-39
...

22
...

Show the resulting tree
...
Insert the keys 2, 30, 42, 10, 96, 15, 50, 82, and 98 into the B-tree of
order 5 of Figure 11-39
...

The binary tree of Figure 11-40 is to be used for Exercises 24 to 27
...


50

12 30

2 4 7

FIGURE 11-40

16 22 24

65

35 40

55 60

B-tree of order 5 for Exercises 24 to 27

88

70 85

90 96

1
1

682 |

24
...

26
...

28
...
Show the resulting tree
...
Show the resulting tree
...
Show the resulting tree
...
Show the resulting tree
...

a
...


Insert these keys into an initially empty B-tree of order 5
...


PROGRAMMING EXERCISES
1
...


3
...


5
...


7
...


9
...
Add this function to the class binaryTreeType and
create a program to test this function
...
Add this function to the class binaryTreeType and
create a program to test this function
...
Add this function to the class binaryTreeType and
create a program to test this function
...
Add this function to the class
binaryTreeType and create a program to test this function
...
)
Write a program to test various operations on a binary search tree
...
Write the definition of the function to implement the nonrecursive
postorder traversal algorithm
...
Write a program to test the nonrecursive inorder, preorder, and postorder traversal algorithms
...
)
Write a version of the preorder traversal algorithm in which a user-defined
function can be passed as a parameter to specify the visiting criteria at a node
...

a
...
(You do not need to implement the delete operation
...
Write the definitions of the member functions of the class that you
defined in (a)
...
Write a program to test various operations of an AVL tree
...


11
...
Also write a program to test your function
...

b
...

c
...

d
...

e
...

Write the definitions of the functions of the class videoBinaryTree not
given in the Programming Example Video Store
...

(Video Store Program) In Programming Exercise 14 in Chapter 5, you
were asked to design and implement a class to maintain customer data in a
linked list
...
The class
customerBTreeType must be derived from the class bSearchTreeType
as designed in this chapter
...

Write the definition of the function insertBTree to recursively insert a
record into a B-tree
...

Rewrite the definition of the function searchNode of the class B-tree so
that it uses a binary search to search the node
...

a
...


13
...


15
...


1
1

This page intentionally left blank

12
CHAPTER

G RAPHS
I N T H I S C H A P T E R , YO U W I L L :


...


Become familiar with the basic terminology of graph theory


...


Examine and implement various graph traversal algorithms


...


Examine and implement the minimum spanning tree algorithm


...


Learn how to find Euler circuits in a graph

686 |

Chapter 12: Graphs

In previous chapters, you learned various ways to represent and manipulate data
...


Introduction
In 1736, the following problem was posed
...
See Figure 12-1
...
These land areas are
connected using seven bridges, as shown in Figure 12-1
...
The Konigsberg bridge problem is as follows: Starting at one land area, is it
¨
possible to walk across all the bridges exactly once and return to the starting land area?
In 1736, Euler represented the Konigsberg bridge problem as a graph, as shown in
¨
Figure 12-2, and answered the question in the negative
...


C
c

g

d
e

A
a

D

b
f
B

FIGURE 12-2

Graph representation of the Konigsberg bridge problem
¨

Graph Definitions and Notations

|

687

Over the past 200 years, graph theory has been applied to a variety of applications
...

They are also used in the analysis of electrical circuits, finding the shortest route, project
planning, linguistics, genetics, social science, and so forth
...


Graph Definitions and Notations
To facilitate and simplify our discussion, we borrow a few definitions and terminology
from set theory
...
If a is an element of X, we write a ˛ X
...
’’) A set Y is called a subset of X if every element of Y is also an
element of X
...
(The symbol ‘‘˝’’ means ‘‘is a subset
of
...
(The symbol ‘‘˙’’ means
‘‘intersection
...
(The symbol ‘‘¨’’ means
‘‘union
...
(The symbol ‘‘Â’’ means ‘‘Cartesian
product
...
That is, the elements of E are pairs of elements of V
...
G is called trivial if it has only one vertex
...
If the
elements of E are ordered pairs, G is called a directed graph or digraph; otherwise, G is
called an undirected graph
...

Let G be a graph
...

A graph can be shown pictorially
...
In an undirected graph, the edges are drawn using lines
...


1
2

688 |

Chapter 12: Graphs

EXAMPLE 12-1
Figure 12-3 shows some examples of undirected graphs
...


1

2

3

4

0

1

5
G1

3

G2

4

2

0

5

2

4

3
7

6

FIGURE 12-4

1

8

9

G3

10

Various directed graphs

For the graphs of Figure 12-4, we have
V(G1) = {1, 2, 3, 4, 5}
V(G2) = {0, 1, 2, 3, 4}
V(G3) = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

E(G1) = {(1, 2), (1, 4), (2, 5), (3, 1), (3, 4), (4, 5)}
E(G2) = {(0, 1), (0, 3), (1, 2), (1, 4), (2, 1), (2, 4), (4, 3)}
E(G3) = {(0, 1), (0, 5), (1, 2), (1, 3), (1, 5), (2, 4), (4, 3),
(5, 6), (6, 8), (7, 3), (7, 8), (8, 10), (9, 4),
(9, 7), (9, 10)}

Graph Representation

|

689

Let G be an undirected graph
...
Then u and v are called
adjacent if there is an edge from one to the other; that is, (u, v) ˛ E
...
If two edges, e1 and e2, are associated with the same pair of
vertices {u, v}, then e1 and e2 are called parallel edges
...
Let e ¼ (u, v) be an edge in G
...
The degree of u, written deg(u) or d(u), is the
number of edges incident with u
...
u is called an even (odd) degree vertex if the degree of
u is even (odd)
...
, un
such that u ¼ u1, un ¼ v and (ui, ui+ 1) is an edge for all i ¼ 1, 2,
...
Vertices u and v
are called connected if there is a path from u to v
...
A cycle in G is a simple
path in which the first and last vertices are the same
...
A maximal subset of connected vertices is called
a component of G
...
If there is an edge from u
to v, that is, (u, v) ˛ E, we say that u is adjacent to v and v is adjacent from u
...
G is
called strongly connected if any two vertices in G are connected
...
In G1, 1-4-5 is a path from vertex 1 to
vertex 5
...
In G2, 1-2-1 is a cycle
...
There are no
cycles in G3
...
A graph can be represented (in computer
memory) in several ways
...


Adjacency Matrices
Let G be a graph with n vertices, where n > 0
...
, vn}
...
That is,
&
AG ði; jÞ ¼

1 ifðvi ; vj Þ 2 EðGÞ
0 otherwise

In an undirected graph, if (vi, vj) ˛ E(G), then (vj, vi) ˛ E(G), so AG(i, j) ¼ 1 ¼ AG( j, i)
...


1
2

690 |

Chapter 12: Graphs

EXAMPLE 12-3
Consider the directed graphs of Figure 12-4
...
Let V(G) ¼ {v1, v2,
...
In the
adjacency list representation, corresponding to each vertex, v, there is a linked list such
that each node of the linked list contains the vertex, u, such that (v, u) ˛ E(G)
...

Clearly, each node has two components, say vertex and link
...

EXAMPLE 12-4
Consider the directed graphs of Figure 12-4
...


[0]

1

3

[1]

2

4

[2]

1

4

[3]
[4]

FIGURE 12-5

3

Adjacency list of graph G2 of Figure 12-4

Operations on Graphs

|

691

Figure 12-6 shows the adjacency list of the directed graph G3
...
The operations commonly performed on a
graph are as follows:
1
...
That is, store the graph in computer memory using a
particular graph representation
...
Clear the graph
...

3
...

4
...

5
...

We will add more operations on a graph when we discuss a specific application or a
particular graph later in this chapter
...
For
illustration purposes, we use the adjacency list (linked list) representation of graphs
...

To manage the data in a linked list, we use the class unorderedLinkedList, discussed
in Chapter 5
...
If you are dealing
with the graph of cities, you could label the vertices by the names of the cities
...
That is, we must specify the first vertex, the second
vertex, and so on
...
, n - 1
...


Graphs as ADTs
In this section, we describe the class to implement graphs as an abstract data type (ADT)
and provide the definitions of the functions to implement the operations on a graph
...
S
...

//****************************************************************
class graphType
{
public:
bool isEmpty() const;
//Function to determine whether the graph is empty
...

void createGraph();
//Function to create a graph
...

void clearGraph();
//Function to clear graph
...

void printGraph() const;
//Function to print graph
...

void depthFirstTraversal();
//Function to perform the depth first traversal of
//the entire graph
...


Graphs as ADTs

|

693

void dftAtVertex(int vertex);
//Function to perform the depth first traversal of
//the graph at a node specified by the parameter vertex
...

void breadthFirstTraversal();
//Function to perform the breadth first traversal of
//the entire graph
...

graphType(int size = 0);
//Constructor
//Postcondition: gSize = 0; maxSize = size;
//
graph is an array of pointers to linked lists
...

protected:
int maxSize;
//maximum number of vertices
int gSize;
//current number of vertices
unorderedLinkedList *graph; //array to create
//adjacency lists
private:
void dft(int v, bool visited[]);
//Function to perform the depth first traversal of
//the graph at a node specified by the parameter vertex
...

//Postcondition: Starting at vertex, the vertices are
//
printed using the depth first traversal algorithm
...

The definitions of the functions of the class graphType are discussed next
...
Therefore, the
definition of the function isEmpty is as follows:
bool graphType::isEmpty() const
{
return (gSize == 0);
}

The definition of the function createGraph depends on how the data is input into
the program
...
The user is prompted for the input file
...
-999
1 3 6 8
...


The first line of input specifies the number of vertices in the graph
...
Each line ends with the number –999
...
open(fileName);
if (!infile)
{
cout << "Cannot open input file
...
insertLast(adjacentVertex);
infile >> adjacentVertex;
} //end while
} // end for
infile
...


Graph Traversals

|

695

void graphType::clearGraph()
{
for (int index = 0; index < gSize; index++)
graph[index]
...
print();
cout << endl;
}
cout << endl;
} //end printGraph

The definitions of the constructor and the destructor are as follows:
//Constructor
graphType::graphType(int size)
{
maxSize = size;
gSize = 0;
graph = new unorderedLinkedList[size];
}
//Destructor
graphType::~graphType()
{
clearGraph();
}

Graph Traversals
Processing a graph requires the ability to traverse the graph
...

Traversing a graph is similar to traversing a binary tree, except that traversing a graph is a
bit more complicated
...
On the other hand, a graph might have cycles and we might
not be able to traverse the entire graph from a single vertex (for example, if the graph is
not connected)
...
We
must also traverse the graph from each vertex (that has not been visited) of the graph
...


1
2

696 |

Chapter 12: Graphs

The two most common graph traversal algorithms are the depth-first traversal and
breadth-first traversal, which are described next
...
Moreover, each vertex is visited only once
...


Depth-First Traversal
The depth-first traversal is similar to the preorder traversal of a binary tree
...
It is shown here again as Figure 12-7 for easy reference
...
After visiting all the
vertices that can be reached starting at the vertex 0, the depth-first search starts at the next
vertex that is not visited
...
Therefore, when the depth-first search starts at the vertex 0, all the vertices
except 7 and 9 are visited before these vertices
...

Note that there is no path from the vertex 7 to the vertex 9
...

The general algorithm to do a depth-first traversal at a given node, v, is as follows:
1
...
visit the node
3
...
We use a recursive function, dft, to implement this
algorithm
...

void graphType::dft(int v, bool visited[])
{
visited[v] = true;
cout << " " << v << " "; //visit the vertex
linkedListIterator graphIt;
//for each vertex adjacent to v
for (graphIt = graph[v]
...
end();
++graphIt)
{
int w = *graphIt;
if (!visited[w])
dft(w, visited);
} //end while
} //end dft

In the preceding code, note that the statement
linkedListIterator graphIt;

declares graphIt to be an iterator
...
Next, let us look at the
statement
int w = *graphIt;

The expression *graphIt returns the label of the vertex, adjacent to the vertex v, to
which graphIt points
...

void graphType::depthFirstTraversal()
{
bool *visited; //pointer to create the array to keep
//track of the visited vertices
visited = new bool[gSize];
for (int index = 0; index < gSize; index++)
visited[index] = false;
//For each vertex that is not visited, do a depth
//first traverssal
for (int index = 0; index < gSize; index++)
if (!visited[index])
dft(index,visited);
delete [] visited;
} //end depthFirstTraversal

1
2

698 |

Chapter 12: Graphs

The function depthFirstTraversal performs a depth-first traversal of the entire graph
...
All the nodes at any level, i, are
visited before visiting the nodes at level i + 1
...
After visiting the vertex 0, we
visit the vertices that are directly connected to it and are not visited, which are 1 and 5
...
After this, we visit the vertices that are directly connected to 5 and are not
visited, which is 6
...

As in the case of the depth-first traversal, because it might not be possible to traverse the
entire graph from a single vertex, the breadth-first traversal also traverses the graph from
each vertex that is not visited
...
To implement the
breadth-first search algorithm, we use a queue
...
for each vertex v in the graph
if v is not visited
add v to the queue //start the breadth first search at v
2
...
while the queue is not empty
3
...
Remove vertex u from the queue
3
...
Retrieve the vertices adjacent to u

Graph Traversals

3
...


|

699

for each vertex w that is adjacent to u
if w is not visited

3
...
1
...
3
...
Mark w as visited
The following C++ function, breadthFirstTraversal, implements this algorithm:
void graphType::breadthFirstTraversal()
{
linkedQueueType queue;
bool *visited;
visited = new bool[gSize];
for (int ind = 0; ind < gSize; ind++)
visited[ind] = false;
//initialize the array
//visited to false
linkedListIterator graphIt;
for (int index = 0; index < gSize; index++)
if (!visited[index])
{
queue
...
isEmptyQueue())
{
int u = queue
...
deleteQueue();

}

for (graphIt = graph[u]
...
end(); ++graphIt)
{
int w = *graphIt;
if (!visited[w])
{
queue
...


1
2

700 |

Chapter 12: Graphs

Shortest Path Algorithm
Graph theory has many applications
...
Graphs also be used to show the highway
structure of a city, state, or country
...
If the graph represents a highway
structure, the weight can represent the distance between two places or the travel time from
one place to another
...

Let G be a weighted graph
...
The weight of the path P is the sum of the weights of all the edges on the
path P, which is also called the weight of v from u via P
...
Suppose that the weight of an
edge represents the travel time
...
Many such problems exist in which we want to find
the shortest path from a given vertex, called the source, to every other vertex in the graph
...

Let G be a graph with n vertices, where n ‡ 0
...
, vn}
...

To make inputting the data easier, we extend the definition of the class graphType
(using inheritance), and add the function createWeightedGraph to create the graph and
the weight matrix associated with the graph
...

The functions to implement the shortest path algorithm will also be added to this class
...
S
...

//****************************************************************
class weightedGraphType: public graphType
{
public:
void createWeightedGraph();
//Function to create the graph and the weight matrix
...


Shortest Path Algorithm

void shortestPath(int vertex);
//Function to determine the weight
//from vertex, that is, source, to
//in the graph
...


void printShortestDistance(int vertex);
//Function to print the shortest weight from the vertex
//specified by the parameter vertex to every other vertex in
//the graph
...

weightedGraphType(int size = 0);
//Constructor
//Postcondition: gSize = 0; maxSize = size;
//
graph is an array of pointers to linked lists
...

//
smallestWeight is an array to store the smallest weight
//
from source to vertices
...

protected:
double **weights;
//pointer to create weight matrix
double *smallestWeight; //pointer to create the array to store
//the smallest weight from source to vertices
};

We leave the UML class diagram of the class weightedGraphType and the inheritance
hierarchy as an exercise
...
Next, we describe the shortest path algorithm
...

The general algorithm is as follows:
1
...
Set smallestWeight[vertex] = 0
...
Find the vertex v that is closest to vertex for which the shortest path
has not been determined
...
Mark v as the (next) vertex for which the smallest weight is found
...
For each vertex w in G, such that the shortest path from vertex to w has
not been determined and an edge (v, w) exists, if the weight of the path
to w via v is smaller than its current weight, update the weight of w to
the weight of v + the weight of the edge (v, w)
...
Example 12-5
illustrates the shortest path algorithm
...
If the
smallest weight for a vertex, from the source, has been found, then this vertex’s corresponding
entry in the array weightFound is set to true; otherwise the corresponding entry is false
...


3
1

16
0

5

3

12

2

10

2
3

7

4

4

5

Weighted graph G

FIGURE 12-8

Suppose that the source vertex of G is 0
...
After
Steps 1 and 2 execute, the resulting graph is as shown in Figure 12-9
...
We do this by finding a vertex in the array

Shortest Path Algorithm

|

703

smallestWeight that has the smallest weight and its corresponding entry in the array
weightFound is false
...
At Step 4, we
mark weightFound[3] as true
...
We then check if the path from the vertex 0 to the vertices
1 and 4 via the vertex 3 can be improved
...
So we update smallestWeight[1] to 14
...
So

we do not update the weight of the vertex 4
...
(The
dotted arrow shows the shortest path from the source—that is, from 0—to the vertex
...
Next we execute Steps 4 and 5
...
At Step 5, we consider vertices 1 and 2 because these are the
vertices for which there is an edge from the vertex 4 and the shortest path from 0 to these
vertices has not been found
...
Clearly, the weight of the path 0-4-1, which is 13, is
smaller than the current weight of 1, which is 14
...

Similarly, we update smallestWeight[2]
...


w = 13
1

16
0

smallestWeight

5
3

2
3

3

weightFound

12

2

10
7

[0]
0
[0]
T

w=7
4

4
w=3

w=2
5

FIGURE 12-11

Graph after the second iteration of Steps 3 to 5

[1]
13
[1]
F

[2]
7
[2]
F

[3]
2
[3]
T

[4]
3
[4]
T

1
2

704 |

Chapter 12: Graphs

Iteration 3 of Steps 3 through 5: At Step 3, the vertex selected is 2
...
Next at Step 5, we consider the vertex 1 because this is the
vertex for which there is an edge from the vertex 2 and the shortest path from 0 to this
vertex has not been found
...
Clearly, the weight of the path 0-4-2-1, which is 10,
from 0 to 1 is smaller than the current weight of 1 (which is 13)
...
Figure 12-12 shows the resulting graph
...
In this iteration, the action of Step 5 is null because
the shortest path from the vertex 0 to every other vertex in the graph has been
determined
...


16

w = 10
1

[0]
0
[0]
weightFound
T

smallestWeight

5

0
3

12

2
3

3

7

[2]
7
[2]
T

[3]
2
[3]
T

[4]
3
[4]
T

2

10

w=2

[1]
10
[1]
T

w=7
4

4

w=3

5

FIGURE 12-13

Graph after the fourth iteration of Steps 3 through 5

The following C++ function, shortestPath, implements the previous algorithm:
void weightedGraphType::shortestPath(int vertex)
{
for (int j = 0; j < gSize; j++)
smallestWeight[j] = weights[vertex][j];

Shortest Path Algorithm

|

705

bool *weightFound;
weightFound = new bool[gSize];
for (int j = 0; j < gSize; j++)
weightFound[j] = false;
weightFound[vertex] = true;
smallestWeight[vertex] = 0;
for (int i = 0; i < gSize - 1; i++)
{
double minWeight = DBL_MAX;
int v;
for (int j = 0; j < gSize; j++)
if (!weightFound[j])
if (smallestWeight[j] < minWeight)
{
v = j;
minWeight = smallestWeight[v];
}
weightFound[v] = true;
for (int j = 0; j < gSize; j++)
if (!weightFound[j])
if (minWeight + weights[v][j] < smallestWeight[j])
smallestWeight[j] = minWeight + weights[v][j];
} //end for
} //end shortestPath

Note that the function shortestPath records only the weight of the shortest path from
the source to a vertex
...
Moreover, this function uses the named
constant DBL_MAX, which is defined in the header file cfloat
...
In the function shortestPath, the first for loop
executes n times and the second for loop also executes n times
...
The body of the third for loop contains two for loops, in sequence,
and each of these for loops executes n times
...
Hence, the function
2
shortestPath, that is, the shortest path algorithm is of order O(n )
...
"
<< endl;
cout << "Vertex Shortest_Distance" << endl;

1
2

706 |

Chapter 12: Graphs

for (int j = 0; j < gSize; j++)
cout << setw(4) << j << setw(12) << smallestWeight[j]
<< endl;
cout << endl;
} //end printShortestDistance
//Constructor
weightedGraphType::weightedGraphType(int size)
:graphType(size)
{
weights = new double*[size];
for (int i = 0; i < size; i++)
weights[i] = new double[size];
}

smallestWeight = new double[size];

//Destructor
weightedGraphType::~weightedGraphType()
{
for (int i = 0; i < gSize; i++)
delete [] weights[i];

}

delete [] weights;
delete smallestWeight;

Minimum Spanning Tree
Consider the graph of Figure 12-14, which represents the airline connections of a
company, between seven cities
...


0
5

6
2
1

2

3

2

4

7
5

8

4

6
10

FIGURE 12-14

5

Airline connections between cities and the cost factor of maintaining the connections

Minimum Spanning Tree |

707

Due to financial hardship, the company needs to shut down the maximum number of
connections and still be able to fly (may be not directly) from one city to another
...


0

0
6

2

3

1

2

3

2

7

4

5

5

4

8
6
5
(a)

FIGURE 12-15

5

2

2

1

4

0
5

5

2
1

2

3

2

4

7
5

4
6
10

5
(b)

6
5
(c)

Possible solutions to the graph of Figure 12-14

The total cost factor of maintaining the remaining connections in Figure 12-15(a) is 33, in
Figure 12-15(b) it is 28, and in Figure 12-15(c) it is 25
...
Graphs of Figure 12-15 are called spanning trees of the graph of Figure 12-14
...
Each of the graphs of Figure 12-15
is a subgraph of the graph of Figure 12-14, and there is a unique path from a node to any
other node
...
There are many other situations, where given a
weighted graph, we need to determine a graph such as in Figure 12-15 with the smallest
weight
...
However, first
we introduce some terminology
...
A tree in which a particular vertex is designated as a root is called a rooted
tree
...
If T is a weighted
tree, the weight of T, denoted by W(T), is the sum of the weights of all the edges in T
...

Suppose G denotes the graph of Figure 12-14
...
Let us note the following theorem
...

From this theorem, it follows that to determine a spanning tree of a graph, the graph must
be connected
...
A minimum (minimal) spanning tree of G
is a spanning tree with the minimum weight
...
This section discusses Prim’s algorithm to find a
minimum spanning tree
...

Prim’s algorithm builds the tree iteratively by adding edges until a minimum spanning
tree is obtained
...
At
each iteration, a new edge that does not form a cycle is added to the tree
...
,vn-1}, where n, the number of vertices,
is nonnegative
...
Let T be the partially built tree
...
At the next iteration, a new vertex that is not in
V(T) is added to V(T), such that an edge exists from a vertex in T to the new vertex so that the
corresponding edge has the smallest weight
...

The general form of Prim’s algorithm is as follows
...
)
1
...
Set E(T) ¼ empty
3
...
1
...
2
...
3
...
4
...


0
5

6
2
1

2

3

2

4

7
5

8

4

6
10

FIGURE 12-16

Weighted graph G

5

Minimum Spanning Tree |

709

Let N denote the set of vertices of G that are not in T
...
Figure 12-17 shows how Prim’s algorithm works
...
Step 3
checks the following edges: (0,1), (0,2), and (0,3)
...
Clearly, the edge (0,3)
has the smallest weight; see Figure 12-17(b)
...
Figure 12-17(b) shows the resulting graph, V(T), E(T), and N
...
)

1
2

710 |

Chapter 12: Graphs

Next, Step 3 checks the following edges: (0,1), (0,2), and (3,4)
...
Clearly,
the edge (0,2) has the smallest weight
...
Figure 12-17(c) shows the resulting graph, V(T), E(T), and N
...
The
weight of the edge (0,1) is 6, the weight of the edge (2,5) is 7, the weight of the edge (2,6)
is 5, and the weight of the edge (3,4) is 8
...

Therefore, vertex 6 is added to V(T) and the edge (2,6) is added to E(T)
...
(The dotted lines show the edges in T
...
The
weight of the edge (0,1) is 6, the weight of the edge (2,5) is 7, the weight of the edge (3,4)
is 8, and the weight of the edge (6,1) is 4
...

Therefore, vertex 1 is added to V(T) and the edge (6,1) is added to E(T)
...
(The dotted lines show the edges in T
...
The
weight of the edge (1,4) is 2, the weight of the edge (2,5) is 7, and the weight of the edge
(3,4) is 8
...
Therefore, vertex 4 is added to
V(T) and the edge (1,4) is added to E(T)
...
(The dotted lines show the edges in T
...
The weight of
the edge (2,5) is 7 and the weight of the edge (4,5) is 10
...
Therefore, vertex 5 is added to V(T) and the edge (2,5) is added to E(T)
...
(The dotted lines show the
edges in T
...

Before we give the definition of the function to implement Prim’s algorithm, let us first
define a spanning tree as an ADT
...
Let edges be an array such that edges[j] = k, if there is an edge connecting
vertices vj and vk
...
Let edgeWeights be an array such that edgeWeights[j] is the weight of the
edge (vi, vj)
...
S
...

//****************************************************************

Minimum Spanning Tree |

711

class msTreeType: public graphType
{
public:
void createSpanningGraph();
//Function to create the graph and the weight matrix
...

void minimumSpanning(int sVertex);
//Function to create a minimum spanning tree with
//root as sVertex
...

//
The weight of the edges is also saved in the array
//
edgeWeights
...

//Postcondition: The edges of a minimum spanning tree
//
and their weights are printed
...

//
weights is a two-dimensional array to store the weights
//
of the edges
...

//
edgeWeights is an array to store the weights of the
//
edges of a minimum spanning tree
...

protected:
int source;
double **weights;
int *edges;
double *edgeWeights;
};

We leave the UML class diagram of the class msTreeType and the inheritance hierarchy
as an exercise
...
This function creates the graph and the weight matrix associated
with the graph
...

Therefore, in the worst case, Prim’s algorithm given in this section is of the order
O(n3)
...

The definition of the function printTreeAndWeight is as follows:
void msTreeType::printTreeAndWeight()
{
double treeWeight = 0;
cout << "Source Vertex: " << source << endl;
cout << "Edges
Weight" << endl;

Topological Order |

for (int j = 0; j < gSize; j++)
{
if (edges[j] != j)
{
treeWeight = treeWeight + edgeWeights[j];
cout << "("<<< edgeWeights[j] << endl;
}
}

713

"

cout << endl;
cout << "Minimum spanning Tree Weight: "
<< treeWeight << endl;
} //end printTreeAndWeight

The definitions of the constructor and the destructor are as follows:
msTreeType::msTreeType(int size)
:graphType(size)
{
weights = new double*[size];
for (int i = 0; i < size; i++)
weights[i] = new double[size];
edges
}

= new int[size];

edgeWeights = new double[size];

//Destructor
msTreeType::~msTreeType()
{
for (int i = 0; i < gSize; i++)
delete [] weights[i];

}

delete [] weights;
delete [] edges;
delete edgeWeights;

Topological Order
In college, before taking a particular course, students, usually, must take all its prerequisite
courses, if any
...
However, certain courses can be taken independent of
each other
...
A
directed edge from, say vertex u to vertex v means the course represented by the vertex
u is a prerequisite of the course represented by the vertex v
...
In this section, we describe an algorithm that can be
used to output the vertices of a directed graph in such a sequence
...

Let G be a directed graph and V(G) ¼ {v1, v2,
...
A topological
ordering of V(G) is a linear ordering vi1, vi2,
...

In this section, we describe an algorithm, topological order, which outputs the
vertices of a directed graph in topological order
...
We leave it as an exercise for you to modify the algorithm for the graphs that
have cycles
...

There exists a vertex u in G such that u has no predecessor
...
Thus, if a vertex, say u, is a successor of the
vertex v and topologicalOrder[j] = v and topologicalOrder[k] = u, then j < k
...
This section discusses how to implement topological ordering
using the breadth-first traversal
...

We extend the definition of the class graphType (using inheritance) to implement the
breadth-first topological ordering algorithm
...

Next, we give the definition of the class that includes the functions to implement the
topological ordering algorithm
...
S
...

//****************************************************************
class topologicalOrderType: public graphType
{
public:
void bfTopOrder();
//Function to perform breadth first topological ordering of
//a graph
...


Topological Order |

};

715

topologicalOrderType(int size = 0);
//Constructor
//Postcondition: gSize = 0; maxSize = size;
//
graph is an array of pointers to linked lists
...


Breadth-First Topological Ordering
Recall that the breadth-first traversal algorithm is similar to traversing a binary tree
level-by-level, and so the root node (which has no predecessor) is visited first
...
We next find the
vertex, say v, all of whose predecessors have been placed in the topological ordering
and place v next in the topological ordering
...
Initially, predCount[j] is the number of
predecessors of the vertex vj
...
In essence, the general
algorithm is as follows:
1
...

2
...
(Clearly, queue is not empty because the graph has
no cycles
...
while the queue is not empty
3
...
Remove the front element, u, of the queue
...
2
...

3
...
For all the immediate successors w of u,
3
...
1
...

3
...
2
...

The graph G3 of Figure 12-7 has no cycles
...


1
2

716 |

Chapter 12: Graphs

After Steps 1 and 2 execute, the arrays predCount, topologicalOrder, and queue are as
shown in Figure 12-18
...
)

0

1

5

2
3

topologicalOrder
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]

9

7
6

predCount
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]
0
1
1
3
2
2
1
1
2
0
2

4

8

10
queue
0, 9

FIGURE 12-18

Arrays predCount, topologicalOrder, and queue after Steps 1 and 2 execute

Step 3 executes as long as the queue is nonempty
...
1 executes, the value of u is 0
...
2 stores the value of
u, which is 0, in the next available position in the array topologicalOrder
...
Step 3
...
The successor nodes of the node 0 are the nodes 1 and 5
...
The node 1 is pushed into queue
...


0
5

predCount
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]
0
0
1
3
2
1
1
1
2
0
2

4

3
7

6

2

1

8

topologicalOrder
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]
0

9
10

queue
9, 1
u

FIGURE 12-19

0

Arrays predCount, topologicalOrder, and queue after the first iteration of Step 3

Iteration 2 of Step 3: The queue is nonempty
...
1 executes, the value of u is 9
...
2 stores the value of u, which is 9, in the next available position in the array
topologicalOrder
...
Step 3
...
The successor nodes of the node 9
are the nodes 4, 7, and 10
...
The node 7 is pushed into queue
...


0

1

5

2
3

7
6

predCount
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]
0
0
1
3
1
1
1
0
2
0
1

4

topologicalOrder
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]
0
9

9

8

10
queue
1, 7
u

FIGURE 12-20

9

Arrays predCount, topologicalOrder, and queue after the second iteration

of Step 3

Iteration 3 of Step 3: The queue is nonempty
...
1 executes, the value of u
is 1
...
2 stores the value of u, which is 1, in the next available position in the array
topologicalOrder
...
Step 3
...
The successor nodes of
the node 1 are the nodes 2, 3, and 5
...
The nodes 2 and 5, in this
order, are pushed into the queue
...


0

1

5

4

3
7

6

2

predCount
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]
0
0
0
2
1
0
1
0
2
0
1

8

topologicalOrder
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]
0
9
1

9
10

queue
7, 2, 5
u

FIGURE 12-21

1

Arrays predCount, topologicalOrder, and queue after the third iteration of Step 3

1
2

718 |

Chapter 12: Graphs

If you repeat Step 3 eight more times, the arrays predCount, topologicalOrder, and
queue are as shown in Figure 12-22
...

The following C++ function implements this breadth-first topological ordering algorithm:
void topologicalOrderType::bfTopOrder()
{
linkedQueueType queue;
int *topologicalOrder; //pointer to the array to store
//breadth first topological ordering
topologicalOrder = new int[gSize];
int topIndex = 0;
linkedListIterator graphIt; //iterator to traverse a
//linked list
int *predCount;

//pointer to the array to store the
//predecessor count of a vertex
...

for (int ind = 0; ind < gSize; ind++)
{
for (graphIt = graph[ind]
...
end(); ++graphIt)
{
int w = *graphIt;
predCount[w]++;
}
}

Euler Circuits

|

719

//Initialize queue: If the predecessor count of
//vertex is 0, put this node into the queue
...
addQueue(ind);
while (!queue
...
front();
queue
...
If the predecessor count of a vertex
//becomes 0, put this vertex into the queue
...
begin();
graphIt != graph[u]
...
addQueue(w);
}
}//end while
//output the vertices in breadth first topological order
for (int ind = 0; ind < gSize; ind++)
cout << topologicalOrder[ind] << " ";
cout << endl;
delete [] topologicalOrder;
delete [] predCount;
}//bfTopOrder

We leave the definition of the constructor as an exercise
...
)

Euler Circuits
Let us consider the Konigsberg bridge problem stated at the beginning of the chapter
...
As remarked earlier,
Euler converted this problem into a graph theory problem as follows: Each of the islands
A, B, C, and D is considered as a vertex of a graph and the bridges are considered as
edges, as shown in Figure 12-2
...
In this section, we further
describe properties of graphs, which will help us answer this question
...

Definition: A circuit in a graph that includes all the edges of the graph is called an Euler
circuit
...

Notice that the graph of Figure 12-2 is a connected graph and this graph has odd degree
vertices as well as even degree vertices
...
For example, consider the graph of Figure 12-23
...
This graph has no circuit and so has no
circuit that contains all the edges
...


0
e8
4

e1

1

e2
e6

e5
5

e4

2
e3
3

e7

FIGURE 12-24

A graph with all vertices of even degree

The graph of Figure 12-24 has an Euler circuit
...

The following theorems give necessary and sufficient conditions for a connected graph to
have an Euler circuit
...

Theorem 12-3: Let G be a connected graph such that every vertex of G is of even
degree
...

We can effectively use this theorem to determine whether a connected graph G has an
Euler circuit by checking whether all of its vertices are of even degree
...
Notice that the graph correspond¨
ing to this problem is a connected graph but has vertices of odd degree; see Figure 12-2
...
In other words,
starting at one land area, it is not possible to walk across all the bridges exactly once and
return to the starting land area
...
The graph with the
additional two bridges is shown in Figure 12-25
...
Next, we describe an algorithm, known as Fleury’s algorithm, which can be used
to construct an Euler circuit in a connected graph with vertices of even degrees
...
Choose a vertex v as the starting vertex for the circuit and choose an edge e with
v as one of the end vertices
...
If the other end vertex u of the edge e is also v, go to Step 3
...
If the other vertex u1 of e1 is
v, go to Step 3; otherwise, choose an edge e2 different from e and e1 with u1 as one of the
end vertices and repeat Step 2
...
If the circuit T1 obtained in Step 2 contains all the edges, then stop
...


1
2

722 |

Chapter 12: Graphs

Step 4
...

Step 5
...
Now go
to Step 3 and repeat Step 3 with the circuit T3
...
Consider the graph of Figure 12-26
...

First, select vertex 0 and form the circuit: T1: (0, e1, 1, e3, 2, e2, 0)
...
Construct the circuit: C1: (1, e4, 2, e6, 6, e7, 3, e5, 1)
...

Circuit T2 does not contain all the edges of the given graph
...

Now construct the circuit: T3: (0, e1, 1, e4, 2, e6, C2, e7, 3, e5, 1, e3, 2, e2, 0)
...
Select now vertex 3 and edge e11
...

Next, construct the circuit: T4: (0, e1,1, e4, 2, e6, C2, e7, C3, e5, 1, e3, 2, e2, 0)
...

We leave it as an exercise for you to write a program to implement Fleury’s algorithm
...
)

QUICK REVIEW
1
...


A graph G is a pair, G ¼ (V, E), where V is a finite nonempty set, called the
set of vertices of G and E  V Â V , called the set of edges
...


Quick Review |

3
...

5
...

7
...

9
...

11
...

13
...

15
...


17
...


19
...

21
...

23
...

Let G be a graph
...

Two vertices u and v in an undirected graph are called adjacent if there is an
edge from one to the other
...

In an undirected graph, if two edges e1 and e2 are associated with the same
pair of vertices {u, v}, then e1 and e2 are called parallel edges
...

Let e ¼ (u, v) be an edge in an undirected graph G
...

A path from a vertex u to a vertex v is a sequence of vertices u1, u2,
...
, n - 1
...

A simple path is a path in which all the vertices, except possibly the first and
last vertices, are distinct
...

An undirected graph G is called connected if there is a path from any vertex
to any other vertex
...

Suppose that u and v are vertices in a directed graph G
...

A directed graph G is called strongly connected if any two vertices in G are
connected
...
Let V(G) ¼ {v1, v2,
...

The adjacency matrix AG is a two-dimensional n  n matrix such that the
(i, j)th entry of AG is 1 if there is an edge from vi to vj; otherwise, the (i, j)th
entry is 0
...

The depth-first traversal of a graph is similar to the preorder traversal of a
binary tree
...

The shortest path algorithm gives the shortest distance for a given node to
every other node in the graph
...


723

1
2

724 |

24
...

26
...

28
...

30
...

32
...


Chapter 12: Graphs

The weight of the path P is the sum of the weights of all the edges on the
path P, which is also called the weight of v from u via P
...

A tree in which a particular vertex is designated as a root is called a rooted
tree
...
If a weight is assigned to the edges in T, T is called a
weighted tree
...

A tree T is called a spanning tree of graph G if T is a subgraph of G such
that V(T) ¼ V(G)—that is, if all the vertices of G are in T
...
, vn}, where n ‡ 0
...
, vin of the vertices such that
if vij is a predecessor of vik, j 6¼ k, 1 j, k n, then vij precedes vik, that is,
j < k in this linear ordering
...

A circuit in a graph that includes all the edges of the graph is called an Euler
circuit
...


EXERCISES
Use the graph in Figure 12-27 for Exercises 1 through 4
...

2
...

4
...

Draw the adjacency list of the graph
...

List the nodes of the graph in a breadth-first traversal
...


725

Find the weight matrix of the graph in Figure 12-28
...


|

6

4

Graph for Exercise 5

Consider the graph in Figure 12-29
...


3

0

1

12

4
5
2

5

8

15
3

7

8
4

2
3

FIGURE 12-29
7
...


0

1
2

3

5
7
10

FIGURE 12-30

Graph for Exercise 7

4

1
2

6
8

9
11

726 |

8
...


2
0

1

3
5

4
7

FIGURE 12-31
9
...


7

0

1
2

3
1
2

FIGURE 12-32

10
...


0

1

6
8

Graph for Exercise 10

4

3

5

FIGURE 12-33

2

7
9

Programming Exercises

11
...
If the
graph has an Euler circuit, find one such circuit
...


e2
1
e8

e3
2

e4

Graph for Exercise 11

Describe whether the graph in Figure 12-35 has an Euler circuit
...


8

9

e1
e14

1
e2
2

e9

e10

0

e15
e3

e8
e13
e12

e11

3

7

e4

5
e5

e7
e6

6

4

FIGURE 12-35

Graph for Exercise 12

PROGRAMMING EXERCISES
1
...

3
...

5
...

Write a program that outputs the nodes of a graph in a breadth-first traversal
...

Write a program that outputs the minimum spanning tree for a given graph
...
The following is an alternative to Prim’s algorithm that is
of the order O(n2)
...
,
n – 1; starting with vertex s, with a weight matrix of W
...

Prim2(G, W, n, s)
Let T ¼ (V, E), where E ¼ f
...

visited[k] = true;
E = E ¨ {(k, edges[k])}
V = V ¨ {k}
for each node j that is not visited
if(W(k,j) < edgeWeight[k])
{
edgeWeight[k] = W(k,j);
edges[j] = k;
}
}
return T
...

7
...
Furthermore, write a program to
test this version of Prim’s algorithm
...

Let G be a graph and V(G) ¼ {v1, v2,
...
Recall that a
topological ordering of V(G) is a linear ordering vi1,vi2,
...
Suppose that G has no
cycles
...

In a depth-first topological ordering, we start with finding a vertex that has
no successors (such a vertex exists because the graph has no cycles), and place
it last in the topological order
...


|

729

before any of its successors
...

Write the definitions of the C++ functions to implement the depth-first topological ordering
...
Also, write a program to test your
depth-first topological ordering
...


1
2

This page intentionally left blank

13
CHAPTER

S TANDARD T EMPLATE
L IBRARY (STL) II
I N T H I S C H A P T E R , YO U W I L L :


...


Become familiar with associative containers


...


Learn about various generic algorithms

732 |

Chapter 13: Standard Template Library (STL) II

Chapter 4 introduced the Standard Template Library (STL)
...
The three categories of containers are sequence containers, associative containers, and container adapters
...
The container adapter stack is described in Chapter 7, and the container
adapters queue and priority_queue are described in Chapter 8
...
This chapter discusses the components of the STL not discussed in the previous
chapters, specifically, the associative containers and algorithms
...


Class pair
With the help of the class pair, two values can be combined into a single unit and,
therefore, can be treated as one unit
...
This class is used in several other places in the STL
...

The definition of the class pair is contained in the header file utility
...
Thus, the general syntax to declare an object of type pair is as follows:
pair

pElement;

or
pair

pElement(expr1, expr2);

where expr1 is of type Type1 and expr2 is of type Type2
...
Because the data members of an object of type pair are public,
each object of type pair can directly access these data members in a program
...

EXAMPLE 13-1
Consider the following statements
...
9);

//Line 1
//Line 2

Class pair | 733

pair z(10, 20);
pair name("Bill", "Brown");
pair employee("John Smith", 45678
...
The first component of
x is of type int; the second component is of type double
...

The statement in Line 2 declares y to be an object of type pair
...
The first component of y, that is,
first, is initialized to 13; the second component, that is, second, is initialized to 45
...

The statement in Line 3 declares z to be an object of type pair
...
The first component of z, that is, first, is initialized to 10; the second
component, that is, second, is initialized to 20
...
Both components of
name are of type string
...

The statement in Line 5 declares employee to be an object of type pair
...

The first component of employee, that is, first, is initialized to "John Smith"; the
second component, that is, second, is initialized to 45678
...

The statement
x
...
Similarly, the statement
name
...

The following statements show how to output the value of an object of type pair
...

Statement
cout << y
...
second << endl; Outputs: 13 45
...
first << " "
<< name
...
first << " "
<< employee
...
50

1
3

734 |

Chapter 13: Standard Template Library (STL) II

Comparing Objects of Type pair
The relational operators have been overloaded for the class pair
...

Suppose x and y are objects of type pair, and the corresponding data members of x and
y are of the same type
...
) Table 13-1 describes
how the relational operators are defined for the class pair
...
first == y
...
second == y
...
first < y
...
first >= y
...
second < y
...

With the help of the function make_pair, we can create pairs without explicitly specifying the
type pair
...
The
components of the value returned by the function template make_pair are passed as
parameters to the function template make_pair
...
The value of the first component is 75; the value of the
second component is the character 'A'
...
Example 13-2 illustrates the use of make_pair
...
S
...

//**************************************************************
#include
#include
#include
#include






//Line
//Line
//Line
//Line

1
2
3
4

using namespace std;

//Line 5

void
void
void
void

//Line
//Line
//Line
//Line

6
7
8
9

//Line
//Line
//Line
//Line

10
11
12
13

funcExp(pair);
funcExp1(pair);
funcExp2(pair x);
funcExp3(pair x);

int main()
{
pair x(50, 87
...
first << " " << x
...
first << " "
name
...
first << " " << y
...
first << "***"
<< name2
...
first
<< " " << x
...
first
<< " " << x
...
first
<< " " << x
...
first
<< " " << x
...
67
John Johnson
0 0
***
In funcExp: 75 80
In funcExp1: 87 H
In funcExp1: 198 K
In funcExp2: 250 Hello
In funcExp2: 65 Hello There
In funcExp3: 35 Hello World
In funcExp3: 22 Sunny

Associative Containers
Elements in an associative container are automatically sorted according to some ordering
criteria
...
Users also
have the option of specifying their own ordering criterion
...
A convenient and fast way to
implement this type of data structure is to use a binary search tree
...
Thus, every element in the container has a parent
node (except the root node) and at most two children
...


Associative Containers

|

737

The predefined associative containers in the STL are sets, multisets, maps, and
multimaps
...


Associative Containers: set and multiset
As described earlier, both the containers set and multiset automatically sort their
elements according to some sort criteria
...
The user can
also specify other sorting criteria
...

The only difference between the containers set and multiset is that the container
multiset allows duplicates, whereas the container set does not
...
The name of the header file containing the
definitions of the classes set and multiset, and the definitions of the functions to
implement various operations on these containers, is set
...
This section discusses the various ways that these types of
associative containers are declared and initialized
...

TABLE 13-2

Various ways to declare a set/multiset container

Statement

Effect

ctType ct;

Creates an empty set/multiset
container, ct
...


ctType ct;

Creates an empty set/multiset
container, ct
...


ctType ct(otherCt);

Creates a set/multiset
container, ct
...
The
sort criterion is <
...


1
3

738 |

Chapter 13: Standard Template Library (STL) II

TABLE 13-2

Various ways to declare a set/multiset container (continued)

Statement

Effect

ctType ct(otherCt);

Creates a set/multiset
container, ct
...

The sort criterion is specified by
sortOp
...
Note that the
sort criteria of ct and otherCt
must be the same
...
The elements starting
at the position beg until the position
end-1 are copied into ct
...


ctType ct(beg, end);

Creates a set/multiset
container, ct
...
Both beg and end are
iterators
...


If you want to use a sort criterion other than the default, you must specify this option
when the container is declared
...
The statement in Line 2 declares
otherIntSet to be an empty set container, the element type is int, and the sort
criterion is greater than (that is, the elements in the container otherIntSet will be
arranged in descending order)
...

The statements in Lines 2 and 4 illustrate how to specify the descending sorting criterion
...
This space is important because >> is also a shift
operator in C++
...
Table 13-3 describes the operations
that can be used to insert or delete elements from a set
...
The name of the function is shown in bold
...
insert(elem)

Inserts a copy of elem into ct
...


ct
...
The position
where elem is inserted is returned
...
The parameter
position is an iterator
...
insert(beg, end);

Inserts a copy of all the elements into ct
starting at the position beg until end-1
...


ct
...

The number of deleted elements is returned
...
erase(position);

Deletes the element at the position specified by
the iterator position
...


ct
...
Both beg and
end are iterators
...


ct
...

After this operation, the container ct is empty
...

EXAMPLE 13-3
//***************************************************************
// Author: D
...
Malik
//
// This program illustrates how the operations on a set/multiset
// container work
...
insert(16);
intSet
...
insert(20);
intSet
...
begin(), intSet
...
insert(36);
intSetA
...
insert(39);
intSetA
...
insert(156);

//Line
//Line
//Line
//Line
//Line

cout << "Line 25: intSetA: ";
copy(intSetA
...
end(), screen);
cout << endl;

//Line 25
//Line 26
//Line 27

intSetA
...
begin(), intSetA
...
begin();
++intGtIt;

//Line 32
//Line 33

intSetA
...
begin(), intSetA
...
begin(), namesMultiSet
...
insert("Donny");
namesMultiSet
...
insert("Ronny");
namesMultiSet
...
insert("Ronny");

|

//Line 49
//Line 50

41
42
43
44
45

//Line 47
//Line 48

Sample Run:
Line
Line
Line
Line
Line

17:
25:
29:
35:
46:

intSet: 3 8 16 20
intSetA: 156 59 39 36 30
After removing 59, intSetA: 156 39 36 30
After removing the second element, intSetA: 156 36 30
namesMultiSet: Zippy Ronny Ronny Hungry Donny

The statement in Line 9 declares intSet to be a set container
...
The statement in Line 11 declares intGtIt to be a set iterator
...
The statement in Line 12 declares screen to be an
ostream iterator that outputs the elements of any container whose elements are of type int
...
In the output, see the line marked Line 17,
which contains the output of the statements in Lines 17 through 19 of the program
...
In the output, see the line marked
Line 25, which contains the output of the statements in Lines 25 through 27 of the
program
...

The statement in Line 28 removes 59 from intSetA
...
After the statement in Line 32 executes,
intGtIt points to the second element of intSetA
...
The statement in Line 36 outputs the
elements of intSetA
...

The elements in namesMultiSet are of type string and are arranged in descending
order
...

The statements in Lines 41 through 45 insert Donny, Zippy, Ronny, Hungry, and Ronny
into namesMultiSet
...


1
3

742 |

Chapter 13: Standard Template Library (STL) II

Associative Containers: map and multimap
The containers map and multimap manage their elements in the form key/pair
...
The
default sorting criterion is the relational operator < (less than); that is, the elements are
arranged in ascending order
...
For userdefined data types, such as classes, the relational operators must be properly overloaded
...

The name of the class defining the container map is map; the name of the class defining
the container multimap is also multimap
...
Therefore, to use any of these
containers, the program must include the following statement:
#include

DECLARING map OR multimap ASSOCIATIVE CONTAINERS
The classes map and multimap contain several constructors to declare and initialize
containers of these types
...
Table 13-4 describes how a map/
multimap container of a specific type can be declared and initialized
...
)
TABLE 13-4

Various ways to declare a map/multimap container

Statement

Effect

ctType ct;

Creates an empty map/multimap
container, ct
...


ctType ct;

Creates an empty map/multimap
container, ct
...


ctType ct(otherCt);

Creates a map/multimap container, ct
...
The sort criterion is <
...


ctType
ct(otherCt);

Creates a map/multimap container,
ct
...
The sort criterion is
specified by sortOp
...
Note
that the sort criteria of ct and
otherCt must be the same
...
The elements starting at the
position beg until the position end-1
are copied into ct
...


ctType
ct(beg, end);

Creates a map/multimap container,
ct
...
Both beg and end
are iterators
...


If you want to use a sort criterion other than the default, you must specify this option
when the container is declared
...
The statement
in Line 2 declares otherIntMap to be an empty map container, the key type and the
element type are int, and the sort criterion is greater than
...
The statements in Lines 3
and 4 have similar conventions
...

In the statements in Lines 2 and 4, note the space between the two >—that is, the space
between greater and >
...


ITEM INSERTION AND DELETION FROM map/multimap
Suppose that ct is of type either map or multimap
...
Table 13-5 also illustrates how to
use these operations
...
In this table, ct is either
a map or multimap container
...
insert(elem)

Inserts a copy of elem into ct
...


ct
...
The position
where elem is inserted is returned
...
The parameter
position is an iterator
...
insert(beg, end);

Inserts a copy of all the elements into ct
starting at the position beg until end-1
...


ct
...

The number of deleted elements is returned
...
erase(position);

Deletes the element at the position specified by
the iterator position
...


ct
...
Both beg and
end are iterators
...


ct
...

After this operation, the container ct is empty
...

EXAMPLE 13-4
//***************************************************************
// Author: D
...
Malik
//
// This program illustrates how the operations on a map/multimap
// container work
...
insert(make_pair(1, 16));
intMap
...
insert(make_pair(4, 20));
intMap
...
insert(make_pair(1, 23));
intMap
...
insert(make_pair(8, 28));
intMap
...
insert(make_pair(6, 43));
intMap
...
begin();
mapItr != intMap
...
erase(12);

//Line 25

mapItr = intMap
...
erase(mapItr);

//Line
//Line
//Line
//Line

745

mapItr;

//Line 22
//Line 23
//Line 24

26
27
28
29

cout << "Line 30: After deleting, elements of "
<< "intMap" << endl;
for (mapItr = intMap
...
end(); mapItr++)
cout << mapItr->first << "\t"
<< mapItr->second << endl;
cout << endl;

//Line 31

multimap namesMultiMap;
multimap::iterator nameItr;

//Line 34
//Line 35

namesMultiMap
...
insert(make_pair("B1",
namesMultiMap
...
insert(make_pair("A2",
namesMultiMap
...
insert(make_pair("A1",

//Line
//Line
//Line
//Line
//Line
//Line

"Donny"));
"Zippy"));
"Ronny"));
"Hungry"));
"Ronny"));
"Dumpy"));

cout << "Line 42: namesMultiMap: " << endl;
for (nameItr = namesMultiMap
...
end(); nameItr++)

//Line 30

//Line 32
//Line 33

36
37
38
39
40
41

//Line 42
//Line 43

1
3

746 |

Chapter 13: Standard Template Library (STL) II

cout << nameItr->first << "\t"
<< nameItr->second << endl;
cout << endl;
}

return 0;

//Line 44
//Line 45
//Line 46
//Line 47

Sample Run:
Line 21: The elements of intMap
1
16
2
8
3
3
4
20
6
43
8
28
12
16
15
60
20
18
Line 30: After deleting, elements of intMap
1
16
2
8
4
20
6
43
8
28
15
60
20
18
Line 42: namesMultiMap:
A1
Donny
A1
Dumpy
A2
Hungry
B1
Zippy
D1
Ronny
K1
Ronny

The statement in Line 9 declares intMap to be a map container
...
The iterator intGtIt can process the elements of
any map container whose elements have the key type and the element type int
...
For example,
16 is inserted with the key 1
...

The for loop in Line 22 outputs the elements of the container intMap
...
The statement
in Line 26 initializes mapItr to the first element in the container intMap
...
After the
statement in Line 28 executes, mapItr points to the third element of intMap
...
The for
loop in Line 31 outputs the elements of the container intMap
...

The elements and their keys in namesMultiMap are of type string
...

The statements in Lines 36 through 41 insert the elements into namesMultiMap
...


Containers, Associated Header Files,
and Iterator Support
Chapters 4 and 5 and the previous sections discussed various types of containers
...
The definition of the class implementing a specific
container is contained in the header file
...

TABLE 13-6 Containers, their associated header files, and the type of iterator supported
by each container

Sequence containers

Associated header file

Type of iterator support

vector



Random access

deque



Random access

list



Bidirectional

Associative containers

Associated header file

Type of iterator support

map



Bidirectional

multimap



Bidirectional

set



Bidirectional

multiset



Bidirectional

Adapters

Associated header file

Type of iterator support

stack



No iterator support

queue



No iterator support

priority_queue



No iterator support

1
3

748 |

Chapter 13: Standard Template Library (STL) II

Algorithms
Several operations can be defined for a container
...
However, several operations—
such as find, sort, and merge—are common to all containers
...
The algorithms are bound to a particular container through an iterator pair
...
This section
describes several of these algorithms and shows how to use them in a program
...


STL Algorithm Classification
In earlier sections, you applied various operations on the sequence container, such as
clear, sort, merge, and so on
...
All those algorithms and a few more are also
available in more general forms, called generic algorithms, and can be applied in a
variety of situations
...

The STL contains algorithms that only look at the elements in a container and that move
the elements of a container
...
In addition, the STL
contains algorithms for basic set theory operations, such as set union and intersection
...
The algorithms in the STL can be classified into the
following categories:





Nonmodifying algorithms
Modifying algorithms
Numeric algorithms
Heap algorithms

The next four sections describe these algorithms
...
Certain algorithms, such as the numeric algorithms, are contained in the header file numeric
...
Table 13-7 lists the nonmodifying algorithms
...
Table 13-8 lists the
modifying algorithms
...
For example, next_permutation, partition, prev_
permutation, random_shuffle, reverse, reverse_copy, rotate, rotate_copy,
and stable_partition are mutating algorithms
...
Table 13-9 lists these algorithms
...
Recall that in the heapsort algorithm,
the array containing the data is viewed as a binary tree
...
In a heap, the first element is the largest element,
and the ith element (if it exists) is larger than the elements at positions 2i and 2i + 1 (if
they exist)
...

Table 13-10 lists the algorithms provided by the STL to implement the heap sort
algorithm
...
For the most
part, the function prototypes of these algorithms are given along with a brief explanation
of what each algorithm does
...
The STL algorithms are very powerful and accomplish wonderful
results
...
For example, the natural sorting order is ascending, but the user can specify
criteria to sort the container in descending order
...
Before starting to describe these
algorithms, we discuss function objects, which allow the user to specify the manipulating criteria
...
The first form of an algorithm
uses the natural operation to accomplish this goal
...
For example, the algorithm
adjacent_find searches the container and returns the position of the first two elements that
are equal
...
These
criteria are passed as a function object
...
In fact, a function object
is a class template that overloads the function call operator, ()
...

The STL’s function objects are contained in the header file functional
...


minus

minus subtractNum;
int difference = subtractNum(56, 35);
The value of difference is 21
...


divides

divides divideNum;
int quotient = divideNum(16, 3);
The value of quotient is 5
...


negate

negate opposite;
int num = opposite(-25);
The value of opposite is 25
...

EXAMPLE 13-5
//*************************************************************
// Author: D
...
Malik
//
// This program shows how STL arithmetic function objects work
...
begin(), intList
...
begin(),
intList
...
begin(), intList
...

TABLE 13-12 Relational STL function objects

Function object name

Description
Returns true if the two arguments are equal, and false
otherwise
...

Returns true if the two arguments are not equal, and
false otherwise
...

Returns true if the first argument is greater than the
second argument, and false otherwise
...


1
3

754 |

Chapter 13: Standard Template Library (STL) II

TABLE 13-12 Relational STL function objects (continued)

Function object name

Description
Returns true if the first argument is greater than or equal to
the second argument, and false otherwise
...

Returns true if the first argument is less than the second
argument, and false otherwise
...

Returns true if the first argument is less than or equal to the
second argument, and false otherwise
...


The STL relational function objects can also be applied to containers, as shown next
...
This algorithm has a second form that allows the user to
specify the comparison criteria
...
To see if the elements
are out of order, we can use the algorithm adjacent_find as follows:
intItr = adjacent_find(vecList
...
end(),
greater());

where intItr is an iterator of type vector
...
begin()—that is, at the first element of vecList—and looks for
the first set of consecutive elements such that the first element is greater than the second
...

The program in Example 13-6 further illustrates how to use the relational function objects
...
S
...

//*************************************************************

STL Algorithm Classification

#include
#include
#include
#include
#include
#include








//Line
//Line
//Line
//Line
//Line
//Line

|

755

1
2
3
4
5
6

using namespace std;

//Line 7

int main()
{
equal_to compare;
bool isEqual = compare(6, 6);

//Line
//Line
//Line
//Line

8
9
10
11

cout << "Line 12: isEqual = " << isEqual << endl;

//Line 12

greater greaterStr;

//Line 13

string str1 = "Hello";
string str2 = "There";

//Line 14
//Line 15

if (greaterStr(str1, str2))
//Line 16
cout << "Line 17: \"" << str1 << "\" is greater "
<< "than \"" << str2 << "\"" << endl;
//Line 17
else
//Line 18
cout << "Line 19: \"" << str1 << "\" is not "
<< "greater than \"" << str2 << "\""
<< endl;
//Line 19
int temp[8] = {2, 3, 4, 5, 1, 7, 8, 9};

//Line 20

vector vecList(temp, temp + 8);
vector::iterator intItr1, intItr2;
ostream_iterator screen(cout, " ");

//Line 21
//Line 22
//Line 23

cout << "Line 24: vecList: ";
copy(vecList
...
end(), screen);
cout << endl;

//Line 24
//Line 25
//Line 26

intItr1 = adjacent_find(vecList
...
end(), greater());
intItr2 = intItr1 + 1;

//Line 27
//Line 28

cout <<
<<
<<
cout <<
<<
<<
}

"Line 29: In vecList, the first set of "
"out of order elements are: " << *intItr1
" " << *intItr2 << endl;
"Line 30: In vecList, the first out of "
"order element is at position: "
vecList
...

TABLE 13-13 Logical STL function objects

Function object name

Effect

logical_not

Returns true if its operand evaluates to false, and
false otherwise
...


logical_and

Returns true if both of its operands evaluate to true,
and false otherwise
...


logical_or

Returns true if at least one of its operands evaluates to
true, and false otherwise
...


Predicates
Predicates are special types of function objects that return Boolean values
...
Unary predicates check a specific property for a
single argument; binary predicates check a specific property for a pair—that is, two—of
arguments
...
In the
STL, a predicate must always return the same result for the same value
...

INSERT ITERATOR
Consider the following statements:
int list[5] = {1, 3, 6, 9, 12};
vector vList;

//Line 1
//Line 2

The statement in Line 1 declares and initializes list to be an array of 5 components; the
statement in Line 2 declares vList to be a vector
...
Now suppose that we want to
copy the elements of list into vList
...
begin());

will not work because no memory space is allocated for the elements of vList, and the copy
function uses the assignment operator to copy the elements from the source to the destination
...
However, there is a
better solution, which is convenient and applicable whenever no memory space is allocated at
the destination
...

back_inserter: This inserter uses the push_back operation of the container in place of

the assignment operator
...
For
example, for the preceding problem, we can copy the elements of list into vList by
using back_inserter as follows:
copy(list, list + 5, back_inserter(vList));
front_inserter: This inserter uses the push_front operation of the container in place

of the assignment operator
...
Because
the vector class does not support the push_front operation, this iterator cannot be
used for the vector container
...
This iterator has two arguments: The first argument is the container itself; the
second argument is an iterator to the container specifying the position at which the
insertion should begin
...

EXAMPLE 13-7
//*************************************************************
// Author: D
...
Malik
//
// This program shows how STL inserters work
...
begin(), vecList1
...
begin(), vecList1
...
begin()));

//Line 17

cout << "Line 18: vecList2: ";
copy(vecList2
...
end(), screenOut);
cout << endl;

//Line 18
//Line 19
//Line 20

list tempList;

//Line 21

copy(vecList2
...
end(),
front_inserter(tempList));

//Line 22

cout << "Line 23: tempList: ";
copy(tempList
...
end(), screenOut);
cout << endl;
}

//Line 14
//Line 15
//Line 16

//Line 23
//Line 24
//Line 25

return 0;

//Line 26
//Line 27

Sample Run:
Line 14: vecList1: 1 2 3 4 5 6 7 8
Line 18: vecList2: 1 2 3 4 5 6 7 8
Line 23: tempList: 8 7 6 5 4 3 2 1

STL Algorithms
This section describes most of the STL algorithms
...
In the function prototypes, the parameter types indicate for
which type of container the algorithm is applicable
...
Throughout, we use abbreviations such as outputItr to
mean output iterator, inputItr to mean input iterator, forwardItr to mean forward
iterator, and so on
...
The element that is used as a filling element is passed as a
parameter to these functions
...
The prototypes of these functions are as follows:

STL Algorithms |

759

template
void fill(forwardItr first, forwardItr last, const Type& value);
template
void fill_n(forwardItr first, size n, const Type& value);

The first two parameters of the function fill are forward iterators specifying the starting
and ending positions of the container; the third parameter is the filling element
...
The program in Example 13-8 illustrates
how to use these functions
...
S
...

//*************************************************************
#include
#include
#include
#include






//Line
//Line
//Line
//Line

1
2
3
4

using namespace std;

//Line 5

int main()
{
vector vecList(8);
ostream_iterator screen(cout, " ");

//Line
//Line
//Line
//Line

6
7
8
9

fill(vecList
...
end(), 2);

//Line 10

cout << "Line 11: After filling vecList with 2's: ";
copy(vecList
...
end(), screen);
cout << endl;

//Line 11
//Line 12
//Line 13

fill_n(vecList
...
begin(), vecList
...
The statement in Line 10 uses the function fill to fill vecList with 2; that is, all
eight elements of vecList are set to 2
...
begin() returns an iterator
to the first element of vecList, and vecList
...
The statement in Line 12 outputs the elements of vecList using
the copy function
...
The first parameter of fill_n is vecList
...
The second parameter of
fill_n is 3, which specifies the number of elements to be filled
...
Therefore, 5 is copied into the first three elements of
vecList
...


Functions generate and generate_n
The functions generate and generate_n are used to generate elements and fill a
sequence
...
The prototypes of
these functions follow:
template
void generate(forwardItr first, forwardItr last, function gen);
template
void generate_n(forwardItr first, size n, function gen);

The function generate fills a sequence in the range first
...
The function generate_n fills a sequence in the range
first
...
Note that gen can also be a pointer to a function
...
The program in
Example 13-9 illustrates how to use these functions
...
S
...

//*************************************************************

STL Algorithms |

#include
#include
#include
#include






//Line
//Line
//Line
//Line

761

1
2
3
4

using namespace std;

//Line 5

int nextNum();

//Line 6

int main()
{
vector vecList(8);
ostream_iterator screen(cout, " ");

//Line
//Line
//Line
//Line

7
8
9
10

generate(vecList
...
end(), nextNum);

//Line 11

cout << "Line 12: vecList after filling with "
<< "numbers: ";

//Line 12

copy(vecList
...
end(), screen);
cout << endl;

//Line 13
//Line 14

generate_n(vecList
...
begin(), vecList
...
A call to this function returns the current value of n and then
increments the value of n
...

The statements in Lines 9 and 10 declare vecList to be a sequence container of size 8, and
screen to be an ostream iterator initialized to cout with the delimit character space
...
Notice that after the statement in Line 11 executes, the value of the
static variable n of nextNum is 9
...
The statement in Line 15 calls the function generate_n to fill the first three
elements of vecList by calling the function nextNum three times
...
begin(), which is the first element of vecList, and the number of elements to
be filled is 3, given by the second parameter of generate_n (see Line 15)
...


Functions find, find_if, find_end, and find_first_of
The functions find, find_if, find_end, and find_first_of are used to find the
elements in a given range
...
The
prototypes of the functions find and find_if are as follows:
template
inputItr find(inputItr first, inputItr last,
const Type& searchValue);
template
inputItr find_if(inputItr first, inputItr last, unaryPredicate op);

The function find searches the range of elements first
...
If searchValue is found in the range, the function returns the position in
the range where searchValue is found; otherwise, it returns last
...
last-1 for the element for which
op(rangeElement) is true
...

Example 13-10 illustrates how to use the functions find and find_if
...

char cList[10] = {'a', 'i', 'C', 'd', 'e', 'f',
'o', 'H', 'u', 'j'};
vector charList(cList, cList + 10);
vector::iterator position;

//Line 1
//Line 2
//Line 3

After the statement in Line 2 executes, the vector container charList is as follows:
charList = {'a', 'i', 'C', 'd', 'e', 'f', 'o', 'H', 'u', 'j'};

Consider the following statement:
position = find(charList
...
end(), 'd');

STL Algorithms |

763

This statement searches charList for the first occurrence of 'd' and returns an iterator,
which is stored in position
...
Therefore, position points to the element at position 3 in charList
...
begin(), charList
...
Note that the function isupper from the header file cctype is passed as
the third parameter to the function find_if
...
Therefore, after this statement executes, position points to the
third element of charList
...


Next, we describe the functions find_end and find_first_of
...
The prototypes of the function find_end are as follows:
template
forwardItr1 find_end(forwardItr1 first1, forwardItr1 last1,
forwardItr2 first2, forwardItr2 last2);
template class binaryPredicate>
forwardItr1 find_end(forwardItr1 first1, forwardItr1 last1,
forwardItr2 first2, forwardItr2 last2,
binaryPredicate op);

Both forms of the function find_end search the range first1
...
last2-1
...
last1-1 where the match occurs; otherwise, it returns
last1
...
last1-1 where the range first2
...
last1-1
...

The prototypes of the function find_first_of are as follows:
template
forwardItr1 find_first_of(forwardItr1 first1, forwardItr1 last1,
forwardItr2 first2, forwardItr2 last2);
template class binaryPredicate>
forwardItr1 find_first_of(forwardItr1 first1, forwardItr1 last1,
forwardItr2 first2, forwardItr2 last2,
binaryPredicate op);

1
3

764 |

Chapter 13: Standard Template Library (STL) II

The first form returns the position, within the range first1
...
last2-1 that is also in the range first1
...
The
second form returns the position, within the range first1
...
last2-1 for which op(elemRange1, elemRange2) is true
...

Example 13-11 illustrates how to use the functions find_end and find_first_of
...
The last occurrence of list2 in list1 starts at position 6
(that is, at the seventh element)
...

Now consider the following statement:
location = find_first_of(list1, list1 + 10, list3, list3 + 5);

This statement uses the function find_first_of to find the position in list1 where
the first element of list3 is also an element of list1
...
Therefore, after this statement executes, location points to the element at
position 1, in list1, which is the second element of list1
...


Functions remove, remove_if, remove_copy,
and remove_copy_if
The function remove is used to remove certain elements from a sequence; the function
remove_if is used to remove the elements from a sequence by using some criteria
...
Similarly, the function remove_copy_if copies the
elements of a sequence into another sequence by excluding certain elements, using some
criteria, of the first sequence
...


STL Algorithms |

765

The prototypes of the functions remove and remove_if are as follows:
template
forwardItr remove(forwardItr first, forwardItr last,
const Type& value);
template
forwardItr remove_if(forwardItr first, forwardItr last,
unaryPredicate op);

The function remove removes each occurrence of a given element in the range
first
...
The element to be removed is passed as the third parameter to this
function
...
last-1,
for which the predicate op(element) is true
...
These
functions do not modify the size of the container; in fact, the elements are moved to the
beginning of the container
...
The
function returns a pointer to element 9 (which is after 5)
...
(See Lines 17, 19, 21, and 23
...
last1-1,
except the elements specified by value, into the sequence starting at the position
destFirst
...
last1-1, except the elements for which op(element) is true, into
the sequence starting at the position destFirst
...

The program in Example 13-12 shows how to use the functions remove, remove_if,
remove_copy, and remove_copy_if
...
S
...

//*************************************************************

1
3

766 |

Chapter 13: Standard Template Library (STL) II

#include
#include
#include
#include
#include







//Line
//Line
//Line
//Line
//Line

1
2
3
4
5

using namespace std;

//Line 6

bool lessThanEqualTo50(int num);

//Line 7

int main()
{
char cList[10] = {'A', 'a', 'A', 'B', 'A',
'c', 'D', 'e', 'F', 'A'};

//Line 8
//Line 9
//Line 10

vector charList(cList, cList + 10);
vector::iterator lastElem, newLastElem;

//Line 11
//Line 12

ostream_iterator screen(cout, " ");

//Line 13

cout << "Line 14: Character list: ";
copy(charList
...
end(), screen);
cout << endl;

//Line 14
//Line 15
//Line 16

//remove
lastElem = remove(charList
...
end(), 'A');

//Line 17

cout << "Line 18: Character list after removing A: "; //Line 18
copy(charList
...
begin(),
lastElem, isupper);
cout << "Line 22: Character list after removing "
<< "the uppercase letters: " << endl;
copy(charList
...
begin(), intList
...
begin(), intList
...
begin(), 34);
//Line 33
cout << "Line 34: temp1 after copying all the "
<< "elements of intList except 34: " << endl;
copy(temp1
...
begin(), intList
...
begin(), lessThanEqualTo50);

//Line 38

cout << "Line 39: temp2 after copying all the elements of "
<< "intList except \nnumbers less than 50: ";
//Line 39
copy(temp2
...
end(), screenOut);
//Line 40
cout << endl;
//Line 41
}

return 0;

bool lessThanEqualTo50(int num)
{
return (num <= 50);
}

//Line 42
//Line 43
//Line
//Line
//Line
//Line

44
45
46
47

Sample Run:
Line 14: Character list: A a A B A c D e F A
Line 18: Character list after removing A: a B c D e F
Line 22: Character list after removing the uppercase letters:
a c e
Line 29: intList: 12 34 56 21 34 78 34 55 12 25
Line 34: temp1 list after copying all the elements of intList except 34:
12 56 21 78 55 12 25
Line 39: temp2 after copying all the elements of intList except
numbers less than 50: 56 78 55 0 0 0 0 0 0 0

The statement in Line 11 creates a vector list, charList, of type char, and initializes
charList using the array cList created in Line 10
...
The statement in Line 13 declares an
ostream iterator, screen
...
The
statement in Line 17 uses the function remove to remove all the occurrences of 'A' from
charList
...
The statement in Line 19 outputs the elements in the new
range
...
begin()
...
) The statement in Line 21 uses the function
remove_if to remove the uppercase letters from the list charList and stores the pointer
returned by the function remove_if in newLastElem
...

The statement in Line 26 creates a vector, intList, of type int and initializes intList
using the array list, created in Line 25
...
The statement in Line 33 copies all the elements, except the occurrences of
34, of intList into temp1
...
The statement in Line 35
outputs the elements of temp1
...
The statement in
Line 38 uses the function remove_copy_if to copy those elements of intList that are
less than 50
...


Functions replace, replace_if, replace_copy,
and replace_copy_if
The function replace is used to replace all the occurrences, within a given range, of a
given element with a new value
...
The
prototypes of these functions are as follows:
template
void replace(forwardItr first, forwardItr last,
const Type& oldValue, const Type& newValue);
template
void replace_if(forwardItr first, forwardItr last,
unaryPredicate op, const Type& newValue);

The function replace replaces all the elements in the range first
...
The function
replace_if replaces all the elements in the range first
...

The function replace_copy is a combination of replace and copy
...
Let us first look
at the prototypes of the functions replace_copy and replace_copy_if:
template
outputItr replace_copy(forwardItr first, forwardItr last,
outputItr destFirst,
const Type& oldValue,
const Type& newValue);
template class unaryPredicate, class Type>

STL Algorithms |

769

outputItr replace_copy_if(forwardItr first, forwardItr last,
outputItr destFirst,
unaryPredicate op,
const Type& newValue);

The function replace_copy copies all the elements in the range first
...
If the value of an element in this range is equal to
oldValue, it is replaced by newValue
...
last-1 into the container starting at destFirst
...
Both of these functions return an outputItr (a pointer) positioned one past
the last element copied at the destination
...

EXAMPLE 13-13
Consider the following statements:
char cList[10] = {'A', 'a', 'A', 'B', 'A',
'c', 'D', 'e', 'F', 'A'};
vector charList(cList, cList + 10);

//Line 1
//Line 2

After the statement in Line 2 executes, the vector container charList is as follows:
charList = {'A', 'a', 'A', 'B', 'A', 'c',
'D', 'e', 'F', 'A'}

//Line 3

Now consider the following statement:
replace(charList
...
end(), 'A', 'Z'); //Line 4

This statement uses the function replace to replace all the occurrences of 'A' with 'Z'
in charList
...
begin(), charList
...
After this statement executes, charList is as follows:
charList ={'*', 'a', '*', '*', '*', 'c', '*',
'e', '*', '*'}

//Line 7

Next suppose that you have the following statements:
int list[10] = {12, 34, 56, 21, 34, 78, 34, 55, 12, 25}; //Line 8
vector intList(list, list + 10);
//Line 9
vector temp(10);
//Line 10

1
3

770 |

Chapter 13: Standard Template Library (STL) II

The statement in Line 9 creates a vector, intList, of type int and initializes intList using
the array list, created in Line 8
...
Next consider the following
statement:
replace_copy(intList
...
end(),
temp1
...
The list
intList is not modified
...
Consider the following statement:
replace_copy_if(intList
...
end(),
temp
...
Notice that the fourth
parameter of the function replace_copy_if is the function lessThanEqualTo50
...


Functions swap, iter_swap, and swap_ranges
The functions swap, iter_swap, and swap_ranges are used to swap elements
...
The prototypes of these functions are as follows:
template
void swap(Type& object1, Type& object2);
template
void iter_swap(forwardItr1 first, forwardItr2 second);

STL Algorithms |

771

template
forwardItr2 swap_ranges(forwardItr1 first1, forwardItr1 last1,
forwardItr2 first2);

The function swap swaps the values of object1 and object2
...

The function swap_ranges swaps the elements of the range first1
...
It returns the iterator of the second
range positioned one past the last element swapped
...

EXAMPLE 13-14
//*************************************************************
// Author: D
...
Malik
//
// This program shows how the STL functions swap, iter_swap,
// and swap_ranges work
...
begin(), charList
...
begin(), charList
...
begin() + 2,
charList
...
begin(), charList
...
begin() + 4;
iter_swap(charItr, charItr + 1);

//Line 23
//Line 24

cout << "Line 25: Character list after swapping the "
<< "fifth and sixth elements: " << endl;
//Line 25
copy(charList
...
end(), screen);
//Line 26
cout << endl << endl;
//Line 27
int list[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

//Line 28

vector intList(list, list + 10);

//Line 29

ostream_iterator screenOut(cout, " ");

//Line 30

cout << "Line 31: intList: ";
copy(intList
...
end(), screenOut);
cout << endl;

//Line 31
//Line 32
//Line 33

//swap_ranges
swap_ranges(intList
...
begin() + 4,
intList
...
begin(), intList
...
The statement in Line 13 outputs the values of charList
...
The statement in Line 19, using
the function iter_swap, swaps the third and fourth elements of charList
...
) After the statement in Line 23 executes,
charItr points to the fifth element of charList
...
The statement in Line 26
outputs the values of the elements of charList
...
)
The statement in Line 29 creates the vector intList and initializes it using the array
declared in Line 28
...
The statement in Line 34 uses the function swap_ranges to swap the first four
elements of intList with the four elements of intList starting at the sixth element of
intList
...
(In the output, the
line marked Line 35 contains the output of Lines 35 through 37 of the program
...
These functions are defined in the header file algorithm
...
last1-1 and first2
...
last1-1 where the range
first2
...
last1-1
...
For the second form,
the comparison op(elemFirstRange, elemSecondRange) must be true
...
last1-1 where the
match occurs; otherwise, the function returns last1
...
last-1, the function search_n searches count for
any consecutive occurrences of value
...
last-1 where a subsequence of count consecutive elements have values equal
to value
...
last-1 where a
subsequence of count consecutive elements exists for which op(elemRange, value) is
true
...

The prototypes of the function sort are as follows:
template
void sort(randomAccessItr first, randomAccessItr last);
template
void sort(randomAccessItr first, randomAccessItr last,
compare op);

The first form of the sort function reorders the elements in the range first
...
The second form reorders the elements according to the criteria
specified by op
...
last-1, and
false otherwise
...

Example 13-15 illustrates how to use these searching and sorting functions
...
S
...

//*************************************************************

STL Algorithms |

#include
#include
#include
#include






//Line
//Line
//Line
//Line

775

1
2
3
4

using namespace std;

//Line 5

int main()
{
int intList[15] = {12, 34, 56, 34, 34, 78, 38, 43,
12, 25, 34, 56, 62, 5, 49};

//Line 6
//Line 7
//Line 8

vector vecList(intList, intList + 15);
int list[2] = {34, 56};

//Line 9
//Line 10

vector::iterator location;

//Line 11

ostream_iterator screenOut(cout, " ");

//Line 12

cout << "Line 13: vecList: ";
copy(vecList
...
end(), screenOut);
cout << endl;

//Line 13
//Line 14
//Line 15

cout << "Line 16: list: ";
copy(list, list + 2, screenOut);
cout << endl;

//Line 16
//Line 17
//Line 18

//search
location = search(vecList
...
end(),
list, list + 2);

//Line 19

if (location != vecList
...
The "
<< "first occurrence of \n
list in vecList "
<< "is at position: "
<< (location - vecList
...
begin(), vecList
...
end())
//Line
cout << "Line 26: Two consecutive occurrences of "
<< "34 found in \n
vecList at position: "
<< (location - vecList
...
begin(), vecList
...
begin(), vecList
...
begin(),
vecList
...
The first occurrence of
list in vecList is at position: 1
Line 26: Two consecutive occurrences of 34 found in
vecList at position: 3
Line 30: vecList after sorting:
5 12 12 25 34 34 34 34 38 43 49 56 56 62 78
Line 36: 43 found in vecList

The statement in Line 9 creates a vector, vecList, and initializes it using the array intList
created in Line 8
...
The statement in Line 14 outputs vecList
...
The statements in Lines 20 through 23
output the result of the search; see the line marked Line 21 in the output
...
The statements in Lines 25 through 28
output the result of the search
...
The statement in Line
31 outputs vecList
...

The statement in Line 34 uses the function binary_search to search vecList
...


STL Algorithms |

777

Functions adjacent_find, merge, and inplace_merge
The algorithm adjacent_find is used to find the first occurrence of consecutive
elements that meet certain criteria
...
In the second form, the algorithm returns an
iterator to the element in the range first
...
last-1 and nextElem is an element
in this range next to elem
...

Suppose that intList is a list container of type int
...
begin(), intList
...
The statement in Line 3 uses the function adjacent_find to find
the position of the (first set of) consecutive identical elements
...
After the
statement in Line 3 executes, listItr points to the second element of intList
...
Further assume that
vecList is as follows:
vecList = {1, 3, 5, 7, 9, 0, 2, 4, 6, 8};

//Line 4

Consider the following statements:
vector::iterator intItr;
//Line 5
intItr = adjacent_find(vecList
...
end(),
greater());
//Line 6

The statement in Line 5 declares intItr to be a vector iterator that can point to any
vector container of type int
...
Notice that the third parameter of the function adjacent_find is the
binary predicate greater, which returns the position in vecList where the first element is
greater than the second element
...
After
the statement in Line 6 executes, intItr points to the element 9
...
The algorithm merge merges the sorted lists
...
Both lists must be sorted according to the same criteria
...
The prototypes of the
functions to implement the merge algorithms are as follows:
template class outputItr>
outputItr merge(inputItr1 first1, inputItr1 last1,
inputItr2 first2, inputItr2 last2,
outputItr destFirst);
template class outputItr, class binaryPredicate>
outputItr merge(inputItr1 first1, inputItr1 last1,
inputItr2 first2, inputItr2 last2,
outputItr destFirst, binaryPredicate op);

Both forms of the algorithm merge merge the elements of the sorted ranges
first1
...
last2-1
...
The first form uses the less-than
operator, <, for ordering the elements
...
Both forms
return the position after the last copied element in the destination range
...

Consider the following statements:
int list1[5] = {0, 2, 4, 6, 8};
int list2[5] = {1, 3, 5, 7, 9};

//Line 7
//Line 8

list intList;
merge(list1, list1 + 5, list2, list2 + 5,
back_inserter(intList));

//Line 9
//Line 10

The statements in Lines 7 and 8 create the sorted arrays list1 and list2
...
The statement in Line 10
uses the function merge to merge list1 and list2
...
After the statement in Line 10 executes, intList contains the merged list,
that is,
intList = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

The algorithm inplace_merge is used to combine the sorted consecutive sequence
...
middle-1 and
middle
...
The merged elements overwrite the two ranges beginning at
first
...
The second form uses the binary predicate op to merge the sequences; that
is, for the elements of the two sequences, op(elemSeq1, elemSeq2) must be true
...
Further suppose that vecItr is a vector iterator
pointing to element 2
...
begin(), vecItr, vecList
...


Functions reverse, reverse_copy, rotate,
and rotate_copy
The algorithm reverse reverses the order of the elements in a given range
...
last-1 are reversed
...

The algorithm reverse_copy reverses the elements of a given range while copying into
a destination range
...
The prototype of the function implementing the reverse_copy algorithm is as follows:
template
outputItr reverse_copy(biDirectionalItr first,
biDirectionalItr last,
outputItr destFirst);

The elements in the range first
...
The function also returns the position one past the last
element copied at the destination
...
Its prototype is as follows:
template
void rotate(forwardItr first, forwardItr newFirst,
forwardItr last);

The elements in the range first
...

The element specified by newFirst becomes the first element of the range
...
Then, after the statement
rotate(vecList
...
end());

executes, vecList is as follows:
vecList = {0, 7, 8, 2, 5, 3, 5, 4}

The algorithm rotate_copy is a combination of rotate and copy
...
The source is not modified
...
last-1 are copied into the destination range
beginning with destFirst in the rotated order so that the element specified by
middle in the range first
...

The function also returns the position one past the last element copied at the destination
...
The program in Example 13-16 illustrates how to use these
algorithms
...
S
...

//*************************************************************
#include
#include
#include
#include






//Line
//Line
//Line
//Line

1
2
3
4

STL Algorithms |

using namespace std;

//Line 5

int main()
{
int temp[10] = {1, 3, 5, 7, 9, 0, 2, 4, 6, 8};

781

//Line 6
//Line 7
//Line 8

list intList(temp, temp + 10);
list resultList;
list::iterator listItr;

//Line 9
//List 10
//Line 11

ostream_iterator screen(cout, " ");

//Line 12

cout << "Line 13: intList: ";
copy(intList
...
end(), screen);
cout << endl;

//Line 13
//Line 14
//Line 15

reverse(intList
...
end());

//reverse Line 16

cout << "Line 17: intList after reversal: ";
copy(intList
...
end(), screen);
cout << endl;

//Line 17
//Line 18
//Line 19

reverse_copy(intList
...
end(),
back_inserter(resultList)); //reverse_copy Line 20
cout << "Line 21: resultList: ";
//Line 21
copy(resultList
...
end(), screen); //Line 22
cout << endl;
//Line 23
listItr = intList
...
begin(), intList
...
begin(), listItr, intList
...
begin(), intList
...
clear();

//Line 34

rotate_copy(intList
...
end(),
back_inserter(resultList)); //rotate_copy Line 35
cout << "Line 36: intList after rotating and "
<< "copying: ";
copy(intList
...
end(), screen);
cout << endl;

//Line 36
//Line 37
//Line 38

1
3

782 |

Chapter 13: Standard Template Library (STL) II

cout << "Line 39: resultList after rotating and "
<< "copying: ";
//Line 39
copy(resultList
...
end(), screen); //Line 40
cout << endl;
//Line 41
resultList
...
begin(),
find(intList
...
end(), 6), intList
...
begin(), resultList
...
The details are left as an exercise for you
...
The
prototype of the function implementing this algorithm is as follows:
template
iterator_traits:: difference_type
count(inputItr first, inputItr last, const Type& value);

The function count returns the number of times the value specified by the parameter
value occurs in the range first
...

The algorithm count_if counts the occurrences of a given value in a given range
satisfying a certain criterion
...
last-1
for which op(elemRange) is true
...
It has two forms, as
shown by the following prototypes:
template
const Type& max(const Type& aVal, const Type& bVal);
template
const Type& max(const Type& aVal, const Type& bVal, compare comp);

In the first form, the greater-than operator associated with Type is used
...

The algorithm max_element is used to determine the largest element in a given range
...
last-1
...
Both forms return an iterator to the element containing the largest value
in the range first
...

The algorithm min is used to determine the minimum of two values
...
In the second form,
the comparison operation specified by comp is used
...

This algorithm has two forms, as shown by the following prototypes:
template
forwardItr min_element(forwardItr first, forwardItr last);
template
forwardItr min_element(forwardItr first, forwardItr last,
compare comp);

1
3

784 |

Chapter 13: Standard Template Library (STL) II

The first form uses the less-than operator associated with the data type of the elements in
the range first
...
In the second form, the comparison operation specified by
comp is used
...
last-1
...
There are two forms of this algorithm, as shown by the following prototypes:
template
void random_shuffle(randomAccessItr first,
randomAccessItr last);
template
void random_shuffle(randomAccessItr first,
randomAccessItr last,
randomAccessGenerator rand);

The first form reorders the elements in the range first
...
The second form reorders the elements in the
range first
...

Example 13-17 illustrates how to use these functions
...
S
...

//*************************************************************
#include
#include
#include
#include
#include







//Line
//Line
//Line
//Line
//Line

1
2
3
4
5

using namespace std;

//Line 6

int main()
{
char cList[10] = {'Z', 'a', 'Z', 'B', 'Z',
'c', 'D', 'e', 'F', 'Z'};

//Line 7
//Line 8
//Line 9

vector charList(cList, cList + 10);

//Line 10

ostream_iterator screen(cout, " ");

//Line 11

cout << "Line 12: charList: ";
copy(charList
...
end(), screen);
cout << endl;

//Line 12
//Line 13
//Line 14

STL Algorithms |

785

int noOfZs = count(charList
...
end(),
'Z');
//count; Line 15
cout << "Line 16: Number of Z\'s in charList:"
<< noOfZs << endl;

//Line 16

int noOfUpper = count_if (charList
...
end(),
isupper);
//count_if; Line 17
cout << "Line 18: Number of uppercase letters in "
<< "charList: " << noOfUpper << endl;

//Line 18

int list[10] = {12, 34, 56, 21, 34,
78, 34, 55, 12, 25};

//Line 19

ostream_iterator screenOut(cout, " ");

//Line 20

cout << "Line 21: list: ";
copy(list, list + 10, screenOut);
cout << endl;

//Line 21
//Line 22
//Line 23

int *maxLoc = max_element(list,
list + 10);

//max_element; Line 24

cout << "Line 25: Largest element in list: "
<< *maxLoc << endl;

//Line 25

int *minLoc = min_element(list,
list + 10); //min_element; Line 26
cout << "Line 27: Smallest element in list: "
<< *minLoc << endl;
random_shuffle(list, list + 10);

//Line 27

//random_shuffle; Line 28

cout << "Line 29: list after random shuffle: ";
copy(list, list + 10, screenOut);
cout << endl;
}

//Line 29
//Line 30
//Line 31

return 0;

//Line 32
//Line 33

Sample Run:
Line
Line
Line
Line
Line
Line
Line

12:
16:
18:
21:
25:
27:
29:

charList: Z a Z B Z c D e F Z
Number of Z's in charList:4
Number of uppercase letters in charList: 7
list: 12 34 56 21 34 78 34 55 12 25
Largest element in list: 78
Smallest element in list: 12
list after random shuffle: 12 34 25 56 12 78 55 21 34 34

The preceding output is self-explanatory
...


1
3

786 |

Chapter 13: Standard Template Library (STL) II

Functions for_each and transform
The algorithm for_each is used to access and process each element in a given range by
applying a function, which is passed as a parameter
...
last-1
...
The returned value of
the function for_each is usually ignored
...
The prototypes of the functions implementing
this algorithm are as follows:
template class unaryOperation>
outputItr transform(inputItr first, inputItr last,
outputItr destFirst,
unaryOperation op);
template class outputItr, class binaryOperation>
outputItr transform(inputItr1 first1, inputItr1 last,
inputItr2 first2,
outputItr destFirst,
binaryOperation bOp);

The first form of the function transform has four parameters
...
last-1
...

The second form of the function transform has five parameters
...
last1-1 and the
range beginning with first2
...
The function returns the position one element past the last
element copied at the destination
...

EXAMPLE 13-18
//*************************************************************
// Author: D
...
Malik
//
// This program shows how the STL functions for_each and
// transform work
...
begin(), charList
...
begin(), charList
...
begin(), toupper);

//Line 16

cout << "Line 17: charList after changing all lowercase"
<< " letters to \n
uppercase: ";
//Line 17
copy(charList
...
end(), screen);
//Line 18
cout << endl;
//Line 19
int list[7] = {2, 8, 5, 1, 7, 11, 3};

//Line 20

ostream_iterator screenOut(cout, " ");

//Line 21

cout << "Line 22: list: ";
copy(list, list + 7, screenOut);
cout << endl;

//Line 22
//Line 23
//Line 24

cout << "Line 25: The effect of the for_each "
<< "function: ";
for_each(list, list + 7, doubleNum);
cout << endl;

//Line 25
//Line 26
//Line 27

cout << "Line 28: list after a call to the for_each "
<< "function: ";
//Line 28
copy(list, list + 7, screenOut);
//Line 29
cout << endl;
//Line 30
}

return 0;

//Line 31
//Line 32

1
3

788 |

Chapter 13: Standard Template Library (STL) II

void doubleNum(int& num)
{
num = 2 * num;
cout << num << " ";
}

//Line
//Line
//Line
//Line
//Line

33
34
35
36
37

Sample Run:
Line 13: cList: a b c d e
Line 17: cList after changing all lowercase letters to
uppercase: A B C D E
Line 22: list: 2 8 5 1 7 11 3
Line 25: The effect of the for_each function: 4 16 10 2 14 22 6
Line 28: list after a call to the for_each function: 4 16 10 2 14 22 6

The statement in Line 16 uses the function transform to change every lowercase letter
of cList into its uppercase counterpart
...
Notice that the
fourth parameter of the function transform (in Line 16) is the function toupper from
the header file cctype
...
The function doubleNum has a reference parameter, num, of
type int
...
Because num is a reference parameter, the value of the actual parameter is changed
...
The statement in Line 29 outputs the values of the elements of list
...


Functions includes, set_intersection, set_union,
set_difference, and set_symmetric_difference
This section describes the set theory operations includes (subset), set_intersection,
set_union, set_difference, and set_symmetric_difference
...

The algorithm includes determines whether the elements in one range appear in
another range
...
last1-1 and first2
...
The function returns true if all the elements in the range first2
...
last1-1
...
last1-1 contains all the elements in the range first2
...
The
first form assumes that the elements in both ranges are in ascending order
...

Example 13-19 illustrates how the function includes works
...
S
...

// This function assumes that the elements in the given ranges
// are ordered according to some sorting criterion
...
The details are left as exercise for you
...
This algorithm has two forms, as shown by the following prototypes:
template class outputItr>
outputItr set_intersection(inputItr1 first1, inputItr1 last1,
inputItr2 first2, inputItr2 last2,
outputItr destFirst);
template class outputItr, class binaryPredicate>
outputItr set_intersection(inputItr1 first1, inputItr1 last1,
inputItr2 first2, inputItr2 last2,
outputItr destFirst,
binaryPredicate op);

Both forms create a sequence of sorted elements that are common to two sorted ranges,
first1
...
last2-1
...
Both forms return an iterator positioned one past
the last element copied at the destination range
...
The second form assumes that both ranges are sorted using the
operation specified by op
...

Suppose that
setA[5] = {2, 4, 5, 7, 8};
setB[7] = {1, 2, 3, 4, 5, 6, 7};
setC[5] = {2, 5, 8, 8, 15};

STL Algorithms |

791

setD[6] = {1, 4, 4, 6, 7, 12};
setE[7] = {2, 3, 4, 4, 5, 6, 10};

Then
AintersectB = {2, 4, 5, 7}
AintersectC = {2, 5, 8}
DintersectE = {4, 4, 6}

Notice that because 8 appears only once in setA, 8 appears only once in AintersectC,
even though 8 appears twice in setC
...

The algorithm set_union is used to find the elements that are contained in two ranges of
elements
...
last1 - 1 or first2
...
The created sequence is placed in the
container beginning with destFirst
...
The first form assumes that the elements
are in ascending order
...
The elements in the source ranges are not modified
...
Then
AunionB
AunionC
BunionD
DunionE

=
=
=
=

{1,
{2,
{1,
{1,

2,
4,
2,
2,

3,
5,
3,
3,

4,
7,
4,
4,

5,
8,
4,
4,

6,
8,
5,
5,

7, 8}
15}
6, 7, 12}
6, 7, 10, 12}

Notice that because 8 appears twice in setC, it appears twice in AunionC
...

We leave it as an exercise for you to write a program that further illustrates how to use the
functions set_union and set_intersection; see Programming Exercise 5 at the end
of this chapter
...
This algorithm has two forms, as shown
by the following prototypes:

1
3

792 |

Chapter 13: Standard Template Library (STL) II

template class outputItr>
outputItr set_difference(inputItr1 first1, inputItr1 last1,
inputItr2 first2, inputItr2 last2,
outputItr destFirst);
template class outputItr, class binaryPredicate>
outputItr set_difference(inputItr1 first1, inputItr1 last1,
inputItr2 first2, inputItr2 last2,
outputItr destFirst,
binaryPredicate op);

Both forms create a sequence of sorted elements that are in the sorted range
first1
...
last2-1
...
Both forms return an
iterator positioned one past the last element copied at the destination range
...
The second form assumes that
both ranges are sorted using the operation specified by op
...

Suppose that
setA
setC
setD
setE

=
=
=
=

{2,
{1,
{2,
{1,

4,
5,
5,
5,

5,
6,
5,
7,

7,
8,
6,
9,

8}
15}
9}
12}

Then
AdifferenceC = {2, 4, 7}
DdifferenceE = {2, 5, 6}

Because 5 appears twice in setD but only once in setE, 5 appears once in DdifferenceE
...
last1-1 but not in first2
...
last2-1 but not in first1
...
In other words, the sequence of
elements created by set_symmetric_difference contains the elements that are in
range1_difference_range2 union range2_difference_range1
...
Both forms return an iterator positioned
one past the last element copied at the destination range
...
The second form assumes that both ranges are sorted using the
operation specified by op
...
It can be shown
that the sequence created by set_symmetric_difference contains elements that are in
range1_union_range2, but not in range1_intersection_range2
...
Therefore,
BsymDiffC = {1, 3, 4, 7, 10, 15}

Now DdifferenceC = {2, 5, 9, 15} and CdifferenceD = {1, 8, 15}
...

EXAMPLE 13-20
Suppose that we have the following statements:
int setA[5] = {2, 4, 5, 7, 8};
int setB[7] = {3, 4, 5, 6, 7, 8, 10};
int setC[5] = {1, 5, 6, 8, 15};

//Line 1
//Line 2
//Line 3

int AdifferenceC[5];
int BsymDiffC[10];

//Line 4
//Line 5

Consider the following statement:
set_difference(setA, setA + 5, setC, setC + 5, AdifferenceC);

//Line 6

After this statement executes, AdifferenceC contains the elements that are in setA and
not in setC, that is,
AdifferenceC = {2, 4, 7}

//Line 7

Now consider the following statement:
set_symmetric_difference(setB, setB + 7, setC, setC + 5,
BsymDiffC);
//Line 8

1
3

794 |

Chapter 13: Standard Template Library (STL) II

After this statement executes, BsymDiffC contains the elements that are in setB, but not
in setC or the elements that are in setC, but not in setB, that is,
BsymDiffC = {1, 3, 4, 7, 10, 15}

//Line 9

We leave it as an exercise for you to write a program that further illustrates how to use the
functions set_difference and set_symmetric_difference; see Programming Exercise 6 at the end of this chapter
...
Each of these
functions has two forms
...

For example, the algorithm accumulate finds the sum of all the elements in a given
range
...
For example, rather than add the elements of a given range, we can specify the
multiplication operation to the algorithm accumulate to multiply the elements of the
range
...
The algorithms are contained in the header file numeric
...
last-1
...
In the second form,
we can specify a binary operation, such as multiplication, to be applied to the elements of
the range
...

Next, we describe the algorithm adjacent_difference
...
last-1, and all the other elements are the differences
of the current and previous elements
...
The second
element is equal to the second element in the original range minus the first element in the
original range
...

In the second form of adjacent_difference, the binary operation op is applied to the
elements in the range
...
For example, if the sequence is {2, 5, 6, 8, 3, 7} and the operation is
multiplication, the resulting sequence is {2, 10, 30, 48, 24, 21}
...

Example 13-21 illustrates how the functions accumulate and adjacent_difference
work
...
S
...

//*************************************************************
#include
#include
#include
#include
#include
#include








//Line
//Line
//Line
//Line
//Line
//Line

1
2
3
4
5
6

using namespace std;

//Line 7

void print(vector vList);

//Line 8

int main()
{
int list[8] = {1, 2, 3, 4, 5, 6, 7, 8};

//Line 9
//Line 10
//Line 11

vector vecList(list, list + 8);
vector newVList(8);

//Line 12
//Line 13

1
3

796 |

Chapter 13: Standard Template Library (STL) II

cout << "Line 14: vecList: ";
print(vecList);

//Line 14
//Line 15

int sum = accumulate(vecList
...
end(), 0); //accumulate; Line 16
cout << "Line 17: Sum of the elements of vecList = "
<< sum << endl;
//Line 17
int product = accumulate(vecList
...
end(),
1, multiplies());
//Line 18
cout << "Line 19: Product of the elements of "
<< "vecList = " << product << endl;

//Line 19

adjacent_difference(vecList
...
end(),
newVList
...
begin(), vecList
...
begin(), multiplies()); //Line 23
cout << "Line 24: newVList: ";
print(newVList);
}

//Line 24
//Line 25

return 0;

//Line 26
//Line 27

void print(vector vList)
{
ostream_iterator screenOut(cout, " ");

}

copy(vList
...
end(), screenOut);
cout << endl;

//Line 28
//Line 29
//Line 30
//Line 31
//Line 32
//Line 33

Sample Run:
Line
Line
Line
Line
Line

14:
17:
19:
21:
24:

vecList: 1 2 3 4 5 6 7 8
Sum of the elements of vecList = 36
Product of the elements of vecList = 40320
newVList: 1 1 1 1 1 1 1 1
newVList: 1 2 6 12 20 30 42 56

The preceding output is self-explanatory
...

The algorithm inner_product is used to manipulate the elements of two ranges
...
last - 1
and the range of elements starting with first2, and the products of the elements are
added to the value specified by the parameter init
...

The first form computes
init = init + elem1 * elem2

for all the corresponding elements
...
The function computes and returns
0 + 2 * 1 + 4 * 4 + 7 * 6 + 8 * 9 = 132

In the second form, the default addition can be replaced by the operation specified by
op1, and the default multiplication can be replaced by the operation specified by op2
...
last-1 up to the position of the element
...
last-1, the second element is the sum of the first two elements in the range
first
...
last-1, and so on
...

For example, if the sequence is
{1, 3, 4, 6}

1
3

798 |

Chapter 13: Standard Template Library (STL) II

and the operation is multiplication, the function partial_sum generates the following
sequence:
{1, 3, 12, 72}

The created sequence is copied at the destination specified by destFirst, and returns an
iterator positioned one past the last copied element at the destination
...

EXAMPLE 13-22
Suppose that you have the following statement:
int list1[8] = {1, 2, 3, 4, 5, 6, 7, 8};
int list2[8] = {2, 4, 5, 7, -9, 11, 12, 14};

//Line 1
//Line 2

vector vecList(list1, list1 + 8);
vector newVList(list2, list2 + 8);

//Line 3
//Line 4

int sum;

//Line 5

After the statements in Lines 3 and 4 execute,
vecList = {1, 2, 3, 4, 5, 6, 7, 8}
newVList = {2, 4, 5, 7, -9, 11, 12, 14}

//Line 6
//Line 7

Now consider the following statement:
sum = inner_product(vecList
...
end(),
newVList
...
begin(), vecList
...
begin(), 0,
plus(), minus());
//Line 9

This statement calculates the inner product of vecList and newVList
...
begin(), vecList
...
begin());

//Line 10

This statement uses the function partial_sum to generate the sequence of elements 1,
3, 6, 10, 15, 21, 28, 36
...
begin(), vecList
...
begin(), multiplies());

//Line 11

This statement uses the function partial_sum to generate the sequence of elements 1,
2, 6, 24, 120, 720, 5040, 40320
...

These elements are assigned to newVList, that is,
newVList = {1, 2, 6, 24, 120, 720, 5040, 40320}

We leave it as an exercise for you to write a program that further illustrates how to use the
functions inner_product and partial_sum; see Programming Exercise 7 at the end of
this chapter
...

2
...

4
...


6
...


8
...


The STL provides class templates that process lists, stacks, and queues
...

Algorithms are used to manipulate the elements in a container
...

The class pair allows you to combine two values into a single unit
...
The classes map
and multimap use the class pair to manage their elements
...

The function make_pair allows you to create pairs without explicitly
specifying the type pair
...

Elements in an associative container are automatically sorted according to
some ordering criterion
...

The predefined associative containers in the STL are sets, multisets,
maps, and multimaps
...

11
...

13
...


15
...


17
...

19
...

21
...

23
...

25
...

27
...

29
...


Chapter 13: Standard Template Library (STL) II

Containers of type set do not allow duplicates
...

The name of the class defining the container set is set
...

The name of the header file containing the definition of the classes set
and multiset, and the definitions of the functions to implement various
operations on these containers, is set
...

The containers map and multimap manage their elements in the form key/
value
...

The default sorting criterion for the key of the containers map and
multimap is the relational operator < (less than)
...
For userdefined data types, such as classes, the relational operators must be properly overloaded
...

The name of the class defining the container map is map
...

The name of the header file containing the definitions of the classes map
and multimap, and the definitions of the functions to implement various
operations on these containers, is map
...

The main categories of STL algorithms are nonmodifying, modifying,
numeric, and heap
...

Modifying algorithms modify the elements of the container by rearranging,
removing, and/or changing the values of the elements
...

Numeric algorithms are designed to perform numeric calculations on the
elements of a container
...

The predefined arithmetic function objects are plus, minus, multiplies,
divides, modulus, and negate
...


Quick Review |

31
...

33
...

35
...

37
...

39
...

41
...

43
...

45
...

47
...


49
...

51
...

Predicates are special types of function objects that return Boolean values
...

Predicates are typically used to specify a searching or sorting criterion
...

The functions that modify their internal states cannot be considered
predicates
...

The back_inserter uses the push_back operation of the container in
place of the assignment operator
...

Because the class vector does not support the push_front operation,
this iterator cannot be used for the vector container
...

The function fill is used to fill a container with elements; the function
fill_n is used to fill in the next n elements
...

The functions find, find_if, find_end, and find_first_of are used
to find the elements in a given range
...

The function remove_if is used to remove elements from a sequence
using some criterion
...

The function remove_copy_if copies the elements in a sequence into
another sequence by excluding certain elements, using some criterion, from
the first sequence
...

The functions search, search_n, sort, and binary_search are used to
search elements
...


801

1
3

802 |

52
...

54
...

56
...

58
...

60
...

62
...

64
...

66
...

68
...

70
...


72
...

The algorithm inplace_merge is used to combine two sorted, consecutive
sequences
...

The algorithm reverse_copy reverses the elements in a given range while
copying into a destination range
...

The algorithm rotate rotates the elements in a given range
...

The algorithm count counts the occurrences of a given value in a given range
...

The algorithm max is used to determine the maximum of two values
...

The algorithm min is used to determine the minimum of two values
...

The algorithm random_shuffle is used to randomly order the elements in
a given range
...

The function transform creates a sequence of elements by applying
certain operations to each element in a given range
...

The algorithm set_intersection is used to find the elements that are
common to two ranges of elements
...

The algorithm set_difference is used to find the elements in one range
of elements that do not appear in another range of elements
...

The algorithms accumulate, adjacent_difference, inner_product,
and partial_sum are numerical functions and manipulate numeric
data
...

2
...

b
...

Suppose that you have the following statement:
a
...


pair name;

What is the output, if any, of the following statements?
name = make_pair("Duckey", "Donald");
cout << name
...
second << endl;
4
...


Explain how a set container differs from a map container
...
Declare the map container stateDataMap to store pairs of the form
(stateName, capitalName), where stateName and capitalName are
variables of type string
...
Write C++ statements that add the following pairs to stateDataMap:
(Nebraska, Lincoln), (New York, Albany), (Ohio, Columbus),
(California, Sacramento), (Massachusetts, Boston), and
(Texas, Austin)
...

Write a C++ statement that changes the capital of California to Los
Angeles
...

d
...

7
...


charList = {a, A, B, b, c, d, A, e, f, K}

Further suppose that:
lastElem = remove_if(charList
...
end(), islower);
ostream_iterator screen(cout, " ");

where lastElem is a vector iterator into a vector container of type char
...
begin(), lastElem, screen);
9
...
begin(), intList
...
begin(), 24);

What is the output of the following statement?
copy(otherList
...


Suppose that intList is a vector container and:
intList = {2, 4, 6, 8, 10, 12, 14, 16}

What is the value of result after the following statement executes?
result = accumulate(intList
...
end(), 0);
11
...
begin(), intList
...


Suppose that setA, setB, setC, and setD are defined as follows:
int
int
int
int

setA[]
setB[]
setC[]
setD[]

=
=
=
=

{3,
{2,
{2,
{4,

4,
3,
5,
4,

5,
4,
5,
4,

8, 9, 12, 14};
5, 6, 7, 8};
9};
6, 7, 12};

Further suppose that you have the following declarations:
int
int
int
int
int

AunionB[10];
AunionC[9];
BunionD[10];
AintersectB[4];
AintersectC[2];

What is stored in AunionB, AunionC, BunionD, AintersectB, and
AintersectC after the following statements execute?
set_union(setA, setA + 7, setB, setB + 7, AunionB);
set_union(setA, setA + 7, setC, setC + 4, AunionC);

PROGRAMMING EXERCISES
1
...


3
...

Write a program that illustrates how to use the functions find_end and
find_first_of
...
Your program
must use the function lessThanEqualTo50, as shown in Example 13-13
...


5
...


7
...


9
...


11
...


|

805

Write a program that illustrates how to use the functions adjacent_find,
merge, and inplace_merge
...

Write a program that illustrates how to use the functions set_difference
and set_symmetric_difference
...

(Stock Market Revisited) In Programming Exercise 8 of Chapter 4, you
are asked to design a program that analyzes the performance of the stocks
managed by a local stock trading company and at the end of each day
produce a listing of those stocks ordered by the stock symbol
...

Because the company also requires you to produce the list ordered by the
percent gain/loss, you need to sort the stock list by this component
...

To do so, add a data member, a vector, to hold the indices of the stock list
ordered by the component percent gain/loss
...

When printing the list ordered by the component percent gain/loss, use the
array indexByGain to print the list
...

Redo the Programming Example Video Store of Chapter 5 so that it uses
the STL class set to process a list of videos
...

Redo Programming Exercise 15 of Chapter 5 so that it uses the STL class
set to process the list of videos owned by the store, the list of videos rented
by the customer, and the list of store members
...
You program must give the
user the following choices:
Guess only the face value of the card
...
Guess only the suit of the card
...
Guess both the face value and suit of the card
...
Before each guess, use
the function random_shuffle to randomly shuffle the deck
...


1
3

This page intentionally left blank

APPENDIX A

R ESERVED W ORDS

and
bitand
case
compl
default
dynamic_cast
export
for
include
mutable
not_eq
private
reinterpret_cast
sizeof
switch
true
typename
virtual
while

and_eq
bitor
catch
const
delete
else
extern
friend
inline
namespace
operator
protected
return
static
template
try
union
void
xor

asm
bool
char
const_cast
do
enum
false
goto
int
new
or
public
short
static_cast
this
typedef
unsigned
volatile
xor_eq

807

auto
break
class
continue
double
explicit
float
if
long
not
or_eq
register
signed
struct
throw
typeid
using
wchar_t

This page intentionally left blank

APPENDIX B

O PERATOR P RECEDENCE

The following table shows the precedence (highest to lowest) and associativity of the
operators in C++
...


Left to right

++

ÀÀ

(as postfix operators)

Right to left

typeid

Right to left

dynamic_cast

static_cast

Right to left

const_cast

Right to left

reinterpret_cast
++
~

ÀÀ (as prefix operators)

& (address of)

new

delete

->*

ÀÀ

*
+

/

!

+ (unary)

* (dereference)

Left to right
Left to right

>>
<=

==

Right to left

Left to right


...

ASCII
0

1

2

3

4

5

6

7

8

9

0

nul

soh

stx

etx

eot

enq

ack

bel

bs

ht

1

lf

vt

ff

cr

so

si

dle

dc1

dc2

dc3

2

dc4

nak

syn

etb

can

em

sub

esc

fs

gs

3

rs

us

b

!

"

#

$

%

&

'

4

(

)

*

+

,

-


...
For example,
811

812 |

Appendix C: Character Sets

the character in the row marked 6 (the number in the first column) and the column
marked 5 (the number in the second row) is A
...
Moreover, the character b at position 32 represents the
space character
...
The following table shows the abbreviations and meanings of
these characters
...

EBCDIC
0

1

2

3

5

6

7

8

9


...
For
example, the character in the row marked 19 (the number in the first column) and the
column marked 3 (the number in the second row) is A
...
Moreover, the character b at position 64
represents the space character
...
In fact, the characters at positions 00-63 and 250-255 are
nonprintable control characters
...


Operators that can be overloaded
+

-

*

/

%

^

&

|

!

&&

||

=

==

<

<=

>

>=

!=

+=

-=

*=

/=

%=

^=

|=

&=

<<

>>

>>=

<<=

++



->*

,

->

[]

()

~

new

delete

The following table lists the operators that cannot be overloaded
...



...
This appendix discusses some of the most widely used library
routines (and several named constants)
...


Header File cassert
The following table describes the function assert
...

expression is any
int expression;
expression is usually

• If the value of expression

a logical expression

assert(expression)



is nonzero (true), the program
continues to execute
...
The expression,
the name of the file containing
the source code, and the line
number in the source code are
displayed
...


817

818 |

Appendix E: Header Files

Header File cctype
The following table shows various functions from the header file cctype
...
The following table lists some
of these constants
...
The following table lists some
of these constants
...

Function Name
and Parameters

Parameter(s) Type

Function Return Value

acos(x)

x is a floating-point expression,
–1
...
0

Arc cosine of x, a value between 0
...
0
x
1
...
718
...
0

Natural logarithm (base e) of x

log10(x)

x is a floating-point expression,
where x > 0
...
If x = 0
...
0, y must
if x
be a whole number
...
0

Square root of x

tan(x)

x is a floating-point expression;
x is measured in radians

Trigonometric tangent of the angle

tanh(x)

x is a floating-point expression

Hyperbolic tangent of x

x; (‘‘floor’’ of x)

822 |

Appendix E: Header Files

Header File cstddef
Among others, this header file contains the definition of the following symbolic constant:
NULL: The system-dependent null pointer (usually 0)

Header File cstring
The following table shows various string functions
...
Associated with the string type are a data type
string::size_type and a named constant string::npos
...
The following table shows some
of these functions
...
The position of the first character in a string variable (such as str) is
0, the second character is 1, and so on
...
c_str()

None

The base address of a
null-terminated C-string
corresponding to the
characters in str
...
(The newline
character is read but not
stored into str
...


stream variable (of type
istream or ifstream)
...


str
...
length()

None

Returns true if str is
empty, that is, the number
of characters in str is zero,
false otherwise
...


str
...


str
...
The string expression, the character specified by
strExp, can also be a
strExp
...

successful, the function
find returns the position
in str where the match
begins
...


824 |

Appendix E: Header Files

Function Name and Parameters

Parameter(s) Type

Function Return Value

str
...
pos, represent
the starting position (of the
substring in str), and len
represents the length (of the
substring)
...
length()
...
The
length of the substring is, at
most, len characters
...


str1
...
str1 and str2
are string variables
...


str
...


str
...


str
...


string::size_type
...
erase(m, n);

Two parameters of type
int
...
If n > length of str,
removes all the characters
starting at the mth
...
insert(m, n, c);

Parameters m and n are of
type

Inserts n occurrences of the
character c at index m into
str
...

str1
...


str1
...


Inserts all the characters of
str2 at index m into
str1
...
If n >
length of str1, then all the
characters until the end of
str1 are replaced
...
Consider the kth entry in the list
...
And, if the kth entry is not moved,
it stays at its current position
...
Assume all possibilities are
equally likely
...

If the kth entry is not moved, the number of key comparisons is one and the number of
item assignments is zero
...
Then, the average number of key comparisons
(executed by the loop) to move the kth entry is
1 þ 2 þ 3 þ
...
It now follows that, if the kth
entry is moved, on average it requires (k / 2) + 1 key comparisons and (k / 2) + 2 item
assignments
...

To find the average number of key comparisons made by insertion sort, we add the
average number of key comparisons made by list entries 2 through n
...
) Thus, the average number of key comparisons is
!
n
n
n
X 1
X1
1
1X


¼
2
2
2 k¼2
2
k¼2
k¼2
¼

n
1X
nÀ1 1

À
2 k¼1
2
2

¼

n
1X
nÀ1

2 k¼2
2

¼

1 nðn þ 1Þ n À 1 1
þ
À
2
2
2
2

nðn þ 1Þ þ 2ðn À 1Þ À 2
n2 þ n þ 2n À 4
¼
4
4
Á
1À 2
¼ Oðn2 Þ:
¼ n þ 3n À 4
4
¼

In a similar manner, we can show that the average number of item assignments made by
insertion sort is O(n2)
...
Let C(n) denote the number of key comparisons and S(n)
denote the number of swaps of entries in L, n ¼ 1, 2, 3,
...
Thus, the
pivot is compared n – 1 times for a list of length n
...
Then

Analysis: Quicksort

CðnÞ ¼ ðnÀ1Þ þ CðrÞ þ CðnÀrÀ1Þ

|

827

ðEquation 1Þ

for all n ¼ 1, 2, 3,
...

Equation 1 is also called a recurrence relation
...
Thus,
CðnÞ ¼ ðnÀ1Þ þ Cð0Þ þ Cðn À 0 À 1Þ ¼ ðn À 1Þ þ Cðn À 1Þ:

ðEquation 2Þ

Substitute n ¼ 2, 3, and 4, respectively, in Equation 2 to get
Cð2Þ ¼ 1þCð1Þ ¼ 1 þ 0 ¼ 1;
Cð3Þ ¼ 2þCð2Þ ¼ 2 þ 1 ¼ 3;
Cð4Þ ¼ 3þCð3Þ ¼ 3 þ 3 ¼ 6:
We now solve Equation 1
...


...

¼ ðn À 1Þ þ ðn À 2Þ þ ðn À 3Þ þ ::: þ 2 þ Cð2Þ
¼ ðn À 1Þ þ ðn À 2Þ þ ðn À 3Þ þ ::: þ 2 þ 1
¼ nðn À 1Þ=2
¼ ð1=2Þn2 À ð1=2Þn
¼ Oðn2 Þ:

We now look at the number of swaps in the worst case
...

Hence,
SðnÞ ¼ ðn þ 1Þ þ Sðn À 1Þ

ðEquation 3Þ

for all n ¼ 1, 2, 3,
...
Now
SðnÞ ¼ ðn þ 1Þ þ Sðn À 1Þ
¼ ðn þ 1Þ þ n þ Sðn À 2Þ because Sðn À 1Þ ¼ n þ Sðn À 2Þ
¼ ðn þ 1Þ þ n þ ðn À 1Þ þ Sðn À 3Þ because Sðn À 2Þ ¼ ðn À 1Þ þ Sðn À 3Þ

...


...

Let S(n, p) denote the number of swaps for a list of length n such that the pivot is the
pth key, p ¼ 1, 2,
...
Now, if the pivot is the pth key, then
Sðn; pÞ ¼ ðp þ 1Þ þ Sðp À 1Þ þ Sðn À pÞ:
From this, it follows that if the pivot is the first key,
Sðn; 1Þ ¼ ð1 þ 1Þ þ Sð1 À 1Þ þ Sðn À 1Þ ¼ 2 þ Sð0Þ þ Sðn À 1Þ:
Similarly,
Sðn; 2Þ ¼ 3 þ Sð1Þ þ Sðn À 2Þ;

...


...


...

Sðn; nÞ ¼ ðn þ 1Þ þ Sðn À 1Þ þ Sð0Þ:

Analysis: Quicksort

|

829

Assume that the pivot can occur at any position, that is, all positions are equally likely
...


...

Sð3Þ
4
Sð2Þ
3

1 Sð2Þ
¼ þ
;
3
3
1 Sð1Þ
¼ þ
:
2
2

Hence,
SðnÞ
1
1
1 1 Sð1Þ
¼ þ
þ
...
þ þ þ 1 ¼ lnðnÞ þ Oð1Þ:
n nÀ1
3 2
It follows that
SðnÞ
¼ lnðnÞ þ Oð1Þ:
nþ1
Hence,
SðnÞ ¼ nlnðnÞ þ OðnÞ:
Also, because
lnðnÞ ¼ lnð2Þlog2 ðnÞ ¼ 0:69 log2 ðnÞ
it follows that
SðnÞ ¼ 0:69 n log2 ðnÞ þ OðnÞ ¼ Oðn log2 ðnÞÞ:
Next, we derive a formula for C(n) for the average case of quicksort
...
Let C(n, p) denote the number of
comparisons made by the partition function when the pivot is the pth key
...
þ Cðn; nÞ
:
n

Now,
Cðn; 1Þ ¼ ðn À 1Þ þ Cð0Þ þ Cðn À 1Þ;
Cðn; 2Þ ¼ ðn À 1Þ þ Cð1Þ þ Cðn À 2Þ;
Cðn; 3Þ ¼ ðn À 1Þ þ Cð2Þ þ Cðn À 3Þ;

...


...


...

Cðn; nÞ ¼ ðn À 1Þ þ Cðn À 1Þ þ Cð0Þ:
This implies that
nðn À 1Þ þ 2ðCð0Þ þ Cð1Þ þ Á Á Á þ Cðn À 1ÞÞ
n
2ðCð0Þ þ Cð1Þ þ Á Á Á þ Cðn À 1ÞÞ
:
¼ ðn À 1Þ þ
n

CðnÞ ¼

Change n to n-1 to get
Cðn À 1Þ ¼ ðn À 2Þ þ

2ðCð0Þ þ Cð1Þ þ Á Á Á þ Cðn À 2ÞÞ
:
nÀ1

Thus,
nCðnÞ À ðn À 1ÞCðn À 1Þ ¼ nðn À 1Þ À ðn À 1Þðn À 2Þ þ 2Cðn À 1Þ
¼ 2ðn þ 1Þ þ 2Cðn À 1Þ:
This implies that
nCðnÞ ¼ 2ðn þ 1Þ þ ðn þ 1ÞCðn À 1Þ:
Divide both sides by n(n + 1), to get
CðnÞ 2 Cðn À 1Þ
¼ þ
:
nþ1 n
n

|

831

832 |

Additional C++ Topics

We now solve this equation
...
þ þ þ
nþ1 n nÀ1 nÀ2
3 2
2
2
2
2
2 2 1
þ
þ
...
þ þ
þ
¼2 þ
n nÀ1 nÀ2
3 2
2


1
1
1
1 1
1
¼2 þ
þ
þ
...
However, to help you, this appendix quickly reviews
these basic elements of C++
...
In addition to
describing the basic elements of C++, we also compare various features of C++ with Java
...


Data Types
C++ data types fall into three categories—simple data types, structured data types, and pointers
...
Chapter 3 describes pointers
...
Moreover,
later in this appendix, we briefly discuss arrays, a structured data type, in C++
...
There are three categories
of simple data—integral, floating-point, and enumeration type
...
Some of the integral data
types are char, bool, short, int, long, and unsigned int
...

TABLE G-1

Values and memory allocation for three simple data types

Data type

Values

Storage (in bytes)

int

À2147483648 to 2147483647

4

bool

true and false

1

char

À128 to 127

1
833

834 |

Appendix G: C++ for Java Programmers

Use this table only as a guide
...

Check your compiler’s documentation
...

Notice that the data type char in C++ is a set of 256 values, whereas the data type char
in Java is a set of 65,536 values
...
Typically,
C++ uses the ASCII characters, a set of 128 characters and described in Appendix C, to
deal with characters
...
Also, true and false are
called the logical (Boolean) values
...

To deal with decimal numbers, C++ provides the floating-point data type
...
As in the case of integral data types, the data types
float and double differ in their sets of values
...


Arithmetic Operators and Expressions
The arithmetic operators— +, -, *, and /—in C++ work the same way as in Java
...

Furthermore, arithmetic expressions in C++ are formed and evaluated the same as they are in
Java
...

The cast operator in C++ takes the following form:
static_cast expression

You can also use the following C-like cast operator:
dataType expression

Named Constants, Variables, and Assignment
Statements
Named constants in C++ are declared using the reserved word const
...
54 to it:
const double CONVERSION = 2
...

The general syntax for declaring one variable or multiple variables is as follows:
dataType identifier, identifier,
...
The expression on the right side is evaluated, and its value is assigned to
the variable on the left side
...
If the value of quantity is 20, the
following statement assigns 150
...
50;

C++ Library: Preprocessor Directives
Only a small number of operations, such as arithmetic and assignment operations, are
explicitly defined in C++
...
Every library has a name and is referred
to as a header file
...
Similarly, the descriptions
of some very useful mathematical functions, such as power, absolute, and sine, are
contained in the header file cmath
...
You use preprocessor directives
and the names of the header files to tell the computer the locations of the code provided
in the libraries
...

Preprocessor directives are commands supplied to the preprocessor that cause the preprocessor to modify the text of a C++ program before it is complied
...
There are no semicolons at the end of preprocessor commands
because they are not C++ commands
...


836 |

Appendix G: C++ for Java Programmers

The general syntax to include a system-provided header file in a C++ program is as
follows:
#include

For example, the following statement includes the header file iostream in a C++ program:
#include

Preprocessor directives that include the header files are placed as the first lines of a
program so that the identifiers declared in those header files can be used throughout
the program
...
)
Certain header files are provided as part of C++
...


C++ Program
Every C++ program has two parts: preprocessor directives and the program
...
The program contains statements that accomplish some
meaningful results
...
To be useful, this source code must be saved in a file
that has the file extension
...

When the program is compiled, the compiler generates the object code, which is saved in
a file with the file extension
...
When the object code is linked with system resources,
the executable code is produced and saved in a file with the file extension
...
The
name of the file containing the object code, and the name of the file containing the
executable code, are the same as the name of the file containing the source code
...
cpp, the name of the
file containing the object code is firstProg
...
exe
...
cpp,
...
exe—are
system dependent
...

A C++ program is a collection of functions and one of the functions is the function
main
...
The basic parts of
the function main are the heading and the body of the function
...

The following is an example of a C++ program:
#include
using namespace std;
int main()
{
int num1, num2;
num1 = 10;
num2 = 2 * num1;
cout << "num1 = " << num1 << ", and num2 = " << num2 << endl;
}

return 0;

The next section discusses input and output (I/O) in detail
...
Because I/O differs quite significantly in C++ and Java, this section
describes I/O in C++ in detail
...
The syntax of cin together with >> is as follows:
cin >> variable >> variable
...
Sometimes this is also called a cin statement
...

The input (or cin) statement works as follows
...
The statement
cin >> miles;

causes the computer to get a value from the standard input device of the data type
double and place it in the memory cell named miles
...

Suppose feet and inch are variables of type int
...

The extraction operator >> is defined only for putting data into variables of simple data
types
...


How does the extraction operator >> work? When scanning for the next input, >> skips
all the white space characters
...
Thus, whether you
separate the input data by lines or blanks, the extraction operator >> simply finds the
next input data in the input stream
...
Consider the following input statement:
cin >> payRate >> hoursWorked;

Whether the input is
15
...
30

or
15
...
30

or
15
...
30

the preceding input statement would store 15
...
30 in
hoursWorked
...

Now suppose that the input is 2
...
If the right-side operand is a variable of type char, the
input 2 is treated as the character 2 and, in this case, the ASCII value of 2 is stored
...

Next, consider the input 25 and the statement
cin >> a;

where a is a variable of some simple data type
...
If a is of type int, 25 is stored in a
...
0
...


Input and Output

TABLE G-2

|

839

Valid input for a variable of the simple data type

Data type of a

Valid input for a

char

One printable character except the blank
...


double

A decimal number, possibly preceded by a ( + or -) sign
...


When reading data into a char variable, after skipping any leading white space
characters, the extraction operator >> finds and stores only the next character; reading
stops after a single character
...


Input Failure
Many things can go wrong during program execution
...
For example, suppose that a part-time employee’s paycheck is calculated by using the following formula:
wages = payRate * hoursWorked;

If you accidentally type a + in place of *, the calculated wages would be incorrect, even
though the statement containing the + is syntactically correct
...
For example, trying to read a letter into
an int or double variable would result in an input failure
...
If the input were:
35 67
...
93 in x, and 48 in b
...
The reading stops at
...

Because the next variable c is of the data type int, the computer tries to read
...
The input stream then enters a state called the fail state
...
Unfortunately,
the program quietly continues to execute with whatever values are stored in the variables
and produce incorrect results
...
The syntax of cout together with << is as follows:
cout << expression or manipulator << expression or manipulator
...
Sometimes this is also called a cout statement
...

Generating output with the cout statements follows two rules:
1
...
(On the screen, the insertion point
is where the cursor is
...
A manipulator is used to format the output
...

Example G-1 illustrates how cout statements work
...


Input and Output

|

841

EXAMPLE G-1
Consider the following statements
...

Statement
cout <<
cout <<
cout <<
cout <<
cout <<
cout <<
cout <<
cout <<
cout <<

29 / 4 << endl;
"Hello there
...
" << endl;

Output
7
Hello there
...


setprecision
You use the manipulator setprecision to control the output of floating-point numbers
...
Some IDEs
might use a maximum of six decimal places for the default output of floating-point
numbers
...
To print floating-point output to two decimal places,
you use the setprecision manipulator to set the precision to 2
...

You use the setprecision manipulator with cout and the extraction operator
...
Notice that the number of decimal places,
or the precision value, is passed as an argument to setprecision
...
Thus, the following include statement is required:
#include

fixed
To further control the output of floating-point numbers, you can use other manipulators
...
The following statement sets the output of floating-point numbers in a fixed
decimal format on the standard output device:
cout << fixed;

After the preceding statement executes, all floating-point numbers are displayed in the
fixed-decimal format
...


showpoint
Suppose that the decimal part of a decimal number is 0
...
To force the output to show the
decimal point and trailing zeros, you use the manipulator showpoint
...

The value of the expression can be either a string or a number
...
The output is right-justified
...
Furthermore, if the number of columns
specified is less than the number of columns required by the output, the output automatically expands to the required number of columns; the output is not truncated
...
Thus,
the following include statement is required:
#include

Unlike setprecision, which controls the output of all floating-point numbers until it is
reset, setw controls the output of only the next expression
...
Sometimes you might want the output to be left-justified
...

The syntax to set the manipulator left is as follows:
ostreamVar << left;

where ostreamVar is an output stream variable
...
For example, the following statement
sets the output to be right-justified on the standard output device:
cout << right;

File Input/Output
The previous sections discussed how to get input from the keyboard (standard input
device) and send output to the screen (standard output device)
...
C++ allows a program to get
data directly from, and save output directly to, secondary storage
...
Formally, a file is defined as follows:
File: An area in secondary storage used to hold information
...

In addition, C++ provides a header file called fstream, which is used for file I/O
...

The variables cin and cout are already defined and associated with the standard input/
output devices
...
These same operators are also available
for file I/O, but the header file fstream does not declare variables to use them
...
You then use these variables together with >>

844 |

Appendix G: C++ for Java Programmers

and << for I/O
...
Once you declare the fstream objects, you must associate these objects with
the input/output sources
...

2
...

4
...


Include the header file fstream in the program
...

Associate the file stream objects with the input/output sources
...

Close the files
...
A skeleton program then shows how the steps
might appear in a program
...
The following
statement accomplishes this task:
#include

Step 2 requires you to declare file stream objects
...
The second statement
declares outData to be an ofstream object
...
This
step is called opening the files
...
The general syntax for opening a file is as follows:
fileStreamVariable
...

Suppose you include the declaration from Step 2 in a program
...
dat
...
dat and outData with prog
...
That is, the file prog
...
out is opened for outputting data
...
open("prog
...
open("prog
...
NET manage programs in the form of projects
...
The statement in Line 1
assumes that the file prog
...

However, if this is in a different directory (subdirectory), you must specify the path where
the file is located, along with the name of the file
...
dat is on a flash memory in drive H
...
open("h:\\prog
...
In C++, \ is the escape character
...
(To be absolutely sure about specifying the
source where the input file is stored, such as the drive h:\\, check your system’s
documentation
...


Step 4 typically works as follows
...
The syntax for using >> or << with file stream objects is exactly
the same as the syntax for using cin and cout
...
For example, the statement
inData >> payRate;

reads the data from the file prog
...
The
statement
outData << "The paycheck is: $" << pay << endl;

stores the output— The paycheck is: $565
...
out
...
78
...
Closing a file means that the
file stream variables are disassociated from the storage area, and the file stream objects are
freed
...
Moreover,
closing an output file ensures that the entire output is sent to the file, that is, the buffer is
emptied
...
For example, assuming
the program includes the declarations listed in Steps 2 and 3, the statements for closing the
files are as follows:
inData
...
close();
On some systems, it is not necessary to close the files
...
Nevertheless, it is a good practice to close the files
yourself
...


846 |

Appendix G: C++ for Java Programmers

In skeleton form, a program that uses file I/O is usually of the following form:
#include
//Add any additional header files that you use
using namespace std;
int main()
{
//Declare file stream variables such as the following
ifstream inData;
ofstream outData;
//Additional variable declaration
//Open files
inData
...
dat"); //open the input file
outData
...
out"); //open the output file
//Code for data manipulation
//Close files
inData
...
close();
}

return 0;

Step 3 requires the file to be opened for file I/O
...

In the case of an input file, the file must exist before the open statement executes
...
An
output file does not have to exist before it is opened; if the output file does not exist, the
computer prepares an empty file for output
...


Control Structures
C++ and Java have the same six relational operators— ==, !=, <, <=, >, and >=; and they
work the same way in both the languages
...
For example, the selection control structures are if, if
...
while
...
However, there are some differences
...
The
reserved word true is initialized to 1 and the false is initialized to 0
...
On the other hand, logical expressions in Java evaluate to
true or false
...

In C++, the mix-up of the assignment operator and the equality operator in a logical
expression can cause serious problems
...


In C++, the expression drivingCode = 5 returns the value 5
...
So in C++, the expression evaluates to true and the value of
the variable drivingCode is also changed
...
So the preceding statement
in Java results in a compiler error, whereas in C++ it does not cause any syntax error
...
Therefore, if a global identifier in a
program has the same name as one of the global identifiers in the header file, the compiler
generates a syntax error (such as ‘‘identifier redefined’’)
...
To overcome this problem, third-party vendors begin
their global identifier names with a special symbol
...
Therefore, to avoid linking errors, you
should not begin identifier names in your program with an underscore (_)
...


The general syntax of the statement namespace is as follows:
namespace namespaceName
{
members
}

where a member is usually a named constant, variable declaration, function, or another
namespace
...

In C++, namespace is a reserved word
...
50;
int count = 0;
void printResult();
}

defines globalType to be a namespace with four members: named constants n and
rate, the variable count, and the function printResult
...
You can usually access a
namespace member outside the namespace in one of two ways, as described next
...
Thus, to access a member of a
namespace, you use namespaceName, followed by the scope resolution operator, followed by the member name
...

To simplify the accessing of a namespace member, C++ provides the use of the
statement using
...

a
...
To simplify the accessing of a specific namespace member:
using namespaceName::identifier;

For example, the using statement
using namespace globalType;

simplifies the accessing of all the members of the namespace globalType
...

In C++, using is a reserved word
...
For the
namespace globalType, for example, you usually write the code as follows:
namespace globalType
{
const int n = 10;
const double rate = 7
...

However, if a namespace member and a global identifier in a program have the same
name, to access this namespace member in the program, the namespaceName and the
scope resolution operator must precede the namespace member
...

The identifiers in the system that provide the header files such as iostream, cmath,
and iomanip are defined in the namespace std
...
In C++, there are two types of functions—valuereturning and void
...
This
type is also called the data type of the value-returning function
...

SYNTAX: FORMAL PARAMETER LIST
The general syntax of the formal parameter list is as follows:
dataType identifier, dataType identifier,
...


Thus, to call a value-returning function, you use its name, with the actual parameters
(if any) in parentheses
...
However, if the formal parameter list is
empty, the parentheses are still needed
...


Void Functions
The definition of a void function has the following syntax:
void functionName(formal parameter list)
{
statements
}

FORMAL PARAMETER LIST
The formal parameter list may be empty
...


You must specify both the data type and the variable name in the formal parameter list
...

FUNCTION CALL
The function call has the following syntax:
functionName(actual parameter list);

ACTUAL PARAMETER LIST
The actual parameter list has the following syntax:
expression or variable, expression or variable,
...
Actual
and formal parameters have a one-to-one correspondence
...
(Functions with default parameters are discussed at
the end of this appendix
...

}

The function funexp has four parameters
...

Value parameter: A formal parameter that receives a copy of the content of the
corresponding actual parameter
...

When you attach & after the dataType in the formal parameter list of a function, the
variable following that dataType becomes a reference parameter
...

From the definition of value parameters, it follows that if a formal parameter is a value
parameter, the value of the corresponding actual parameter is copied into the formal
parameter
...
Therefore, during
program execution, the value parameter manipulates the data stored in its own memory
space
...

On the other hand, if a formal parameter is a reference parameter, it receives the address
of the corresponding actual parameter
...
During program execution to manipulate the data,
the address stored in the reference parameter directs it to the memory space of the

852 |

Appendix G: C++ for Java Programmers

corresponding actual parameter
...
Any changes that a reference parameter makes to its data immediately changes
the value of the corresponding actual parameter
...

In Java, parameters are passed by value only; that is, the formal parameter receives a
copy of the actual parameter’s data
...
On the other hand,
suppose that a formal parameter is a reference variable
...
Because the formal parameter contains
the address of the object storing the data, the formal parameter can change the value
of the actual object
...


Reference Parameters and Value-Returning Functions
While describing the syntax of the formal parameter list of a value-returning function, we
used only value parameters
...
By definition, a value-returning
function returns a single value; this value is returned via the return statement
...


Functions with Default Parameters
When a function is called, the number of actual and formal parameters must be the
same
...
You specify
the value of a default parameter when the function name appears for the first time,
such as in the prototype
...

All of the default parameters must be the rightmost parameters of the
function
...
In a function
call, if a value to a default parameter is not specified, you must omit all of
the arguments to its right
...

The caller has the option of specifying a value other than the default for
any default parameter
...


Functions and Parameters

|

853

Consider the following function prototype:
void funcExp(int x, int y, double t, char z = 'A', int u = 67,
char v = 'G', double w = 78
...
The parameters z, u, v, and w are default
parameters
...

Suppose you have the following statements:
int a, b;
char ch;
double d;

The following function calls are legal:
1
...
funcExp(a, 15, 34
...
funcExp(b, a, 14
...
In statement 2, the default
value of z is replaced by 'B', the default value of u is replaced by 87, the default value of
v is replaced by the value of ch, and the default value of w is used
...

The following function calls are illegal:
1
...
6, 46
...
funcExp(b, 25, 48
...
34);
In statement 1, because the value of z is omitted, all the other default values must be
omitted
...

The following are illegal function prototypes with default parameters:
1
...
45, char ch, int u = 45);
2
...
void funcThree(int x, int& y = 16, double z = 34);
In statement 1, because the second parameter z is a default parameter, all the other
parameters after z must be default parameters
...
In statement 3, a
constant value cannot be assigned to y because y is a reference parameter
...
However, in C++ arrays are not objects and
so need not be instantiated
...

A one-dimensional array is an array in which the components are arranged in a list
form
...
Also, intExp
specifies the number of components in the array
...
Each component is of type int
...


Accessing Array Components
In C++, array components are accessed just like in Java
...
The index value specifies the position of the component in the array
...
Consider the following statement:
int list[10];

This statement declares an array list of 10 components
...
, list[9]
...


Array Index Out of Bounds
Unfortunately, in C++, there is no guard against out-of-bounds indices
...

If the index goes out of bounds and the program tries to access the component specified by

Arrays

|

855

the index, then whatever memory location is indicated by the index is accessed
...
Consequently, if during execution the index goes out of bounds, several strange
things can happen
...
On some new compilers, if an array index goes out of bounds in a program,
it is possible that the program terminates with an error message
...
Because arrays are passed by reference only,
you do not use the symbol & when declaring an array as a formal parameter
...
If you specify the size of the one-dimensional array when it is declared as a
formal parameter, it is ignored by the compiler
...
However, no such variable is
associated with C++ arrays
...
When the
function initialize is called, the size of the actual array is passed as the second
parameter of the function initialize
...
However, even though an array is always
passed by reference, you can still prevent the function from changing the actual parameter
...
Consider the following function:
void example(int x[], const int y[], int sizeX, int sizeY)
{

...
Any attempt to
change y results in a compile-time error
...


This page intentionally left blank

APPENDIX H

R EFERENCES

1
...
Booch, R
...
Maksimchuk, M
...
Engel, B
...
Young, J
...
A
...
, AddisonWesley, Reading, MA, 2007
...
E
...
Sahni, and S
...

3
...
M
...

4
...
E
...
, Addison-Wesley, Reading, MA, 1997
...
D
...
Knuth, The Art of Computer Programming, Volume 2: Seminumerical Algorithms,
3rd ed
...

6
...
E
...
, Addison-Wesley, Reading, MA, 1998
...
S
...
Lippman and J
...
, Addison-Wesley, Reading, MA,
1998
...
D
...
Malik, C++ Programming: From Problem Analysis to Program Design, 4th ed
...

9
...
S
...
K
...

10
...
M
...
J
...

11
...
Sedgewick, Algorithms in C, 3rd ed
...


857

This page intentionally left blank

APPENDIX I

A NSWERS TO O DD N UMBERED E XERCISES

Chapter 1
1
...
true; b
...
false; d
...
false; f
...
false; h
...
The white box refers to testing the correctness of the program; that is, making sure
that the program does what it is supposed to do
...
The objective is
to ensure that every part of the function or algorithm is executed at least once
...
a
...
O(n3)
c
...
O(n)
e
...


O(nlog2n)

7
...


43

b
...
O(n)
9
...

859

860 |

Answers to Odd-Numbered Exercises

11
...
Each time through the loop a fixed number of
statements execute
...
Now each time through the loop
there is one addition, one subtraction, and one multiplication
...

13
...
For each
iteration of the outer loop, the middle loop has n iterations
...
For each iteration of the middle loop, the
innermost loop has n iterations
...

Hence, this algorithm is O(n3)
...
a
...
2
c
...
void xClass::func()
{
}

u = 10;

v = 15
...
void xClass::print()
{
}

f
...
x
...
xClass t(20, 35
...
00:00:00
23:13:00
06:59:39
07:00:39
The two times are different
...
a
...
student
...
student
...
a
...
true; c
...
false; e
...
true; g
...
false; i
...
true;
k
...
true; m
...
false
3
...
Some of the member functions are setInfo, getSalary,
getEmployeeCategory, and setSalary
...
a
...
Missing semicolon after }
...
a
...
xClass::xClass()
{
}

z = 0;

c
...
a
...
void two::print() const
{
}

one::print();
cout <
11
...
Because the left operand of << is a stream object, which is not of the type mystery
...
a
...
strange operator+(const strange&) const;
c
...
strange operator++(int);
17
...
The correct statement is as
follows:
bool mystery::operator<=(mystery rightObj)
{

//Line 3

}

19
...
The correct statement is as follows:
friend operator+ (mystery, mystery);

//Line 2

21
...
Two
25
...

b
...
bool strange::operator==(strange right)
{
}

return(a == right
...
b);

27
...
21; b
...
a
...
false; c
...
true; e
...
true; g
...
false
3
...
b and c
7
...
4 4 5 7 10 14 19 25 32 40
11
...

In a deep copy of data, each pointer has its own copy of the data
...
Array p: 5 7 11 17 25
Array q: 25 17 11 7 5

15
...

17
...

19
...
In compile-time binding, the compiler generates the necessary code to call a
function
...

23
...


The statement creates the arrayListType object intList of size 100
...


b
...

The elements of stringList are of the type string
...
The statement creates the arrayListType object salesList of size 100
...


Chapter 4
1
...

3
...
ostream_iterator screen(cout, " ");
7
...
3 7 9
11
...
vecList = {8, 23, 40, 6, 18, 9, 75, 9, 75}
15
...
a
...
false; c
...
false; e
...
a
...
true; c
...
false; e
...
a
...


A = A->link;
list = A->link->link;

864 |

Answers to Odd-Numbered Exercises

c
...
list = NULL;
e
...


newNode = new nodeType;
newNode->info = 10;
newNode->link = A->link;
A->link = newNode;

g
...
a
...
The statement s->info = B; is invalid because B is a
pointer and s->info is an int
...
This is an invalid code
...

9
...

18 38 2 15 45 25

11
...
intList = {5, 24, 16, 11, 60, 9, 3, 58, 78, 85, 6, 15, 93, 98, 25}
15
...
a
...
true; c
...
false; e
...
The case in which the solution is defined in terms of smaller versions of itself
...
A function that calls another function and eventually results in the original function
call is said to be indirectly recursive
...
a
...


b
...

c
...

d
...
The value of mystery(0) is 0
...


It is a valid call
...


f
...
It will result in an infinite recursion
...
a
...


b
...
It does not produce any output
...
It does not produce any output
...
a
...
3
c
...
21
13
...
x = 3
y = 9
7
13
4
7

3
...


26

b
...
8
d
...
a
...
(A + B) * (C – D)
c
...
a
...
Because stackADT is an abstract class, we cannot
instantiate an object of this class
...
Creates sales to be an object of the class stackType
...
(Note that because the value -10 is
passed to the constructor with parameters, the definition of the constructor
with parameters creates the stack of size 100
...
Creates names to be object of the class stackType
...

d
...
Because the class linkedStackType does not
have a constructor with parameters, you cannot pass the value 50 to the default
constructor
...
10
50 25 10
50

11
...
isEmptyStack());
temp1 = stack
...
pop();
assert(!stack
...
top();
stack
...
template
void clear(stack& st)
{
while (!st
...
pop();
}

Chapter 8
1
...
The function mystery reverses the elements of a queue and also doubles the values
of the queue elements
...
10
20 40 20 5 3
20 3

7
...


queueFront = 99; queueRear = 26

b
...
a
...
queueFront = 0; queueRear = 99

868 |

Answers to Odd-Numbered Exercises

11
...


queueFront = 74; queueRear = 0

b
...
The position of the removed element was
75
...
template
int queueType::queueCount()
{
return count;
}

15
...
a
...
true; c
...
false
3
...
There are 30 buckets in the hash table and each bucket can hold 5 items
...
Suppose that an item with key X is hashed at t, that is, h(X) = t, and 0
t ;
HTSize - 1
...
In quadratic

Chapter 9 |

869

probing, starting at position t, we linearly search the array at locations (t + 1) %
HTSize, (t + 22) % HTSize = (t + 4) % HTSize, (t + 32) % HTSize = (t + 9) %
HTSize,
...
That is, the probe sequence is t, (t + 1) % HTSize,
(t + 22) % HTSize, (t+32) % HTSize,
...

9
...
101
13
...
Suppose HT is of size 13 indexed 0,1,2,
...
Define the function
h: {k1, k2, k3, k4, k5, k6, k7, k8}!{0,1,2,
...

Now, h(k1) = h(2733) = 2733 % 13 = 3
...

Also, 1409 % 13 = 5, 2731 % 13 = 1, 1541 % 13 = 7, 2004 % 13 = 2, 2101 % 13 = 8,
2168 % 13 = 10, and 1863 % 13 = 4
...

Suppose HT[b] a means ‘‘store the data of the student with ID a into HT[b]
...


HT[10]

2168,

15
...
Suppose HT is of
size 13 indexed 0,1,2,
...
Define the function h: {k1, k2, k3, k4, k5,
k6}!{0,1,2,
...

Now h(k1) = h(147) = 147 % 13 = 4
...
We construct the following table that shows the array position
where each student’s data is stored
...


17
...

Let k = 5701
...
Thus, h(5701) = 1
...

Next consider k = 9302
...
Thus, h(9302) = 11
...

Consider k = 4210
...
Therefore, h(4210) = 11
...
Now g(4210) = 1 + (4210 % 17) =
1 + 11 = 12
...

Because HT[4] is empty, we store the data of the student with ID 4210 in HT[4]
...
If a
collision occurs for an ID, the following table shows the probe sequence of that ID
...
g(4210) = 1 + (4210 % 17) = 1 + 11 = 12

14

14, 9, 4, 18,
...


Thus,
HT[1]
HT[14]

5701,
1553,

HT[11]
HT[3]

9320,
9902,

HT[4]
HT[18]

4210,

HT[9]

9015,

2104
...
In open hashing, the hash table, HT, is an array of pointers
...
) Therefore, items are inserted into
and deleted from a linked list, and so item insertion and deletion are simple and
straightforward
...
Thus, average linked list is short, which results in a shorter search
length
...
Suppose there are 1000 items and each item requires 10 words of storage
...
We then need
1000 words for the hash table, 10,000 words for the items, and 1000 words for
the link in each node
...
On the other hand, if we use quadratic
probing, if the hash table size is twice the number of items, we need 20,000
words of storage
...
The load factor a = 750 / 1001 %
...

a
...
49
...
(Àlog2 (1À a)) / a % 2
...

c
...
38
...
List before the first iteration: 26, 45, 17, 65, 33, 55, 12, 18
List after the first iteration: 12, 45, 17, 65, 33, 55, 26, 18
List after the second iteration: 12, 17, 45, 65, 33, 55, 26, 18
List after the third iteration: 12, 17, 18, 65, 33, 55, 26, 45
List after the fourth iteration: 12, 17, 18, 26, 33, 55, 65, 45
List after the fifth iteration: 12, 17, 18, 26, 33, 55, 65, 45
List after the sixth iteration: 12, 17, 18, 26, 33, 45, 65, 55
List after the seventh iteration: 12, 17, 18, 26, 33, 45, 55, 65
3
...
10, 12, 18, 21, 25, 28, 30, 71, 32, 58, 15
7
...

Each sublist is sorted, so that elements that are far apart move closer to their final
position
...
In the quicksort, the list is partitioned according to an element, called pivot, of
the list
...
The mergesort partitions the
list by dividing it into two sublists of nearly equal size by breaking the list in the
middle
...
a
...
18, 16, 40, 14, 17, 35, 57, 50, 37, 47, 72, 82, 64, 67

872 |

Answers to Odd-Numbered Exercises

13
...
After two passes of the heapsort
algorithm, the list is as follows:
85, 72, 82, 47, 65, 50, 76, 30, 20, 60, 28, 25, 45, 17, 35, 14, 94, 100

15
...
, n – 1
...
Because L is sorted,
for each iteration of the for loop, the expression in the if statement evaluates
to false, so the body of the if statement never executes
...
Because the for loop executes n – 1 times, it
follows that the total number of comparisons is n – 1 and the number of item
assignments is 0
...
template
class unorderedLinkedList: public linkedListType
{
public:
bool search(const Type& searchItem) const;
void insertFirst(const Type& newItem);
void insertLast(const Type& newItem);
void deleteNode(const Type& deleteItem);
void linkedInsertionSort();
void mergeSort();
private:
void divideList(nodeType* first1,
nodeType* &first2);
nodeType* mergeList(nodeType* first1,
nodeType* first2);
void recMergeSort(nodeType* &head);
};

Chapter 11
1
...
false; b
...
false; d
...
LA = {B, C, D, E}
5
...
A B C D E F G
9
...


50

30

25

80

40

55

48

98

58

45

85

70

65

FIGURE I-4

Chapter 11 Exercise 11

90

79

75

110

873

874 |

Answers to Odd-Numbered Exercises

13
...

A
G

B
C

H

L

D
E

FIGURE I-6

I

M

J

F

K

Chapter 11 Exercise 15

17
...


50

30

25

FIGURE I-7

98

40

80

100

Chapter 11 Exercise 17

19
...


40

30

25
20

FIGURE I-8

Chapter 11 Exercise 19

50

48

35
42

80

55

98

875

876 |

Answers to Odd-Numbered Exercises

21
...

42

10 35

2 8

FIGURE I-10

75 95

15 20 30

37 40

50 60

82 90

96 98 99

Chapter 11 Exercise 23

25
...

50

7

2 4

FIGURE I-12

30

16 22 24

Chapter 11 Exercise 27

65

35 40

55 60

88

70 85

90 96

Chapter 12 |

Chapter 12
1
...
0 1 4 2 3 5
2
5
...


0

1
2

3

4

5

6

7

8

9

10

FIGURE I-13

11

Chapter 12 Exercise 7

9
...
This graph has vertices of odd degree
...

Hence, this graph has no Euler circuit
...
A container is used to store data; an algorithm is used to manipulate the data stored
in a container
...
Duckey Donald
5
...


map stateDataMap;

b
...
insert(make_pair("Nebraska", "Lincoln"));
stateDataMap
...
insert(make_pair("Ohio", "Columbus"));
stateDataMap
...
insert(make_pair("Massachusetts", "Boston"));
stateDataMap
...
map::iterator mapItr;
cout << left;
cout << "The elements of stateDataMap:" << endl;
for (mapItr = stateDataMap
...
end(); mapItr++)
cout << setw(15) << mapItr->first
<< setw(15) << mapItr->second << endl;
cout << endl;

d
...
find("California");
if (mapItr != stateDataMap
...
An STL function object contains a function that can be treated as a function using
the function call operator
...
18 5 11 56 27 2
11
...

Á Pa g e n u m b e r s f o l l o w e d b y ( 2 ) i n d i c a t e t w o s e p a r a t e d i s c u s s i o n s
...

Á Pa g e n u m b e r s f o l l o w e d b y t i n d i c a t e t a b l e s
...


Symbols
& (ampersand):
address of operator
...
See dereferencing
operator; multiplication
operator
*= (asterisk–equal sign):
compound assignment
operator, 810t
\ (backslash): escape character,
845n
| (bar): separator operator, 810t
|| (bars): or operator, 810t
[] (brackets)
...
(dot)
...
See
assignment operator
postfix expression operator, 430
== (equal signs)
...
See inequality
operator
> (greater-than sign): balance
factor indicator, 649
See also greater-than operator
>= (greater-than sign–equal
sign)
...
See
extraction operator
-> (hyphen–right angle bracket)
...

See less-than-or-equal-to
operator
<< (less-than signs)
...
See decrement
operator
# (number sign):
postfix expression number
symbol, 430
preprocessor directive character,
835
UML symbol, 23
#define preprocessor directive,
77
#endif preprocessor directive,
77
#ifndef preprocessor directive,
77
#include preprocessor
directive, 75–76, 77,
835–836
() (parentheses)
...
See addition
operator
plus operator, 809t
UML symbol, 23
++ (plus signs)
...
See division operator
/= (slash–equal sign): compound
assignment operator, 810t
(space) (blank space character):
extraction operator and, 838
~ (tilde): destructor name prefix,
33
_ (underscore): identifier
character, 847

Numbers
0 (null pointer), 138, 822
4-queens puzzle, 377–378,
378–379
8-queens puzzle, 376–377,
377, 379–381

A
absolute value function, 821t
abstract classes, 170, 196–197
abstract data types (ADTs), 34
classes and, 34–35
defining, 34–38
...
See member
access specifiers
accessing:
array elements, 854
base class data members in
derived classes: private
members, 62–63, 68;
protected members, 78
class members, 24–25, 28–29,
32; via pointers, 137–138
namespace members,
848–849, 849n
private data members: in
derived classes, 62–63, 68;
with friend functions,
91–93
struct members via pointers,
137–138
vector container elements,
211, 213–214
accumulate function, 794,
794–796
acos(x) function, 820t
actual parameter lists:
value-returning functions, 850
void functions, 850
actual parameters:
passing derived class objects to
base class parameters,
165–166
preventing functions from
changing, 30–31, 855
adapters
...
See abstract data types
advancing queueRear in queues
as arrays, 457
algorithms, 4
backtracking
...
See STL algorithms
key comparisons in
...

See also Big-O values
(notation)
recursive algorithms, 357,
360–361; binary tree
traversal, 606–608
...
See
Big-O values (notation)
uses, 210, 211
See also search algorithms; sort
algorithms; and other
specific algorithms
allocating memory:
for dynamic arrays, 138,
147–148
for dynamic variables, 138–139,
142–145
See also memory allocation
American Standard Code for
Information Interchange
...
See address
of operator
reference parameter symbol,
149, 850, 851, 855
ampersands (&&): and operator, 810t
analysis: problem analysis, 3
See also performance analysis
and operator (&&): precedence,
810t
angular brackets (<>): system
header file delimiters, 76
See also extraction operator (>>);
greater-than operator (>);
insertion operator (<<); lessthan operator (<)
arbitrary binary trees, 609, 616
arc cosine/sine/tangent functions,
820t, 821t
arguments
...
See postfix expressions
See also arithmetic operators
arithmetic operations:
numeric algorithms, 750,
794–799
on pointers, 146n, 195
See also arithmetic operators
arithmetic operators, 834
overloading, 95–98
in postfix expressions, 428–430
precedence, 428, 809t
See also arithmetic operations

arithmetic STL function objects,
751–753
array elements:
accessing, 854
assigning values to, 854–855
finding: the largest/smallest
...
See arrayListType
class
constructor, 179
converting into heaps, 569–573
copy constructor, 180, 181
copying into vector containers,
756–757
defining as ADTs, 172–174,
498–499; ordered lists,
501–502
destructor, 179
inserting items in, 176–177,
182; ordered lists, 506–508
limitations, 266, 600
operations on, 175–183; timecomplexity limits, 183–184t
ordered lists: binary searches,
502–506, 508t; defining as
ADTs, 501–502; inserting
items in, 506–508
overloading the assignment
operator, 180–181
partitioning: mergesort, 559,
560–562; quicksort,
552–557
pivot element, 552–553, 554,
556–557, 826, 827,
828–829, 830–831
processing, 175–176; variables
for, 171
programming example, 187–194
removing items from, 177–178,
179, 183
replacing items in, 178–179
retrieving items from, 178
searching, 181–182

| 881

sorting: heapsort, 567–575;
insertion sort, 540–543;
quicksort, 552–558;
selection sort, 534–539
test program, 184–186
arrayListType class:
definition as an ADT, 172–174,
498–499
including functions in, 537
member functions, 175–183;
time-complexity limits,
183–184t
arrays (one-dimensional arrays),
854–855
character
...
See dynamic arrays
elements
...
See array indices
initializing, 855
lists as
...
See arraybased lists
queues as
...
See stacks as arrays
static arrays, 147
See also array-based lists; twodimensional arrays; vector
containers
ASCII character set, 811–812t
nonprintable characters, 812t
asin(x) function, 820t
assert function, 817t
using (accessing), 6
assert statements:
disabling, 817n
validating input with, 6
assign functions (containers),
229t, 322t
assigning values to variables, 835
assignment operator (=), 31
and classes, 31
compound assignment operators
(op=), 834; precedence,
810t

882

| Index

assignment operator (Continued)
container operation, 222t
deep copying with, 154–155
vs
...
See dereferencing
operator; multiplication
operator
asterisk–equal sign (*=): compound
assignment operator, 810t
asymptotic functions, 14
at function (containers), 213t, 229t
atan(x) function, 821t
automatic class objects, 32
AVL trees (height-balanced binary
search trees), 635–654, 636
building, 649–651
defining as ADTs, 651
deleting items from, 637, 652
inserting items in, 637,
637–641, 648–651
nodes, 637; creating, 651;
height types, 637

operations on, 637, 637–652
performance analysis, 653–654
quick review, 677
rotating/reconstructing, 639,
640, 641, 641–647;
functions for, 645–647;
rotation types, 641–644
See also B-trees

B
B-trees, 662–675, 663
basic operations, 663
...
See rear element (of
queues)
back function (containers), 213t,
229t, 322t
back function (linked lists), 288
doubly linked lists, 316
back function (queues), 452, 453,
470t, 471
linked queues, 466–467
queues as arrays, 461
back_inserter iterator, 757
backslash (\): escape character,
845n
backtracking algorithms:
n-Queens puzzle, 377–383
sudoku problem, 383–386
balance factor (bf) (of AVL tree
nodes), 637, 638n
indicators, 649
balanceFromLeft function,
645–646
balanceFromRight function,
646, 647
bar (|): separator operator, 810t
bars (||): or operator, 810t
base case for recursive definitions,
356–357
base class constructors:
calling/invoking, 70
defining, 72
base class data members:
accessing in derived classes:
private members, 62–63,
68; protected members,
78

base class formal parameters:
passing derived class objects
to, 162–168
base class member functions:
calling, 67, 68
overloading in derived classes,
63n, 67n
overriding in derived classes,
63–69
base classes, 60
constructors
...
See base class data
members; base class
member functions
See also baseClass class;
rectangleType class
baseClass class, 162–163
Baumert, L
...
See AVL trees
B
...
See AVL trees
inserting items in, 620–621
linear, 627
m-way search trees, 662–663
operations on, 617, 618–626
perfectly balanced trees,
635–636
performance analysis, 627–628;
AVL trees, 653–654; Big-O
value, 628
programming example, 654–662
quick review, 676–677
searching, 618–619
structure, 616–617
binary searches, 502–506
algorithms, 502–505, 506
Big-O value, 508t
functions, 503–504, 773–776
implementation methods, 504n
key comparisons in, 504,
504–505, 506, 508t
lower bound (order), 508–509
performance analysis, 506;
Big-O value, 508t
quick review, 525
See also binary search trees
binary tree traversal, 605–608,
628–632
B-trees, 666–667
node sequences, 606, 607
nonrecursive algorithms,
628–632
nonrecursive functions, 629,
630, 631–632
quick review, 676
recursive algorithms, 606–608

recursive functions, 608, 611,
612–613; overloading,
632–633
updating data during, 632–635
binary trees, 600–615
arbitrary trees, 609, 616
from array-based lists, 568–569,
569–574
basic operations, 609
...
See nodes (of binary trees)
operations on, 609, 611–615
paths, 603
perfectly balanced trees,
635–636
programming example, 654–662
quick review, 676–677
searchable
...
See binary tree
traversal
binary_search function,
773–776
binarySearch function, 503–504
including in orderedArray
ListType class, 508
binaryTreeType class:
definition as an ADT, 609–611
member functions, 611–615
binding of member functions, 164
black-box testing, 7–8
blank (blank space character) ( ):
extraction operator and, 838
bool data type, 833, 833t, 834
Boolean expressions (logical
expressions), 834, 846
Boolean operators (logical operators):
precedence, 809t, 810t

| 883

Boolean values (logical values),
834, 846, 847
bOp operation, 786
boundary values, 8
boxType class, 66–67, 70
constructor definitions, 70, 72
member function definitions,
67–69
brackets (square) ([] See array
)
...
Java features,
833–855
C++ library
...
See also objectoriented design
preprocessor directives
...
See syntax
testing
...
See C-strings
character functions (cctype
header file), 818–819t
character sets (encoding schemes):
ASCII set, 811–812t;
nonprintable characters,
812t
EBCDIC set, 812–813t
characters:
arrays of
...
See Symbols section at
the top of this index
as strings
...
See data members
(instance variables)
declaring, 18, 20n
functions
...
See private class
members
protected
...
See public class
members
scope, 32
types, 18
variables
...
See data
data for: operations on
...
See this
pointer
identifying, 4, 48–49

Index

implementation details, 34;
hiding, 25, 33
initializing of, 83–84, 159
logical properties, 33, 34
operators on, 31, 85
overloading operators for
...
See pairs (pair objects)
as parameters of functions, 32
passing as parameters to
functions, 30–31,
160–161; derived class
objects to base class formal
parameters, 162–168
referencing in member function
definitions, 87–91
scope, 32
class scope, 32
class templates, 108, 111–113,
210, 211
and class/function definition
placement, 112–113
defining, 111–112
function members
...
See function
objects
instantiations of, 112
passing parameters to, 112,
663–664
priority_queue class
template, 472
quick review, 115
syntax, 111
See also containers; iterators
class variables
...
See base classes
composite classes, 79–84
creating from existing classes
...
See class definitions
derived
...
See class objects
linked list nodes as, 267
as linked lists
...
See class members
overloading operators for
...
See queues
quick review, 50–51
relationships between
...
See stacks
as structs
...
See class templates
variables
...
See also
function members, below
constructors, 22, 29–30, 32–33
definition of, 18–21, 22, 25; as
an ADT, 34; including
constructors, 22
enhancements, 85
function members, 20, 21,
28–29; definitions of,
26–28
close function, 845
closed hashing
...
See open
addressing
open hashing
...
See
relational operators
comparison tree for sorting three
items, 551
comparison-based search
algorithms: lower bound
(order), 508–509
See also search algorithms
comparison-based sort algorithms:
lower bound (order), 551–552
See also sort algorithms
comparisons, key
...
See
Big-O values (notation)
composite classes: defining, 79–84
composition, 79–84
quick review, 113
compound assignment operators
(op=), 834
precedence, 810t
computer simulations, 472–474
See also movie theater service
simulation
computers:
encoding schemes
...
See programs
(computer programs)
concatenating strings: C-strings, 822t

886

| Index

conditional operator (?:):
precedence, 810t
connected vertices/graphs, 689
consecutive items: finding, 754,
777
const (reserved word):
for arrays, 855
for member functions, 20,
30–31
for named constants, 834
const_cast operator:
precedence, 809t
const_reference typedef, 237t
constant expressions: passing as
parameters to class templates,
663–664
constant pointers, 148–149
constant polynomials, 187
constant reference parameters,
20, 31
in arrays, 855
constants
...
See base class
constructors
calling/invoking, 23; base class
constructors, 70; member
object constructors, 83
copy
...
See default constructors
defining, 29–30; base class
constructors, 72; derived
class constructors, 70, 72
derived class
...
See
constructors with
parameters
properties, 21
for queues as arrays, 462–463
quick review, 51
for server lists, 482
for stacks as arrays, 407
types, 21
waiting customer queue
constructor, 485
weightedGraphType class
constructor, 706

constructors with parameters, 21,
29–30, 221t
calling/invoking, 23, 83
declaring class objects with,
23–24
default parameters, 32–33, 71n
defining, 29–30, 72, 75; derived
class constructors, 70
container adapters, 211, 732, 747t
See also queues; stacks
containers, 211–225, 736–747
adapters
...
See associative
containers
begin function, 217
counting elements, 782–783,
784–785
declaring iterators into,
216–217, 236–237
deque
...
See list containers
member functions common to,
217, 220, 221–222t
operations on, 748
outputting elements, 223–227
processing elements, 786–788
quick reviews, 255–256,
799–800
removing elements, 764–768
replacing elements, 768–770
returning first/last element
positions, 217
reversing elements, 779–782
rotating elements, 779–782
sequence
...
See vector containers

control structures, 846–847
converting:
array-based lists into heaps,
569–573
characters to lower/upper case,
819t
decimal numbers to binary
numbers, 372–375
copy constructors, 159–162, 196
for array-based lists, 180
for binary trees, 614–615
execution of, 161
including in class definitions,
161–162
for linked lists, 285, 290
for linked queues, 468
for linked stacks, 423
and shallow copying, 159
for stacks as arrays, 407–408
copy function (algorithm),
223–225
ostream iterators and,
225–227
copying:
array-based lists into vector
containers, 756–757
binary trees, 604–605, 614,
614–615
C-strings, 822t
class object values, 31,
157–158, 159–162
deep copying, 154–155
dynamic arrays, 153–155
linked lists, 289–290, 290
linked stacks, 422–423
pointers, 145
shallow
...
See for loops
counting items, 782–783,
784–785

Index

counting operations in algorithms,
10–13
cout object, 5n
cout statements, 840–843,
843–844
syntax, 840

...
begin function, 221t
ct
...
empty function, 221t
ct
...
erase functions, 221t, 739t,
744t
ct
...
max_size function, 221t
ct
...
rend function, 221t
ct
...
swap(ct2) function, 221t
ctType ct
statements, 737–738t,
742–743t
ctType ct
statements, 737–738t,
742–743t
current pointer (of linked lists),
268–269, 269–270,
439–440
doubly linked lists, 311, 313,
314–315
customer object (Video Store
programming example),
337–338
customer objects (movie theater
service simulation), 473
class
...
See transaction time
waiting queues
...
See
transaction time
customerType class (movie
theater service simulation),
474–477
data members, 474
definition as an ADT, 475–476
member functions, 476–477

customerType class (Video Store
programming example):
personType class and, 337
cycles (in graphs), 689

D
dangling pointers: avoiding, 141
data (for objects), 4
inputting
...
See operations
outputting
...
See shallow
copying
updating binary tree node data,
632–635
See also char data; int data;
values
data abstraction, 34
data members (instance variables),
18, 24, 30
access specifiers, 18, 20n, 61,
78–79
base class
...
See private data
members
protected data members, 78
scope, 32
See also class members
data operations
...
See abstract data
types (ADTs); arrays; classes;
lists; queues; stacks; structs
data types:
abstract
...
See also
pointers; simple data types;
and structured, below
classes as, 4
file stream data types, 843
numeric
...
See simple data types
string::size_type data
type, 822
structured
...
See enumeration
data types; and structured,
above
dateType class, 79
definition of, 80–81
member function definitions,
81–82
DBL_DIG constant, 819t
DBL_MAX constant, 819t
DBL_MIN constant, 819t
deallocating memory:
for dynamic arrays, 155–156
for dynamic variables, 139–141,
144, 160, 168
for nodes of linked lists,
286–287, 290, 313–314
debugging programs, 7
decimal format, fixed: setting,
841–842
decimal numbers: converting to
binary numbers, 372–375
decimal places: setting output, 841
decimal point: showing with trailing
zeros, 842
declaration statements:
class objects, 23–24
named constants, 834
one-dimensional arrays, 854
variables, 835
declaring:
arrays, 854; as formal
parameters, 855
associative containers: map/
multimap containers,
742–743; set/multiset
containers, 737–738
class members, 18, 20n
class objects, 23–24, 23n,
32, 159
friend functions, 91
identifiers, 18
iterators into containers,
216–217, 236–237
named constants, 834
pair objects, 732

888

| Index

declaring (Continued)
pointers, 132–133, 135; as
parameters to functions,
149
sequence containers: deque
containers, 228, 228t;
list containers, 321;
vector containers,
212–213, 212t, 215
variables, 18, 835; derived class
objects, 69; file stream
objects, 843–844
virtual functions, 164–165
See also declaration statements
decrement operator (––), 834
precedence, 809t
decrementing pointers, 146
decToBin function, 373–375
deep copying (of data), 154–155
default constructors, 21, 29, 30,
33, 221t
for binary trees, 612
calling/invoking, 23
declaring class objects with, 23,
23n
with default parameters, 32–33,
71n
defining, 29, 72; derived class
constructors, 70
for linked lists, 286; doubly
linked lists, 313
for linked queues, 468
for linked stacks, 418
default member access specifier,
18, 61
default member-wise initialization
(of class objects), 159
default parameters:
constructors with, 32–33, 71n
functions with, 852–853
define preprocessor directive, 77
definitions
...
See double quotation
marks; separator operator;
sequencing operator
depth-first ordering (of vertices),
696
depth-first traversal (of graphs),
696–698
topological ordering of vertices,
714, 728–729

depth-first traversal algorithm, 696,
696–697
depthFirstTraversal function,
697–698
deq
...
at function, 229t
deq
...
front function, 229t
deq
...
push_front function, 229t
deq[index] statement, 229t
deque class, 227
deque containers, 227–231
declaring, 228, 228t
header file, 747t
initializing of, 228, 228t
inserting items in, 227–228
iterator support, 747t
operations on, 228–231
quick review, 255–256
See also containers; sequence
containers
dereferencing operator (*),
133–134, 135
iterator operation, 231, 280
placement of, 132–133; in
formal-parameter-asreference-parameter
declarations, 149–150
precedence, 137, 809t
derived class constructors, 63,
69–75
defining, 70, 72
derived class member functions, 69
defining, 67–69, 74–75
derived class objects:
declaring, 69
passing to base class formal
parameters, 162–168
derived classes, 60
accessing base class data
members in: private
members, 62–63, 68;
protected members, 78
constructors
...
See derived
class member functions
function overloading in, 63n,
67n
function overriding in, 63–69
header files, 75–76
inheritance rules, 62, 69

Index

linked queues derived from
linked lists, 469
linked stacks derived from linked
lists, 426–427
objects
...
See directed graphs

diminishing-increment sort,
549–550
direct recursion, 358–359
directed edges (of binary trees),
600
directed graphs (digraphs), 687,
688, 689, 690–691
vertices
...
See member
access operator
double data type, 834
named constants, 819t
See also double variables
double hashing, 518–519
double quotation marks (""):
user-defined header file
delimiters, 76
double rotation (of AVL trees),
642–644, 644
functions for, 645
double variables:
data type, 834
inputting (reading) data into,
838–839
invalid input, 839–840
valid input, 839t
See also variables
double-ended queues
...
See under nodes (of
linked lists)
operations on, 311, 313–320
printing, 314–315; in reverse
order, 315
quick review, 343
searching, 315
traversing, 311, 313, 314–315
See also list containers
doublyLinkedList class:
default constructor, 313
definition as an ADT, 311–313
member functions, 313–320
See also doubly linked lists
dummy customer technique
(waiting queue problem), 486
dynamic arrays, 147
copying, 153–155
creating, 138, 147–148
deque containers as, 227–228
destroying, 155–156
programming example, 187–194
quick review, 196
vector containers as, 211
dynamic binding, 164
dynamic data structures
...
See character
sets

890

| Index

end function (containers), 217,
221t
end function (linked lists), 288–289
endif preprocessor directive, 77
endl manipulator, 840
enumeration data types, 833
equal sign (=):
assignment operator
...
See equality
operator
‘‘equal to’’ operator:
in infix expressions
...
assignment operator (=),
846–847
container operation, 222t
overloading, 96–97
pair class definition, 734t
precedence, 809t
equalTime function, 28–29
equivalence categories (test cases),
7
erase functions (containers),
214t, 221t, 223t, 739t, 744t
erase functions (strings), 824t
erasing strings, 824t
error handling (postfix expressions
calculator), 435
error stream, standard, 5n
escape character (\), 845n
Euler, Leonhard: on the Konigsberg
¨
bridge problem, 686
Euler circuits, 719–722, 720
conditions for, 720–721
constructing, 721–722
Eulerian graphs, 720
evaluateExpression function
(postfix expressions calculator),
432–434
evaluateOpr function (postfix
expressions calculator),
432–434
exclamation mark (!): not operator,
809t
exclamation mark–equal sign (!=)
...
exe extension, 836
executable code, 836

executable code file extension, 836
execution of constructors, 21, 23, 83
exp(x) function, 821t
exponential functions, 821t
expressions
...
See EBCDIC
character set
extensions for program files, 836
extraction operator (>>), 86, 837,
838n
in cin statements, 837–840,
843–844
overloading, 98, 99–100,
100–102, 105
precedence, 809t
using file stream objects with,
845

F
fabs(x) function, 821t
factorials (of integers), 356
calculation function, 357–358
fail state (input stream), 840
See also input failure
Fibonacci numbers:
in AVL trees, 653–654
recursive calculations, 366–369
FIFO (First In First Out) data
structure
...
See file I/O
opening, 844, 846
program file names and
extensions, 836
project file paths, 845n
See also header files;
implementation files; input
files; output files; and
specific file names
fill function, 758–760

fill_n function, 758–760
filling containers, 758–760,
760–762
filling two-dimensional arrays,
152–153
find function, 762–763
find function (strings), 823t
find_end function, 763–764
find_first_of function,
763–764
find_if function, 762–763
finding items, 762–764
consecutive occurrences, 754,
777
largest element, 783, 784–785;
recursive approach,
360–363; selection sort,
539n
smallest element, 783–784,
784–785; selection sort,
534–537
substrings, 824t
See also searching; searching
lists
first element
...
See queues
first pointer (of linked lists),
266, 274–275, 277–278,
280, 285
See also head pointer (of linked
lists)
fixed decimal format: setting,
841–842
fixed manipulator, 841–842
Fleury’s algorithm, 721–722
float data type, 834
named constants, 819t
floating-point data types, 833, 834
floating-point numbers:
data types, 833, 834
default output, 841
formatting output: precision
control, 841; scientific
notation, 841, 842
named constants, 819t
floor(x) function, 821t
FLT_DIG constant, 819t
FLT_MAX constant, 819t
FLT_MIN constant, 819t
folding hash function, 512
for loops: processing arrays with,
536–537, 543
for_each function, 786–788

Index

formal parameter lists:
value-returning functions, 849
void functions, 850
formal parameters:
declaring arrays as, 855
declaring as reference
parameters, 149
passing derived class objects to
base class parameters,
162–168
passing functions as: to other
functions, 632–635,
786–788
types
...
See queueFront
pointer
front_inserter iterator, 757
Fruit Juice Machine programming
example, 38–48
fstream header file, 843–844
function call operator: overloading,
751

function calls
...
See member
functions
function names:
overloading
...
See parameters of
functions
asymptotic, 14
Big-O functions, 14–16, 17t
calling
...
See member
functions (class function
members)
class objects as parameters of, 32
constructors as, 21
with default parameters,
852–853
definitions
...
See friend
functions (nonmember
functions)
growth rates, 12–15, 12t, 14,
14t
...
See
constructors
main
...
See constructors
nonrecursive
...
See operator functions
overloading
...
See parameters of
functions
parentheses in, 850
passing as parameters to other
functions, 632–635,
786–788
passing parameters to
...
See predefined
functions
preventing from changing actual
parameters, 30–31, 855
recursive
...
See return values
(from value-returning
functions)
for searching lists
...
See sort
functions
STL
...
See function
templates
user-defined
...
See valuereturning functions
virtual functions, 164–168
void
...
F
...
See STL
algorithms
getFreeServerID function, 483
getline function (strings), 823t
getNumberOfBusyServers
function, 483
global identifiers: overlapping
names problem, 847
Golomb, S
...
See also
graph traversal
checking whether empty, 693
clearing, 694–695
connected, 689
constructor, 695
creating, 693–694
cycles in, 689
defining as ADTs, 692–693
destructor, 695
directed graphs, 687, 688, 689,
690–691
Eulerian graphs, 720
printing, 695
quick review, 722–724
representations of, 689–691
shortest path algorithm, 700,
701–706
simple graphs, 689
terminology, 687, 689, 700,
707
traversing
...
See vertices
weighted graphs, 700
...
See
extraction operator
greater-than-or-equal-to operator
(>=):
container operation, 222t
pair class definition, 734t
precedence, 809t

greedy algorithm (shortest path
algorithm), 700, 701–706
growth rates of functions, 12–15,
12t, 14, 14t
See also Big-O values (notation)

H
Hamblin, Charles L
...
See
inheritance
hash functions, 509–512
choosing, 511
double hashing, 518–519
linear probing, 513–515,
518–519
quadratic probing, 516–518
random probing, 515
rehashing, 516
hash tables (HTs), 509–511
clustering in
...
See also
collision resolution
data organization in, 510
deleting items from, 519–520,
524
inserting items in, 523
keys for, 509, 511, 512,
519–520
searching, 523
hashing (search algorithm),
509–525
closed
...
See chaining
performance analysis, 524, 525t
quick review, 525–527
rehashing, 516
See also hash functions; hash
tables
head pointer (of linked lists), 266,
268–269, 269–270
See also first pointer (of
linked lists)
header files (specification files),
835, 836
class definition placement in,
112–113
for containers, 747, 747t
for derived classes, 75–76
directives to implementation
files, 113
for function objects, 751

Index

for functions, 817–819,
820–824
including, 75–76, 835–836;
avoiding multiple inclusions,
76–77
for named constants, 819–820
for ordered linked lists, 307–308
for priority queues, 747t
for queues, 472, 747t
for stacks, 408, 424, 440–441,
747t
for unordered linked lists,
298–299
See also specific header files
header nodes (in linked lists),
325–326, 343–344
heap algorithms: STL algorithms,
750
See also under heapsort
heapify function, 572–573, 574,
575
heaps, 568
converting array-based lists into,
569–573
implementing priority queues as,
575–576
sorting
...
quicksort, 575
height (of binary trees), 603–604
height function (binary trees),
604, 613
height-balanced trees
...
See hash tables
hyperbolic cosine/sine/tangent
functions, 821t
hyphen–right angle bracket (->)
...
See for loops
indices
...
See
dereferencing operator
inequality operator (!=):
container operation, 222t
overloading, 96–97
pair class definition, 734t
precedence, 809t
infinite recursion, 359
infix expressions, 428, 428t
infix notation, 428
information hiding, 25, 33
inheritance, 4, 60–79
multiple inheritance, 60–61
private inheritance, 61, 62,
69, 79

| 893

programming example, 238–254
protected inheritance, 79
public inheritance, 61–62, 78
quick review, 113
rules for base and derived
classes, 62, 69
single inheritance, 60–61
structure (hierarchy), 61
See also base classes; derived
classes
initializeList function
(operation):
for linked lists, 287; doubly
linked lists, 314
initializeQueue function
(operation), 452
for linked queues, 466
for queues as arrays, 458, 461
initializeStack function
(operation), 398
for linked stacks, 418–419
for stacks as arrays, 403
initializing:
arrays, 855; queues as arrays,
461; stacks as arrays, 403
of associative containers: map/
multimap containers,
742–743; set/multiset
containers, 737–738
of class objects, 83–84, 159
of data members (instance
variables), 18, 21, 25, 26
linked lists, 286, 287; doubly
linked lists, 314
pointers, 138
queues, 452; linked queues, 466;
queues as arrays, 458, 461
of sequence containers: deque
containers, 228, 228t;
list containers, 321;
vector containers,
212–213, 212t
stacks, 398; linked stacks,
418–419; stacks as arrays,
403
variables, 18
inner_product function, 794,
796–797, 798–799
inOrder function (B-tree
traversal), 666
inorder function (binary tree
traversal), 608, 611, 612–613
overloading, 632–633
inorder sequence, 606, 607

894

| Index

inorder traversal (of binary trees),
605, 606–608
node sequence, 606, 607
nonrecursive algorithm, 628–629
nonrecursive function, 629
recursive algorithm, 606–608
recursive function, 608, 611,
612–613; overloading,
632–633
inorderTraversal function,
632–635
inplace_merge function,
778–779
input: validating, 6
See also inputting data (reading
data)
input failure, 839–840
input files:
creating linked lists from, 338
opening, 844, 846
input iterators, 232
operations on, 232t
input statements
...
See data
members
instantiations of class templates,
112
int data type, 833, 833t, 834
named constants, 820t
See also int variables
int variables:
data type, 833, 833t, 834
inputting (reading) data into,
838–839
invalid input, 839–840
valid input, 839t
See also variables
INT_MAX constant, 820t
INT_MIN constant, 820t
integers:
data types, 833–834
factorials, 356; calculation
function, 357–358
named constants, 820t
See also char variables; int
variables
integral data types, 833–834
interface files
...
See under
calling functions
I/O (input/output operations):
file I/O, 843–846
header file, 835, 843
See also inputting data (reading
data); outputting data
iostream header file, 835, 843
‘‘is-a’’ relationships
...
, 375–376
iteration control structures (loops),
375, 846
iterators, 211, 216, 231–238, 280
bidirectional iterators, 234
for containers, 747t
declaring into containers,
216–217, 236–237
forward iterators, 233–234
hierarchy, 236
input iterators, 232
insert iterators, 756–758
istream iterators, 237–238
of linked lists, 280
operations on, 231, 232t,
233t(2), 234t, 235t, 280
ostream iterator, 238

ostream iterators: and the copy
function, 225–227
output iterators, 232–233
quick review, 254, 255, 256,
801
random access iterators,
234–235
returning the first/last node
iterator, 288–289
stream iterators, 237–238
typedef const_iterator,
236
typedef const_reverse
_iterator, 237
typedef iterator, 216, 236
typedef reverse_iterator,
237
types, 232

J
Java features: C++ features vs
...
See reserved words
Knuth sequence, 550
Konigsberg bridge problem, 686,
¨
719, 721

| 895

L
languages: Unified Modeling
Language (UML) diagrams,
22–23
See also programming languages
larger function: overloading,
108, 109–111
largest element, finding, 783,
784–785
recursive approach, 360–363
selection sort, 539n
largest function, 361–363
largest whole number function,
821t
last element (of queues)
...
See stacks
last pointer (of linked lists),
274–275, 278, 280, 285
LDBL_DIG constant, 819t
LDBL_MAX constant, 819t
LDBL_MIN constant, 819t
leaf nodes (of binary trees), 603
left angle bracket (<)
...

See less-than-or-equal-to
operator
left angle brackets (<<)
...
H
...
See insertion
operator
less-than-or-equal-to operator (<=):
container operation, 222t
pair class definition, 734t
precedence, 809t
level of a node (of binary trees), 603
libraries
...
See stacks
linear binary search trees, 627
linear probing (in hash tables),
513–515
as double hashing, 518–519
performance analysis, 525t
linear queues
...
See sequential
searches
linear stacks
...
See also
operations on, below
building backward, 274,
277–278, 279
building forward, 274,
274–277, 279
checking whether empty/full, 286
circular linked lists, 326
class member functions,
285–292
copy constructor, 285, 290
copying, 289–290, 290
creating from input files, 338
current (pointer), 268–269,
269–270
default constructor, 286
defining as ADTs, 282–286
deleting items (nodes) from,
273–274, 295–298
deriving linked queues from, 469
deriving linked stacks from,
426–427
destroying, 286–287

destructors, 290
dividing, 560–562
doubly linked
...
See nodes (of linked lists)
operations on, 269, 278–279,
286–291; time-complexity
limits, 291–292t
ordered
...
assign functions, 322t
listCont
...
front function, 322t
listCont
...
pop_front function,
322t
listCont
...
remove function, 322t
listCont
...
reverse function, 323t
listCont
...
splice functions,
322–323t
listCont
...
See array-based lists
defining as ADTs, 35–36; with
class templates, 111–112
item delimiter
...
See lengths of lists,
determining
linked
...
See searching lists
sorting
...
See deallocating
memory; memory allocation
make_pair function, 734–736
manipulators (of output), 840
endl manipulator, 840
fixed manipulator, 841–842
left manipulator, 843
right manipulator, 843
scientific manipulator, 842
setprecision manipulator,
841
setw manipulator, 842
showpoint manipulator, 842
map class, 732
map containers, 742–747
declaring and initializing,
742–743

| 897

header file, 747t
item insertion and deletion,
743–744
iterator support, 747t
sort criteria, 742, 743
using (including), 742
math functions (cmath header file),
820–821t
matrices, adjacency, 689–690
matrix operations, 206–207
max function, 604, 613, 783
max_element function, 783,
784–785
max_size function (containers),
218t, 221t
maxListSize function (arraybased lists), 175
member access operator (dot
operator) (
...
See data members
member functions (class function
members), 18
access specifiers, 18, 20n, 61,
78–79
access to other class members,
18, 20n
accessing, 24–25, 28–29, 32
base class functions
...
See calling functions
const specifier, 20, 30–31
defining, 18, 26–28, 81–82,
84; referencing class objects
in, 87–91
definition placement, 112
derived class functions
...
See
passing parameters to
functions
private function members,
21n, 285
referencing class objects in
defining, 87–91
referencing function member
identifiers, 25–26
scope, 32
virtual functions, 164–168
See also class members; and
under specific classes
member functions (class template
function members):
as function templates, 112
functions common to all
containers, 217, 220,
221–222t
functions common to sequence
containers, 222, 223t
member object constructors:
passing arguments to, 83
member objects (in composition),
79–84
member variables
...
See also memory
allocation
deallocating: for dynamic arrays,
155–156; for dynamic
variables, 139–141, 144,
160, 168; for nodes of
linked lists, 286–287, 290,
313–314
leaks, 141
memory allocation:
for instance variables, 24
for recursive functions, 375
memory cells: addresses
...
See decrement
operator
minus function object, 751t
modifying algorithms (STL),
749–750, 749t
modular programming, 4
modulus operator (%): precedence,
809t
modulus function object,
751t
movie theater service simulation,
472–490
class specification, 474–486
function for running, 488–489
information required, 486–487,
488
main program, 486–489
object identification, 473
objects
...
See function names
operator functions, 86
namespace mechanism, 847–849
namespace members: accessing
(using), 848–849, 849n
namespace statements, 847–848
naming identifiers, 847
natural log function, 821t

Index

NDEBUG preprocessor directive,
6n, 817n
negate function object,
751t
new operator:
creating dynamic arrays,
138–139, 147, 148
creating dynamic variables,
138–139
creating two-dimensional arrays,
150–151
precedence, 809t
syntax, 138
newline character (\n): extraction
operator and, 838
nodes (of AVL trees), 637
creating, 651
height types, 637
nodes (of B-trees): definition of, 664
nodes (of binary search trees), 617
AVL trees, 637, 651
B-trees, 664
nodes (of binary trees), 600–601,
602, 603
level, 603
pointers of/to, 602, 608; root
pointer, 603, 606
root
...
See vertices
nodes (of linked lists), 266–269
components, 266
deallocating memory for,
286–287, 290, 313–314
definition of, 267, 270, 279
deleting, 273–274, 295–298;
from doubly linked lists,
318–320; from ordered
linked lists, 306–307
inserting, 270–273, 325; in
doubly linked lists,
316–318; first/last node,
279, 294–295, 325; in
ordered linked lists, 300,
302–305, 325–326
pointers to, 266, 268–269,
269–270, 274–275, 278,
438–440
printing the data in, 287

returning first/last node data,
288; from doubly linked
lists, 316
returning the first/last node
iterator, 288–289
as structs, 267, 279n
nonmember functions
...
See
inequality operator
not operator (!): precedence, 809t
not_equal_to function
object, 753t
npos
...
See floating-point data
types; integral data types
Fibonacci number calculations,
366–369
See also floating-point numbers;
integers
numeric algorithms (STL), 750,
794–799

| 899

numeric data types
...
See Big-O values (notation)

...
See member
objects (in composition)
object-oriented design (OOD), 4,
17, 59–130
basic principles, 4
...
See classes
hardest part, 48
See also composition
object-oriented programming
(OOP)
...
See class objects
ofstream data type, 843
one-dimensional arrays
...
See object-oriented design
OOP (object-oriented
programming): languages, 4
See also object-oriented design
op= (compound assignment
operators): precedence, 810t
open addressing (closed hashing),
512–523
double hashing, 518–519
hash table item deletion,
519–520
linear probing
...
See quadratic
probing
quick review, 526–527
random probing, 515
rehashing, 516
open function, 844, 846
open hashing
...

See also on binary search
trees, above
bOp operation, 786
built-in operations on class
objects, 31, 85
on container adapters
...
See on sequence
containers, below
on deque containers, 228–231
determining, 4, 48–49
on doubly linked lists, 311,
313–320
on graphs, 691
on iterators, 231, 232t, 233t(2),
234t, 235t, 280
on linked lists, 269, 278–279,
286–291; doubly linked
lists, 311, 313–320;
ordered linked lists,
301–307, 307t, 326; timecomplexity limits,
291–292t; unordered linked
lists, 293–298
on linked queues, 465–468
on linked stacks, 418–423;
time-complexity limits, 424t
on list containers, 322–325,
322–323t, 746–748
on ordered linked lists,
301–307, 326; timecomplexity limits, 307t
on pointers, 134–137
on priority queues, 575–576
on queues, 452–453, 470t;
linked queues, 465–468;
priority queues, 575–576;
queues as arrays, 460–462
on queues as arrays, 460–462
on sequence containers, 222,
223t; deque containers,
228–231; list containers,

322–325, 322–323t,
746–748; vector
containers, 213–216
on stacks, 397–398, 441t;
linked stacks, 418–423,
424t; stacks as arrays,
403–409, 409t
on stacks as arrays, 403–409;
time-complexity limits, 409t
on unordered linked lists,
293–298
on vector containers, 213–216
See also functions; member
functions
operator functions, 86
in class definitions, 86, 94
as member functions, 94, 95–97,
102
names, 86
as nonmember functions, 94,
97–98, 102
syntax, 86
See also operator* function;
and other specific operator
functions following
operator overloading, 84, 85–102, 86
arithmetic operators, 95–98
assignment operator, 158–159;
for array-based lists, 180;
for binary trees, 615; for
linked lists, 285, 291; for
linked queues, 468; for
linked stacks, 423; for
stacks as arrays, 408
binary operators, 95–98
extraction operator, 98, 99–100,
100–102, 105
functions for
...
See address
of operator
arithmetic
...
See array index
(subscript) operator
assignment
...
See binary operators
on class objects, 31, 85
compound assignment operators,
810t, 834
decrement
...
See dereferencing
operator
extraction
...
See increment
operator
insertion
...
See logical operators
member access
...
See operator
overloading
precedence
...
See relational
operators
scope resolution operator (::),
25–26, 809t, 848
unary
...
See Big-O values
(notation)
order of precedence
...
See under nodes (of
linked lists)
operations on, 301–307; circular
linked lists, 326; with
header and trailer nodes,
326; time-complexity limits,
307t
printing in reverse order,
363–366, 438–440
searching, 301–302
test program, 309–310
orderedArrayListType class:
definition as an ADT, 501–502
including functions in, 508
orderedLinkedList class, 279
definition as an ADT, 300–301
linkedListType class and,
279, 300, 308–309
member functions, 300,
301–307; time-complexity
limits, 307t
See also ordered linked lists
ostream iterator, 238
ostream iterators: and the copy
function, 225–227
out of bounds indices (of arrays),
854–855

output:
formatting
...
See outputting data
output files: opening, 844, 846
output iterators, 232, 233n
operations on, 233t
output manipulators
...
See cout
statements
outputting data, 840–843
in columns, 842, 843
to files, 843–846
overflow (of hash table buckets),
511, 524
overflow condition (of stacks as
arrays): checking for, 405
overloading:
the function call operator, 751
functions
...
See operator
overloading
overriding base class member
functions in derived classes,
63–69

P
pair class, 732–736
relational operators for, 734, 734t
using (accessing), 732
pair type, 734
pairs (pair objects):
comparing, 734
creating, 734–736
declaring, 732
parallel edges (in graphs), 689
parameter lists
...
See passing
parameters
parameterized types (of class
templates), 111
parameters of functions:
actual
...
See formal parameters
functions with default
parameters, 852–853
memory allocation for, 375
passing
...
See mergesort;
quicksort
partitioning array-based lists:
mergesort, 559, 560–562
quicksort, 552–557, 826–832
partTimeEmployee class, 73–75
passing parameters:
to class templates, 112,
663–664
to functions
...
See member
access operator
personalInfoType class, 79
definition of, 82–83
member function definitions,
83–84
and personType class, 79
personType class, 36, 73, 79
and customerType class, 337
definition of, 36–37, 88–89
function member definitions,
37–38, 89
and partTimeEmployee class,
73
personalInfoType class and,
79
program use of, 89–91
pivot element (array-based lists),
552–553, 554, 556–557,
826, 827, 828–829,
830–831
plus operator (+): precedence, 809t
plus sign (+):
addition operator
...
See increment
operator
plus function object, 751t
plus–equal sign (+=): compound
assignment operator, 810t
pointer arithmetic, 146n, 195

pointer data members: class
requirements (peculiarities),
155–162, 611
pointer data type, 132
pointer typedef, 237t
pointer variables
...
See dynamic arrays
basic operations, 134–137
...
See dynamic
variables
See also iterators; specific
pointers
Poisson distribution, 487–488
Polish notation, 428
polymorphism, 4
See also function overloading;
operator overloading; virtual
functions
polynomial operations:
programming example,
187–194
pop function (queue operation),
470t, 471
pop function (stack operation),
397, 398(2), 399, 441t
linked stacks, 419, 421–422
stacks as arrays, 405–406
pop_back function (containers),
215t, 223t
pop_front function (containers),
229t, 322t
postconditions of functions, 6–7
postfix expressions, 428–430,
428t, 443
evaluation algorithm, 428–431
evaluation program
...
See number sign (#)

Index

pow(x,y) function, 821t
precedence of operators, 809–810t
arithmetic operators, 428, 809t
dereferencing operator, 137,
809t
dot operator, 137, 809t
precision (of values): floating-point
numbers, 841
preconditions of functions, 5–7
predCount array, 715, 716, 717,
718
predefined functions:
header files, 817–824
using (accessing), 835–836
predicates (function objects), 756
prefix notation, 428
pre-increment/decrement
operators: precedence, 809t
preorder function (binary tree
traversal), 608, 613
preorder sequence, 606, 607
preorder traversal (of binary trees),
605
node sequence, 606, 607
nonrecursive algorithm/function,
630
recursive function, 608, 613
preprocessor directives, 75–76,
76–77, 835–836
NDEBUG, 6n, 817n
syntax, 835
Prim’s algorithm, 708–712
alternative to, 727–728
primary clustering (in hash tables),
514–515
random/quadratic probing and,
515, 516, 518
print function (array-based lists),
175–176
print function (baseClass
class), 162–164, 167, 168
as a virtual function, 164–165
printGraph function, 695
printing:
array-based lists, 175–176
doubly linked lists, 314–315; in
reverse order, 315
graphs, 695
linked lists, 287; in reverse
order, 438–440
ordered linked lists in reverse
order, 363–366, 438–440
weighted trees, 712–713
printListReverse function:
ordered linked lists, 365–366

printResult function (postfix
expressions calculator), 435
printShortestDistance
function, 705–706
printTreeAndWeight function,
712–713
priority queues, 471–472,
575–576
header file, 747t
inserting items in, 575
removing elements from, 575,
576
See also queues
priority_queue class template,
472
private class members, 18, 78
data members
...
iteration, 375–376
processing array-based lists,
175–176
variables for, 171
processing container elements,
786–788
program design, 3–4
See also object-oriented design
program structure
...

See enumeration data types

| 903

programming:
modular/structured
programming, 4
references (resource texts), 857
programming examples:
array-based lists, 187–194
binary/binary search trees,
654–662
classes, 38–48
Complex Numbers, 103–107
dynamic arrays, 187–194
Election Results, 576–593
Fruit Juice Machine, 38–48
Grade Report, 238–254
Highest GPA, 411–415
inheritance, 238–254
linked lists, 327–343
operator overloading, 103–107,
576–593, 655–656
polynomial operations, 187–194
sort algorithms, 576–593
stacks, 411–415
vector containers, 238–254
Video Store, 327–343,
654–662
programming languages: objectoriented programming
languages, 4
See also high-level language
programs
programs (computer programs):
analysis (problem analysis), 3
C++
...
See also objectoriented design
development phase(s), 2, 3–8
high-level language
...
See queues as arrays
checking whether empty/full,
452, 453, 470–471
in computer simulations, 473
...
See deque
containers
dynamic
...
See queues as arrays
linked
...
heapsort, 575
performance analysis, 558t,
826–832
recursive function, 557–558
quickSort function, 558
quotation marks
...
See
memory (main memory)
random access iterators, 234
operations on, 235t
random probing (in hash tables), 515
and primary clustering, 515, 518
random_shuffle function, 784,
784–785
rbegin function (containers),
221t, 225
read statements
...
See inputting data

Index

rear element (of queues), 452, 453
linked queues, 464; returning,
466–467
queues as arrays, 454; returning,
461
returning, 453, 470–471; linked
queues, 466–467; queues
as arrays, 461
rear pointer
...
See
rotating/reconstructing AVL trees
records
...
See recursive
algorithms
definitions, 35–67
direct/indirect recursion, 358–359
functions
...
iteration, 375–376
problem solving with, 359–376
quick review, 386–387
recursive algorithms, 357, 360–361
binary tree traversal, 606–608
See also backtracking algorithms
recursive definitions, 356–357
recursive function calls, 357, 358,
375
recursive functions, 357–376
binary tree traversal functions,
608, 611, 612–613;
overloading, 632–633
calls to, 357, 358, 375
designing, 359
Fibonacci number calculations,
366–369
height function (binary trees),
604, 613
largest element search,
360–363
memory allocation for, 375
mergesort, 565
printing ordered linked lists in
reverse order, 363–366
problem solving with, 359–376

recMergeSort function, 565,
566
recQuickSort function,
557–558
Tower of Hanoi problem,
369–372, 376
redefining base class member
functions in derived classes,
63–69
reference parameter symbol (&),
149, 850, 851, 855
placement of in formalparameter-as-referenceparameter declarations,
149–150
reference parameters, 851–852
constant
...
See deleting items
rend function (containers), 221t
repetition
...
See
return values (from valuereturning functions)
last node data (of linked lists),
288; doubly linked lists,
316
rear element (of queues), 453,
470–471; linked queues,
466–467; queues as arrays,
461

906

| Index

returning (Continued)
substrings, 824t
top element (of stacks), 397,
398, 405, 441t; linked
stacks, 419, 420–421;
stacks as arrays, 405
reverse function, 779–782
reverse function (list
containers), 323t
Reverse Polish notation
...
See greaterthan operator
right angle bracket–equal sign (>=)
...
See
extraction operator
right manipulator, 843
right rotation (of AVL trees),
641–642, 644
functions for, 645, 646, 647
root node (of binary search trees):
key, 617
root node (of binary trees), 551,
600, 601
pointer to, 603, 606
root pointer, 603, 606
rooted trees, 707
rotate function, 779–782
rotate_copy function, 779–782
rotateToLeft function, 645
rotateToRight function, 645
rotating items, 779–782
rotating/reconstructing AVL trees,
639, 640, 641, 641–647
functions for, 645–647
rotation types, 641–644
run-time binding, 164
runSimulation function:
algorithm, 488–489

S
scientific manipulator, 842
scientific notation (format):
outputting floating-point
numbers in, 841, 842
scope: class members, 32

scope resolution operator (::),
25–26, 809t, 848
precedence, 809t
search algorithms, 498–531
binary search trees, 618–619
binary searches, 502–505, 506
dominant operations in, 12
key comparisons in
...
See
hashing
performance, 498
performance analysis, 12(2),
500–501, 506
quick review, 525
sequential searches, 181–182,
297, 499–501
See also hashing; search
functions
search function, 773–776
search function (B-trees), 665, 666
search function (binary search
trees), 618, 619
search function (linked lists),
293–294
search functions:
binary search trees, 618, 619
binary searches, 503–504,
773–776
sequential searches, 181–182,
293–294
STL functions, 773–776
search_n function, 773–776
searches
...
See searching lists
See also finding items
searching lists:
algorithms for
...
See search functions
linked lists, 293–294,
297–298, 334–335
ordered linked lists, 301–302

searchNode function (B-trees),
665, 666
secondary clustering (in hash
tables), 518
secondary storage: file I/O from/to,
843–846
selection control structures, 846
selection sort:
array-based lists, 534–539
linked lists, 539n
performance analysis, 539, 548t
test program, 538–539
selectionSort function, 537
including in arrayListType
class, 537
semicolon (;):
in class definitions, 18
as not in preprocessor directives,
835
separator operator (|): precedence,
810t
seqCont
...
erase functions, 223t
seqCont
...
pop_back function,
223t
seqCont
...
resize functions, 223t
seqSearch function, 181–182
sequence containers, 211–220,
227–231, 321–325
header files, 747t
iterator support, 747t
member functions/operations on,
222, 223t
predefined sequence containers,
211
quick reviews, 255–256
types: See also deque
containers; list containers;
vector containers
See also containers
sequencing operator (,):
precedence, 810t
sequential lists
...
See serverListType
class
operations on, 481
server objects (movie theater
service simulation), 473
class
...
See
transaction time
getting and setting the number
of, 487
list
...
, 842
setServerBusy function,
483–484
setSimulationParameters
function, 487
setw manipulator, 842

shallow copying (of data),
153–154, 157, 159–161,
196, 604
avoiding, 154–155, 157–159,
161–162, 604–605,
614–615
shape class, 61, 169–170
Shellsort, 549–550
shellSort function, 550
short data type, 833
named constants, 820t
shortest path (in graphs), 700
algorithm, 700, 701–705; Big-O
value, 705; function for,
704–705
shortestPath function, 704–705
showpoint manipulator, 842
SHRT_MAX constant, 820t
SHRT_MIN constant, 820t
shuffling elements randomly, 784,
784–785
simple assignment operator
...
See
assignment statements
simple data types, 833–834, 833t,
834n
user-defined (programmerdefined)
...
See also
char variables; double
variables; int variables;
string variables
simple graphs, 689
simple paths (in graphs), 689
simulation (of systems), 472
See also computer simulations
simulation parameters: getting and
setting, 486–487
sin(x) function, 821t
single inheritance, 60–61
sinh(x) function, 821t
size function (containers), 218t,
221t
size function (queues), 470t
size function (stacks), 441t
size function (strings), 823t
size_type string data type
...
See division operator
slash–equal sign (/=): compound
assignment operator, 810t
smallest element, finding,
783–784, 784–785
selection sort, 534–537
smallest whole number function,
821t
software, 2
See also programs
software engineering, 2
software engineering principles,
2–17
quick review, 49–50
solveSudoku function, 385–386
sort algorithms, 534–598
Big-O values: heapsort, 567,
575; insertion sort, 548t,
552, 826; mergesort, 558,
566–567; quicksort, 552,
558t, 827, 828, 830;
selection sort, 539, 548t,
552
comparison tree, 551
diminishing-increment sort,
549–550
heapsort, 472, 567–575
insertion sort, 540–548
mergesort, 558–567
order (lower bound): comparisonbased algorithms, 551–552
performance analysis: heapsort,
575; insertion sort, 548,
548t, 825–826; mergesort,
566–567; quicksort, 558t,
826–832; selection sort,
539, 548t
programming example, 576–593
quick review, 593–594
quicksort, 552–558
selection sort, 534–539
Shellsort, 549–550
sort function, 773–776
sort functions:
heapsort, 574
insertion sort, 543, 547–548
list container operations, 323t
mergesort, 564–565
quicksort, 557–558
selection sort, 537
STL function, 773–776
sorted linked lists, 279
sorting lists:
algorithms for
...
See sort functions
linked lists: insertion sort,
544–548; mergesort,
558–567; selection sort,
539n
source code (source program), 836
source code file extension, 836
source vertex, 700
space (blank space character) ( ):
extraction operator and, 838
spanning trees, 707
defining as ADTs, 710–711
See also minimum spanning
trees
special characters
...
See
implementation details of class
objects
specification files
...

square brackets ([] See array
index (subscript) operator
square root function, 821t
stack class, 440–442
stack header files, 408, 424,
440–441, 747t
stackADT class: definition of,
398–399
stacks, 395–450, 396, 397
adding elements to, 397–398,
398, 441t
as arrays
...
See linked stacks
evaluating postfix expressions
with, 428–437
header files, 408, 424,
440–441, 747t
initializing, 398
linear
...
See linked stacks

operations on, 397–398, 441t
postfix notation application
...
See predefined
functions
standard input device: inputting
data from, 837–839
standard output device: outputting
data to, 840–841

Standard Template Library
...
See assert
statements
assignment
...
See declaration
statements
input
...
See cout statements
static arrays, 147
static binding, 164
static class objects, 32
static_cast cast operator, 834
precedence, 809t
std namespace, 849n
stepwise refinement (structured
design), 4
STL (Standard Template Library),
209–263, 731–805
class templates, 210, 211, 472
components, 210–211, 732
...
See function
objects
function prototypes, 758
heap algorithms, 750
modifying algorithms, 749–750,
749t
mutating algorithms, 750
nonmodifying algorithms,
748–749, 749t
numeric algorithms, 750,
794–799
quick review, 800, 801–802
str
...
clear function, 824t
str
...
erase functions, 824t
str
...
insert functions, 824t
str
...
size function, 823t
str
...
insert function, 824t
str1
...
swap(str2) function, 824t
strcat function (C-strings), 822t
strcmp function (C-strings), 822t
strcpy function (C-strings), 822t
stream data types: file stream data
types, 843
stream extraction operator
...
See
insertion operator (<<)
stream iterators, 237–238
See also ostream iterators
stream variables:
cin
...
See cout statements
file stream objects, 843–844
string functions (string type),
823, 823–824t
See also C-string functions
string header file, 822–823,
823–824t
string::npos named constant,
822
string::size_type data type,
822
string variables:
outputting values, 841t
swapping contents, 824t
See also strings; variables
strings (string type), 822
checking whether empty, 823t
clearing, 824t
determining length/size, 823t
erasing, 824t
finding, 823t
functions for manipulating, 823,
823–824t
inputting data into, 823t
inserting characters in, 824t
outputting, 841t
replacing characters in, 824t
returning substrings in, 824t
See also C-strings; string
variables
strlen function (C-strings), 822t
strongly connected vertices/graphs,
689
structs (data types/variables), 33
accessing members via pointers,
137–138
AVL tree nodes, 637
B-tree nodes, 664
binary tree nodes, 602
defining, 33
linked list nodes, 267, 279n

structured data types
...
See structs
subgraphs, 687
subprograms
...
See postfix notation
swap function, 770–773
swap function (containers), 221t
swap function (sort algorithms),
537, 557, 827–828,
828–830
swap function (strings), 824t
swap_ranges function, 770–773
swapping items, 770–773
array elements, 535–537, 557
string variable contents, 824t
symbols
...
See simulation
(of systems)

T
tab character: extraction operator
and, 838
tail recursive functions, 359
tan(x) function, 821t
tanh(x) function, 821t
template instantiations, 112
templates, 84, 108–113
quick review, 114–115
syntax, 108–109
See also class templates;
function templates
temporary queues, 486
test cases, 7
testing programs, 7–8

910

| Index

theater service simulation
...
See Big-O values
(notation)
time-driven simulation, 474
tokens
...
See binary tree
traversal

doubly linked lists, 311, 313,
314–315
graphs
...
See implementation files
user-defined data types
...
See valuereturning functions; void
functions; and specific
functions
user-defined header files:
including, 76
USHRT_MAX constant, 820t
using namespace/
namespacename statements,
848–849, 849n
utility header file, 732, 734

V
validating input: with assert
statements, 6
value parameters, 851, 851
constant reference parameters
vs
...
See logical values
return
...
See under variables
See also data; numbers
variables:
for array-based list processing,
171
assigning values to, 835
as class members
...
See dynamic variables
file stream objects, 843–844
initializing, 18
inputting values
...
See data members
outputting values
...
See stream variables
valid input for simple data types,
838–839, 839t
See also char variables;
double variables; int
variables; string variables
vecCont
...
empty function, 218t
vecCont
...
size function, 218t
vecList
...
back function, 213t
vecList
...
erase functions, 214t
vecList
...
insert functions,
214t

vecList
...
push_back function,
215t
vecList
...
See vector
containers
vertices (in graphs), 687
adjacent, 689, 690, 691
breadth-first ordering, 698
connected, 689
depth-first ordering, 696
immediate successors, 691
keeping track of visited vertices,
696
labeling/numbering, 692
source vertex, 700
topological ordering
...
See passing
parameters to functions
with parameters, 850–852
syntax, 850–851

W
waiting customer queue objects
(movie theater service
simulation), 473, 484–485
accessing the elements of,
485
class
...
J
...
See reserved words


Title: Data structures C++
Description: complete notes of data structure using C++by Malik. Array, linked-list, stack, queue, binary search tree, sorting algorithm, graph, standard template library. for beginner all the way to professional level with detail explanation and concept and example.