You are on page 1of 149

Data Structures

and
Problem Solving
Using Java™
(Third Edition)
Instructor’s Resouce Manual

Mark Allen Weiss


3rd ed IRM revised by Tim Herman
Contents

CHAPTER 1 Primitive Java 1


1.1 Key Concepts and How To Teach Them 1
1.2 Solutions To Exercises 1
1.3 Exam Questions 3

CHAPTER 2 References 5
2.1 Key Concepts and How To Teach Them 5
2.1.1 References 5
2.1.2 Strings 5
2.1.3 Arrays 5
2.1.4 Dynamic Expansion 5
2.1.5 Exceptions 5
2.1.6 Input and Output 6
2.2 Solutions To Exercises 6
2.3 Exam Questions 7

CHAPTER 3 Objects and Classes 10


3.1 Key Concepts and How To Teach Them 10
3.1.1 The class Construct 10
3.1.2 Public and Private Sections 10
3.1.3 Specification vs. Implementation 10
3.1.4 Constructors 10
3.1.5 Accessors and Mutators 10
3.1.6 toString 11
3.1.7 equals 11
3.1.8 Packages 11
3.1.9 this 11
3.1.10 instanceof Operator 11
3.1.11 static Class Members 11
3.1.12 Design patterns 11
3.2 Solutions To Exercises 11
3.3 Exam Questions 13
iv

CHAPTER 4 Inheritance 15
4.1 Key Concepts and How To Teach Them 15
4.1.1 The Concept of Inheritance and
Polymorphism 15
4.1.2 Extending Classes 15
4.1.3 Dynamic binding and polymorphism 15
4.1.4 Designing hierarchies 15
4.1.5 Interfaces 16
4.1.6 Inheritance in Java 16
4.1.7 Generic Components 16
4.1.8 Wrappers and Adapters 16
4.1.9 Functor 16
4.1.10 Nested, anonymous and local classes 16
4.2 Solutions To Exercises 16
4.3 Exam Questions 26

CHAPTER 5 Algorithm Analysis 29


5.1 Key Concepts and How To Teach Them 29
5.1.1 What is Algorithm Analysis 29
5.1.2 Some Examples 29
5.1.3 The Maximum Contiguous Subsequence Sum Problem 29
5.1.4 Official Big-Oh Rules 30
5.1.5 Logarithms 30
5.2 Solutions To Exercises 30
5.3 Exam Questions 32

CHAPTER 6 Collections API 36


6.1 Key Concepts and How To Teach Them 36
6.2 Solutions To Exercises 36
6.3 Exam Questions 39

CHAPTER 7 Recursion 41
7.1 Key Concepts and How To Teach Them 41
7.1.1 What is Recursion? 41
7.1.2 Proof by Induction 41
7.1.3 Basic Recursion 41
7.1.4 Recursive Drawings 42
7.1.5 Numerical Applications 42
7.1.6 Divide-and-Conquer 42
7.1.7 Dynamic Programming and Backtracking 42
7.2 Solutions To Exercises 42
7.3 Exam Questions 45
v

CHAPTER 8 Sorting 49
8.1 Key Concepts and How To Teach Them 49
8.1.1 Motivation for Sorting 49
8.1.2 Insertion Sort Analysis 49
8.1.3 Shellsort 49
8.1.4 Mergesort 49
8.1.5 Quicksort 49
8.1.6 Selection 50
8.1.7 Lower Bound for Sorting 50
8.2 Solutions To Exercises 50
8.3 Exam Questions 54

CHAPTER 9 Randomization 56
9.1 Key Concepts and How To Teach Them 56
9.1.1 Linear Congruential Generators 56
9.1.2 Permutation Generation 56
9.1.3 Randomized Algorithms 56
9.2 Solutions To Exercises 56
9.3 Exam Questions 57

CHAPTER 10 Fun and Games 60


10.1 Key Concepts and How To Teach Them 60
10.1.1 Word Search Puzzle 60
10.1.2 Tic-tac-toe 60
10.2 Solutions To Exercises 60
10.3 Exam Questions 61

CHAPTER 11 Stacks and Compilers 63


11.1 Key Concepts and How To Teach Them 63
11.1.1 Balanced Symbol Checker 63
11.1.2 Infix to Postfix Conversion 63
11.2 Solutions To Exercises 63
11.3 Exam Questions 65

CHAPTER 12 Utilities 67
12.1 Key Concepts and How To Teach Them 67
12.1.1 Huffman’s Algorithm 67
12.1.2 Cross-reference Generator 67
12.2 Solutions To Exercises 67
12.3 Exam Questions 68
vi

CHAPTER 13 Simulation 70
13.1 Key Concepts and How To Teach Them 70
13.1.1 Josephus Problem 70
13.1.2 Discrete-event Simulation 70
13.2 Solutions To Exercises 70
13.3 Exam Questions 71

CHAPTER 14 Graphs and Paths 73


14.1 Key Concepts and How To Teach Them 73
14.1.1 Definitions and Implementation 73
14.1.2 Unweighted Shortest Paths 73
14.1.3 Positive-Weighted Shortest Paths 74
14.1.4 Negative-Weighted Shortest Paths 74
14.1.5 Acyclic Graphs 74
14.2 Solutions To Exercises 74
14.3 Exam Questions 76

CHAPTER 15 Inner Classes and Implementation of ArrayList 80


15.1 Key Concepts and How To Teach Them 80
15.1.1 Nested Classes 80
15.1.2 Inner Classes 80
15.1.3 AbstractCollection 80
15.1.4 ArrayList 80
15.2 Solutions To Exercises 80
15.3 Exam Questions 82

CHAPTER 16 Stacks and Queues 85


16.1 Key Concepts and How To Teach Them 85
16.1.1 Array-based Stack 85
16.1.2 Array-based Queue 85
16.1.3 Linked list-based Stack 85
16.1.4 Linked list-based Queue 85
16.1.5 Double-ended Queue 85
16.2 Solutions To Exercises 85
16.3 Exam Questions 87

CHAPTER 17 Linked Lists 90


17.1 Key Concepts and How To Teach Them 90
17.1.1 Basic Ideas: Header Nodes and Iterator
Classes 90
17.1.2 Implementation Details 90
17.1.3 Doubly Linked Lists and Circular Linked
Lists 90
17.1.4 Sorted Linked Lists 90
17.1.5 Collections API LinkedList implementation 90
vii

17.2 Solutions To Exercises 91


17.3 Exam Questions 92

CHAPTER 18 Trees 95
18.1 Key Concepts and How To Teach Them 95
18.1.1 General Trees and Recursion 95
18.1.2 Binary Trees and Recursion 95
18.1.3 Tree Traversal 95
18.2 Solutions To Exercises 95
18.3 Exam Questions 97

CHAPTER 19 Binary Search Trees 101


19.1 Key Concepts and How To Teach Them 101
19.1.1 The Basic Binary Search Tree 101
19.1.2 Order Statistics 101
19.1.3 AVL Trees, Red-black Trees, and AA-trees 101
19.1.4 B-trees 102
19.2 Solutions To Exercises 102
19.3 Exam Questions 104

CHAPTER 20 Hash Tables 108


20.1 Key Concepts and How To Teach Them 108
20.1.1 Basic Ideas 108
20.1.2 Hash Function 108
20.1.3 Linear Probing 108
20.1.4 Quadratic Probing 108
20.1.5 Other Implementations 109
20.2 Solutions To Exercises 109
20.3 Exam Questions 110

CHAPTER 21 A Priority Queue: The Binary Heap 113


21.1 Key Concepts and How To Teach Them 113
21.1.1 Binary Heap and Heapsort 113
21.2 Solutions To Exercises 113
21.3 Exam Questions 115

CHAPTER 22 Splay Trees 119


22.1 Key Concepts and How To Teach Them 119
22.2 Solutions To Exercises 119
22.3 Exam Questions 120
viii

CHAPTER 23 Merging Priority Queues 123


23.1 Key Concepts and How To Teach Them 123
23.2 Solutions To Exercises 123
23.3 Exam Questions 124

CHAPTER 24 The Disjoint Set Class 126


24.1 Key Concepts and How To Teach Them 126
24.2 Solutions To Exercises 126
24.3 Exam Questions 128

APPENDIX A Sample Syllabi 131

APPENDIX B Sample Assignments 133


ix

Preface

This Instructor’s Resource Manual provides additional material for instructors to use in conjunction with Data
Structures and Problem Solving Using Java (third edition).
Each chapter in the text has a corresponding chapter in this manual that consists of:
• A section on the important concepts and how to teach them
• Solutions to many of the In Short and In Theory questions, as well as some comments for some of the In
Practice questions
• Multiple choice questions
I have attempted to avoid redundancy. As a result, common errors, which are already listed in the text for each chap-
ter, are not repeated. You should be sure to review these errors with your students.
A minimal set of multiple choice questions is provided. It is easy to generate additional questions from both these
multiple choice questions (for example, replace inorder with postorder, and you have a different question in Chapter
18) and from some of the in-chapter exercises. It is also a simple matter to design true-false questions or fill-in ques-
tions based on what is provided. My own preference is to give three types of questions on an exam: long answer
(write a code fragment...), simulation (show the data structure after the following operations...), and theory ques-
tions. Of course if you have very large sections, grading this might be too time consuming.
As mentioned in the textbook, the source code is available online. I have not included any additional directly com-
pilable code in this supplement.
Appendix A provides three syllabi: Two for the separation approach, and one for the traditional approach. Nine
assignments are described in Appendix B. Many many more are suggested as Programming Projects throughout the
text.

Acknowledgement
This IRM was updated by Tim Herman.

E-mail
Please send comments and error reports to weiss@fiu.edu . My home page http://www.cs.fiu.edu/~weiss
will maintain an updated error list and additional notes.
CHAPTER

1 Primitive Java

1.1 Key Concepts and How To Teach Them


This chapter introduces primitive features of Java found in all languages such as Pascal and C:
• basic lexical elements
• primitive types
• basic operators
• control flow
• functions (known as methods in Java)
Students who have had Java already can skip this chapter. I teach the material in the order presented. There is little
tricky material here (this is part of the appeal of Java). Although the text does not mention C or C++, here are some
differences:
1. Primitive types have precise ranges. There is no unsigned type. A char is 16 bits.
2. Order of evaluation is guaranteed (generally left to right). In particular, the sequence point rule from C++
is not needed. Thus nonsense such as x++ + x++ has a precise behavior in Java.
3. Only the C-style type conversion is allowed.
4. boolean is a primitive type, thus removing many of the common errors in C++, such as if(x=y) ... .
5. There is no comma operator, except in for loop expressions.
6. Java provides a labelled break statement.
7. All functions must be class methods.

1.2 Solutions To Exercises


In Short
1.1 Java source files end in .java . Compiled files (containing j-code or byte-codes) end in .class .
1.2 // , which extends to the end of the line and /* and /** , both of which extend to a */ . Comments do
not nest.
1.3 boolean , byte , short , char , int , long , float , and double .
1.4 * multiplies two primitive values, returning the result and not changing its two arguments. *= changes
the left-hand argument to the product of the left-hand argument and the right-hand argument. The
right-hand argument is unchanged.
1.5 Both the prefix and postfix increment operators add one to the target variable. The prefix operator uses
the new value of the variable in a larger expression; the postfix operator uses the prior value.
1.6 The while loop is the most general loop and performs a test at the top of the loop. The body is exe-
cuted zero or more times. The do loop is similar, but the test is performed at the bottom of the loop;
thus the body is executed at least once. The for loop is used primarily for counting-like iteration and
consists of an initialization, test, and update along with the body.
2

1.7 break is used to exit a loop. A labelled break exits the loop that is marked with a label. break is also
used to exit a switch statement, rather than stepping through to the next case.
1.8 The continue statement is used to advance to the next iteration of the loop.
1.9 Method overloading allows the reuse of a method name in the same scope as long as the signatures
(parameter list types) of the methods differ.
1.10 In call-by-value, the actual arguments are copied into the method’s formal parameters. Thus, changes
to the values of the formal parameters do not affect the values of the actual arguments.

In Theory
1.11 After line 1, b is 6, c is 9, and a is 13. After line 2, b is 7, c is 10, and a is 16. After line 3, b is 8, c is
11, and d is 18. After line 4, b is 9, c is 12, and d is 21.
1.12 The result is true . Note that the precedence rules imply that the expression is evaluated as
(true&&false) || true.
1.13 The behavior is different if statements contains a continue statement.
1.14 Because of call-by-value, x must be 0 after the call to method f. Thus the only possible output is 0.

In Practice
1.15 An equivalent statement is:

while( true )
statement

1.16 This question is harder than it looks because I/O facilities are limited, making it difficult to align
columns.

public class MultiplicationTable


{
public static void main( String [ ] args )
{
for( int i = 1; i < 10; i++ )
{
for( int j = 1; j < 10; j++ )
{
if( i * j < 10 )
System.out.print( “ “ );
System.out.print( i * j + “ “ );
}
System.out.println( );
}
}
}

1.17 The methods are shown below (without a supporting class); we assume that this is placed in a class
that already provides the max method for two parameters.

public static int max( int a, int b, int c )


{
return max( max( a, b ), c );
}
3

public static int max( int a, int b, int c, int d )


{
return max( max( a, b ), max( c, d ) );
}

1.18 The method is below:

public static boolean isLeap( int year )


{
return year % 4 == 0 &&
( year % 100 != 0 || year % 400 == 0 );
}

1.3 Exam Questions


1.1. Consider the following statements:
int a = 4;
int b = 7;
b *= a;

What are the resulting values of a and b?


a. a is 4, b is 7
b. a is 28, b is 7
c. a is 4, b is 28
d. a is 28, b is 28
e. the statement is illegal
1.2. Consider the following statements:
int a = 4;
int b = 7
int c = ++a + b—;
What is the resulting value of c?
a. 10
b. 11
c. 12
d. 13
e. none of the above
1.3. Consider the following statements:
boolean a = false;
boolean b = true;
boolean c = false;
boolean d
In which of the following is d evaluated?
a. a && d
b. b && d
c. b || d
d. c && d
e. All the operations evaluate d
1.4. Which of the following loop constructs guarantee that the body is executed at least once?
a. do loop
b. for loop
c. while loop
d. two of the constructs
e. all three of the constructs
4

1.5. In the method below, which statement about the possible outputs is most correct?
public static void what( )
{
int x = 5;
f( x );
System.out.println( x );
}
a. 0 is a possible output
b. 5 is the only possible output
c. any positive integer can be output
d. any integer can be output
e. none of the above are true
1.6. If two methods in the same class have the same name, which is true:
a. They must have a different number of parameters
b. They must have different return types
c. They must have different parameter type lists
d. The compiler must generate an error message
e. none of the above
1.7. Consider the following statements:
int a = 5;
int b = 7;
int c, d;
c = (b - a)/2*a;
d = b + 7/a;
What are the resulting values of c
a. c is 0 and d is 2
b. c is 0 and d is 2.4
c. c is 5 and d is 8
d. c is 5 and d is 2
e. none of the above
1.8. Which of the following is true and d?
a. A variable must be declared immediately before its first use.
b. An identifier may start with any letter or any digit.
c. _33a is a valid identifier.
d. both (a) and (c) are true
e. all of the above are true

Answers to Exam Questions


1. C
2. C
3. B
4. A
5. B
6. C
7. C
8. C
CHAPTER

2 References

2.1 Key Concepts and How To Teach Them


This chapter introduces several concepts:
• references
• strings
• arrays
• exceptions
• I/O
Depending on the students’ background, some or even all of this chapter could be skipped, but I recommend at least
a quick review of the chapter in all circumstances. Students who have not had Java should go through the entire
chapter slowly because there are fundamental differences between Java objects and objects in other languages.

2.1.1 References
Explain the general idea of a reference in the context of a pointer. This is important to do because pointers are fun-
damental in most other languages. Then discuss the basic operations. Most important is to explain that objects must
be created via new, what = and == means for references, and parameter passing for reference types. Students that
have used other languages may be confused with the distinction between call-by-reference in a language such as
Pascal, C++, or Ada and call-by-value applied to reference types in Java. Emphasize that the state of the referenced
object can change, but the object being referenced by the actual argument prior to the method call will still be ref-
erenced after the method call.

2.1.2 Strings
Explain the basics of the String , especially that it is not an array of characters. The tricky parts are mixing up ==
and equals , and remembering that the second parameter to subString is the first non-included position.

2.1.3 Arrays
The tricky part about arrays are that typically the array must be created via new and if the array is an array of
Object , then each Object in the array must also be created by new . Also, array copies are shallow.

2.1.4 Dynamic Expansion


Explain why we double instead of simply add an extra position. Discuss Array-List as a type that maintains the
size as well as capacity of an array and automatically increases capacity when needed.

2.1.5 Exceptions
Designing inheritance hierarchies is deferred to Chapter 4 when inheritance is discussed. This section simply dis-
cusses the try , catch , finally blocks, the throw clause and the throws list. Students do not seem to have diffi-
culty with this material.
6

2.1.6 Input and Output


This section gives a brief description of I/O (mostly I), and the StringTokenizer . The I/O uses Java 1.1 constructs.
This accounts for almost all of the updating from Java 1.0. The StringTokenizer is extremely important for sim-
plifying the parsing of input lines. Without it, life will be difficult.

2.2 Solutions To Exercises


In Short
2.1 Reference values (logically) store the address where an object resides; a primitive value stores the
value of a primitive variable. As a result, operations such as == have seemingly different meanings for
reference types and primitive types.
2.2 The basic operations the can be applied to a reference type are assignment via =, comparison via ==
and != , the dot operator, type conversion, and instanceof .
2.3 An array has its size associated with it. An ArrayList has a capacity in addition to size. Adding an
element to an ArrayList will automatically expand the capacity of the array if needed.
2.4 Exceptions are thrown by a method. The exception immediately propagates back through the calling
sequence until it is handled by a matching catch clause, at which point the exception is considered
handled.
2.5 Basic string operations include equals and compareTo to compare, = to copy, + and += to perform
concatenation, length , charAt , and substring .

In Theory
2.6 The second statement outputs 5 7, as expected. The first outputs 44 , because it used the ASCII value
of ‘ ‘, which is 32.

In Practice
2.9 A method to do this is below:

public static boolean isPrefix( String str1, String str2 )


{
if( str1.length( ) > str2.length( ) )
return false;
for( int i = 0; i < str1.length( ); i++ )
if( str1.charAt( i ) != str2.charAt( i ) )
return false;
return true;
}

2.10 public static int getTotalStrLength(String [] theStrings )


{
int total = 0;
for( String s : theStrings )
total += s.length( );

return total;
}
7

2.11 The elements in the original array are not copied before it is reinitialized.

2.12
public static String [ ] split ( String str, String tokens )
{
//use an ArrayList to hold the strings
ArrayList<String> strList = new ArrayList<String>( );

//go through string, looking for next token each time


int start = 0;
int end = str.indexOf( tokens, start );
String s;

while (end > -1){


s= str.substring( start, end );
if ( s.length( ) > 0 )
strList.add( s );
start = end + tokens.length( );
.end = str.indexOf( tokens, start );
}

//add the last token


s = str.substring( start );
if ( s.length( ) > 0 )
......strList.add( s );

//convert the ArrayList to a String array and return it


return ( String[ ] ) strList.toArray( new String[ 0 ] );
}

2.3 Exam Questions


2.1. In the method below, which statement about the possible outputs is most correct?

public static void what( )


{
Integer x = new Integer( 5 );
f( x );
System.out.println( x );
}

a. 0 is a possible output
b. 5 is the only possible output
c. any positive integer can be output
d. any integer can be output
e. none of the above are true
8

2.2. In the method below, which statement about the possible outputs is most correct?

public static void what( )


{
Integer x;
f( x );
System.out.println( x );
}

a. 0 is a possible output
b. 5 is the only possible output
c. any positive integer can be output
d. any integer can be output
e. none of the above are true
2.3. Which class is used to parse a line of input?
a. BufferedReader
b. FileReader
c. InputStreamReader
d. StringTokenizer
e. none of the above
2.4. Which of the following is not true about a String ?
a. Strings are reference types.
b. Individual characters can be accessed.
c. Strings must be created without using new .
d. The contents of a String can be safely copied via =.
e. The length of a String can always be determined.
2.5. Which of the following is not true about arrays?
a. Arrays are reference types.
b. Array indexing is bounds-checked.
c. Arrays sometimes can be created without using new .
d. The entire contents of an array can be copied via =.
e. The capacity of an array can always be determined.
2.6. Which of the following is true about reference types?
a. They are initialized to 0 by default.
b. = can be used to copy the states of two objects.
c. == can be used to test if the referenced objects have identical states.
d. They are passed using call-by-reference.
e. all of the above are false
2.7. Which of (a) - (d) is false:
a. A catch block for a standard run-time exception is optional.
b. An exception is an object
c. A throws clause is used to throw an exception.
d. An exception handler is inside a catch block.
e. all of the above are true
2.8. Which of the following is false about type ArrayList :
a. The add operation always increase size by 1.
b. Only objects can be stored in an ArrayList
c. An add operation may increase the capacity by more than one.
d. The capacity of an ArrayList can be changed using the set method.
e. none of the above is false
Answers to Exam Questions
1. B
2. E
3. D
4. C
5. D
6. E
7. C
8. D
10

CHAPTER

3 Objects and Classes

3.1 Key Concepts and How To Teach Them


Students who have not had Java, with a description of class design, will need to go through this chapter in its
entirety. Students who have had Java with class design may want to quickly review the chapter.
This chapter introduces the general concept of encapsulation and information hiding, but is geared towards practi-
cal use of Java with emphasis on designing classes and syntax. You may want to have the students bring a copy of
the code to class so you can avoid rewriting the classes. This chapter is by far much simpler than its C++ counter-
part. Topics include:
• the class construct
• public and private sections
• specification (javadoc) vs. implementation
• constructors
• accessors and mutators
• toString
• equals
• packages
• this

• instanceof operator
• static class members

3.1.1 The class Construct


Describe how the class achieves the grouping of data members and the information hiding and encapsulation of
functionality. The basic mechanism for the latter is to allow methods to be class members. You can illustrate this
with Memory-Cell . Add the private and public visibility modifiers after discussing it.

3.1.2 Public and Private Sections


This seems to be a relatively easy topic. Explain that everything in the private section is inaccessible to non-class
routines. Continue with the MemoryCell example.

3.1.3 Specification vs. Implementation


Explain the importance of the specification, and how some of it can be generated automatically via javadoc.

3.1.4 Constructors
Explain that in addition to particular member functions, every class has constructors. Show how a declaration is
matched by a constructor. Remark about the default behavior: If no constructor is provided, a default zero-parame-
ter constructor is generated.

3.1.5 Accessors and Mutators


11

Java has no formal way to specify an accessor or a mutator. However the concept is important to teach.

3.1.6 toString
This is a simple topic to cover.

3.1.7 equals
The tricky part is that the parameter is always of type Object and must be converted to the class type. Mention that
if equals is not implemented, then it typically defaults to returning false (actually it is inherited from its super-
class).

3.1.8 Packages
Mention why we use packages and the special visibility rules. Also discuss the import directive briefly. This is a
reasonable time to discuss compilation issues and the CLASSPATH variable.

3.1.9 this
this is used in two main places: aliasing tests (in which it is important to have a reference to the current object),
and as a shorthand for calling other constructors. Both uses are easy to discuss.

3.1.10 instanceof Operator


This is used mostly in the equals method. Note that the null reference is not an instance of any class.

3.1.11 static Class Members


Explain that a static field is unique to a class and does not belong to any instance of that class. They can be refer-
enced via class names or object references.

3.1.12 Design patterns


Just mention that design patterns are used to encode commonly occurring problems and their solutions, and that we
will be identifying design patterns in the course.

3.2 Solutions To Exercises


In Short
3.1 Information hiding makes implementation details, including components of an object, inaccessible.
Encapsulation is the grouping of data and the operations that apply to them to form an aggregate
while hiding the implementation of the aggregate. Encapsulation and information hiding are achieved
in Java through the use of the class.
3.2 Members in the public section of a class are visible to non-class routines and can be accessed via the
dot member operator. Private members are not visible outside of the class.
3.3 The constructor is called when an object is created by a call to new .
3.4 The default constructor is a member-by-member application of a default constructor, meaning that
primitive members are initialized to zero and reference members are initialized to null .
3.5 this is a reference to the current object. This reference to the current object can be used to compare it
with other objects or to pass the current object to some other unit.
3.6 Packages are used to organize similar classes. Members with no visibility modifier are package visi-
ble: that is, they are visible to other classes within the same package (it is not visible outside the pack-
age).
3.7 Output is typically performed by providing a toString method that generates a String . This String
12

can be passed to println .


3.8 The two import directives are below:
import weiss.nonstandard.*;
import weiss.nonstandard.Exiting;

3.9 A design pattern describes a commonly occurring problem in many contexts and a generic solution
that can be applied in a wide variety of these contexts.
3.10 (a) Line 17 is illegal because p is a non-static field and therefore needs an object reference in order to
be reference in the main method (which is a static method). Line 18 is legal as q is created within the
main method. (b) Line 20 and 23 are legal as NO_SSN is a public static field which can be accessed via
an object reference or a class name. Line 22 is legal because by default, name is visible. Line 21 and
24 are illegal because SSN is private. Also, Person.SSN is illegal as SSN is nonstatic.

In Theory
3.11 By defining a single private constructor for a class A, we can disallow any other object to create an
instance of A. For example, we may use A to hold only static data.
3.12 (a) Yes; main works anywhere. (b) Yes; if main was part of class Int-Cell , then storedValue
would no longer be considered private to main .
3.13 public class Combolock {

private int num1;


private int num2;
private int num3;

public Combolock ( int a, int b, int c){


num1 = a;
num2 = b;
num3 = c;
}

//returns true if the proper combination is given


public boolean open ( int a, int b, int c){
return ( ( a == num1 ) && ( b == num2 ) && (c == num3 ) );
}
public boolean changeCombo( int a, int b, int c, int newA, int newB, int
newC ){
if ( open( a, b, c) ){
... num1 = newA;
... num2 = newB;
... num3 = newC;
... return true;
}
return false;
}

In Practice
3.14 The suprising result in part (d) is that the compiler will find the bytecodes for the local List class, even
though you have recommented it. And thus, no ambiguity will be reported. Even with the import
directive, the local list class will win out over java.awt.List .
3.3 Exam Questions
3.1. Which of the following is the most direct example of how encapsulation is supported in Java?
a. constructors
b. inheritance
c. methods
d. public and private specifiers
e. the class declaration
3.2. Which of the following is the most direct example of how information hiding is supported in Java?
a. constructors
b. inheritance
c. member functions
d. public and private specifiers
e. the class declaration
3.3. What happens if a non-class method attempts to access a private member?
a. compile time error
b. compile time warning, but program compiles
c. the program compiles but the results are undefined
d. the program is certain to crash
e. some of the above, but the result varies from system to system
3.4. Which condition occurs when the same object appears as both an input and output parameter?
a. aliasing
b. copy construction
c. operator overloading
d. type conversion
e. none of the above
3.5. Which of (a) to (c) is false about a static class member?
a. it must be a method
b. one member is allocated for each declared class object
c. the static class member is guaranteed to be private to the class
d. two of the above are false
e. all of (a), (b), and (c) are false
3.6. In which of the following cases is a class member M invisible to a method F?
a. F is a method in the same class and M is private
b. F is a package friendly function and M is not private
c. F is a method in another class and M is public
d. F is a method in another class and M is private
e. none of the above
3.7. Which of the following statements is true?
a. Any documentation produced by javadoc is guaranteed to be implemented by the class.
b. Java provides a mechanism to distinguish accessors and mutators.
c. Each class can provide a main to perform testing.
d. this is available in all methods, including static methods.
e. Every class must implement toString and equals .
3.8. For class C, which are the parameter types of equals ?
a. no parameters.
b. one parameter, type C.
c. one parameter, type Object .
d. two parameters, both of type C.
e. two parameters, both of type Object .
14

3.9. Which of (a) - (d) is false for the main method?


a. Each class can have its own main method.
b. The arguments passed to a main method from a command line are strings.
c. The main method must be written as the last method in the class.
d. The main method typically does not return a value
e. all of the above are true
3.10. Which of the following is false for constructors?
a. There may not be any constructor defined in a class.
b. A constructor must be declared public.
c. There may be multiple constructors in a class.
d. A constructor typically has no return value.
e. A constructor must have the same name as the class name.

Answers to Exam Questions


1. E
2. D
3. A
4. A
5. E
6. D
7. C
8. C
9. C
10. B
15

CHAPTER

4 Inheritance

4.1 Key Concepts and How To Teach Them


Inheritance in Java is a fundamental concept. The text does not directly define large hierarchies, but repeatedly uses
inheritance in two places: the interface and in designing generic algorithms.
My recommendation is that other uses of inheritance should be occasional. The basic concepts are:
• The concept of inheritance and polymorphism
• Extending classes
• Designing hierarchies
• Abstract and final methods and classes; Static vs. dynamic binding
• Interfaces
• Generic components
• Adapters and wrappers

4.1.1 The Concept of Inheritance and Polymorphism


Inheritance is used when new classes are needed that have basic similarity to existing classes. Several examples are
provided in the text. Use the Person example to explain the IS-A relation. Explain advantages of inheritance such as
code reuse.

4.1.2 Extending Classes


I like to explain that the derived class is equal to the base class with possible additions (new data members, new
methods) and modifications (redefinition of base class members).
Explain that the derived class has all of the data members of the base class, plus its own. The inherited members are
not directly accessible if they were private in the base class. Also explain that for the purposes of construction, the
data members that form the base class are considered as an atomic object in the call to super (and if they are pri-
vate, that is the only way to initialize the inherited portion of the class). Use very short examples, such as
Underflow , initially.
Public, private, and protected must be explained. This tends to not be a difficult concept.

4.1.3 Dynamic binding and polymorphism


A reference variable that is polymorphic can reference objects of several different types. When operations are
applied to the reference, the operation that is appropriate to the actual referenced object is automatically selected
(also referred to as dynamic binding). This concept is useful when derived classes may override base class methods.
Explain the concept using the Shape example.

4.1.4 Designing hierarchies


Using the Shape example, explain the need for abstract methods, final methods and static methods. Similarly,
explain the use of abstract classes and final classes. Abstract classes are placeholders and cannot be constructed. The
derived classes must provide implementations for the abstract class methods. Final methods are those that cannot
be overridden.
16

4.1.5 Interfaces
Interfaces are simply abstract classes with no implementation. In other words, they specify a protocol. They are syn-
tactically simple and students do not seem to have trouble with the concept.

4.1.6 Inheritance in Java


Briefly explain the Object class and that all other class is a direct or indirect subclass of Object . The Exception
and I/O hierarchies in the Java libraries are good examples to illustrate inheritance. Without going into too much
detail (as some of these concepts are covered in later chapters), explain the exception hierarchy and I/O decorator
pattern example for wrapping streams and readers to illustrate the I/O hierarchy.

4.1.7 Generic Components


A generic implementation gives the basic functionality in a type independent manner. The main idea is to use inher-
itance and write components in terms of the most general type of object that will be used (this component can then
be reused in all derived classes). This can be accomplished by using an appropriate superclass such as
java.lang.Object. Use the MemoryCell example to illustrate a generic cell class. Interface types may sometimes be
needed to define more specific operations than those available in the Object class. The Comparable interface can
be used to write a generic findMax method. Java 1.5 has simplified the task of writing generic components with the
introduction of language support for generic classes and methods. The generic mechanism in Java 1.5 is driven
entirely by the compiler – essentially converting 1.5 generic code into a pre-1.5 inheritance-based generic imple-
mentation. For this reason, it is essential to understand inheritance based generic coding, even when working with
Java 1.5.

4.1.8 Wrappers and Adapters


A Wrapper stores an object and adds operations, where as an adapter simply changes the interface without chang-
ing the functionality. Java 1.5 introduced new autoboxing and unboxing features that perform behind the scenes
wrapping/unwrapping of primitive types with their corresponding class-based implementations.

4.1.9 Functor
A functor is typically a class with a single method that is used in a generic algorithm. Use the findMax example to
illustrate the concept. Note that the algorithm typically specifies an interface class and the actual function object
class must implement this interface.

4.1.10 Nested, anonymous and local classes


A nested class declaration is placed inside another class declaration. A local class is declared inside a method.
Finally, an anonymous class is also declared inside a method but is declared immediately where it is used. Give a
brief explanation using the findMax example. Students will get comfortable with them when they actually use them
later in the course

4.2 Solutions To Exercises


In Short
4.1 Assuming public inheritance, only public and protected members of the base class are visible in the
derived class unless the derived class is in the same package of the base class, in which case package-
friendly members are also visible. Among base class members, only public members of the base class
are visible to users of the derived class.
4.2 Private inheritance is used to indicate a HAS-A relationship while avoiding the overhead of a layer of
function calls. Composition is a better alternative for this. In composition, a class is composed of
objects of other classes.
4.3 Polymorphism is the ability of a reference type to reference objects of several different types. When
operations are applied to the polymorphic type, the operation that is appropriate to the actual refer-
enced object is automatically selected.
17

4.4 Autoboxing and unboxing are new features introduced in Java 1.5. The perform automatic conversion
between primitive types and their object counterparts (e.g. int and java.lang.Integer).
4.5 A final method is a method that cannot be redefined in a derive class.
4.6 (a) Only b.bPublic and d.dPublic are legal. (b) if main is in Base , then only d.dPrivate is illegal.
(c) If main is in Derived , then b.bPrivate is illegal. (d) Assuming Tester is in the same package,
then b.bProtect is visible in all cases. (e) place the statements below in the public section of their
respective classes; (f) and (g) bPrivate is the only member not accessible in Derived.

Base( int pub, int pri, int pro )


{
bPublic = pub; bPrivate = pri; bProtect = pro;
}

Derived( int bpub, int bpri, int bpro, int dpub, int dpri )
{
super( bpub, bpri, bpro );
dPublic = dpub; dPrivate = dpri;
}

4.8 An abstract method has no meaningful definition. As a result, each derived class must redefine it to
provide a definition. A base class that contains an abstract method is abstract and objects of the class
cannot be defined.
4.9 An interface is a class that contains a protocol but no implementation. As such it consists exclusively
of public abstract methods and public static final fields. This is different from abstract classes, which
may have partial implementations.
4.10 The Java I/O library classes can be divided into two groups, input and output. The I/O is done via
input and output streams. Byte oriented reading and writing is provided via InputStream and
OutputStream classes whereas character-oriented I/O is provided via Reader and Writer classes.
Most other classes for I/O (for files, pipes, sockets etc) are derived from these four classes.

Object
Reader
InputstreamReader
BufferedReader
FileReader
Writer
Printwriter
InputStream
FileInputStream
SocketInputStream
DataInputStream
ObjectInputStream
OutputStream
FileOutputStream
DataOutputStream
ObjectOutputStream
GZIPOutputStream
SocketOutputStream

4.11 Generic algorithms are implemented by using Object or some other interface (such as Comparable )
as parameters and return types. In other words, generic algorithms are implemented by using inheri-
tance. To store primitive types in a generic container, wrapper classes must be used.
18

4.12 A wrapper pattern is used to store an existing entity (e.g., a class) and add operations that the original
type does not support. An adapter pattern is used to change the interface of an existing class to con-
form to another interface. Unlike the wrapper pattern, the aim of the adapter pattern is not to change or
add functionality.
4.13 One way to implement an adaptor is via composition wherein a new class is defined that creates an
instance of the original class and redefines the interface. The other method is via inheritance.
Inheritance adds new methods and may leave the original methods intact whereas via composition, we
create a class that implements just the new interface. A function object is implemented as a class with
no data object and one method.
4.14 A local class is a class that is placed inside a method. The local class is visible only inside the
method.An anonymous class is a class with no name. It is often used to implement function objects
(the syntax allows writing new Interface() with the implementation of Interface immediately fol-
lowing it).
4.15 Type erasure is the process of converting a generic class to a non-generic implementation. Due to type
erasure, generic types cannot be primitive types. Simirlarly, instanceof tests cannot be used with
generic types, nor can a generic type be instantiated or referenced in a static context. Arrays of
generic types cannot be created.
In Theory
4.17 Consider the case in which a method foo() contains a local class and returns an object of type
LocalClass . In this case, if the local class accesses x, then it continues to be available after foo termi-
nates.

Typetest foo ()
{
final int x = 1;

class LocalClass() implements Typetest


{
public int foo()
{ return x; }
}
return new localClass();
}

4.18 The output is 512 and the signature is String getX(). After changing as given in (c), the output is 17
and the signature is int getX(). If Class1 is changed as in (d), we get an error as Class2 is expect-
ing a method getX returning an integer. If code inlining was allowed, the result would have been 17.
4.19 a. The last line throws a ClassCastException as the element in the array must be specifically cast, not
the array itself. b. The last line contains an unnecessary cast. c. No errors. d. No errors.
4.20
,, public static <AnyType> void copy( AnyType [ ] arr1, AnyType [ ] arr2 ){
for ( AnyType val : arr1 )
arr2[2] = val;
}
19

In Practice
4.21 The class below includes the generic method min and a test routine. max is similar.

public class Ex0420


{
public static Comparable min( Comparable a, Comparable b )
{
return a.lessThan( b ) ? a : b;
}

public static void main( String [ ] args )


{
int x = 5, y = 7;

System.out.println( min( new MyInteger( x ), new MyInteger( y ) ) );


}
}
4.22
public static Comparable min( Comparable [] a )
{
int minIndex = 0;
for ( int i = 1; i < a.length; i++ )
if ( a[minIndex].compareTo(a[i]) > 0 )
minIndex = i;
return a[minIndex];
}
4.23
class MaxTwo
{
public static Comparable [] max2( Comparable [] a )
{
Comparable [] obj1 = new Comparable [2];
int maxIndex0 = 0;
int maxIndex1 = 0;

if( a[0].compareTo(a[1]) > 0) { maxIndex0 = 1; }


else { maxIndex1 = 1; }

for( int i = 1; i < a.length; i++ )


{
if( a[maxIndex0].compareTo(a[i]) > 0 )
{
maxIndex1 = maxIndex0;
maxIndex0 = i;
}
}
obj1[0] = a[maxIndex0];
obj1[1] = a[maxIndex1];
return obj1;
}
20

public static void main( String [] args )


{
String [] st1 = { “B”, “C”, “A”, “F” };
Comparable [] st2 = new Comparable[2];
st2 = max2(st1);
System.out.println(st2[0] + “ “ + st2[1]);
}
}

4.24
class SortWithMin
{
public static int findmin( Comparable [] a, int start )
{
int minIndex = start;

for( int i = start; i < a.length; i++ )


if( a[minIndex].compareTo(a[i]) > 0 )
minIndex = i;
return minIndex;
}
public static void sort (Comparable [] a)
{
int tempIndex;
Comparable temp;

for (int i = 0; i < a.length; i++)


{
tempIndex = findmin(a,i);
temp = a[i];
a[i] = a[tempIndex];
a[tempIndex] = temp;
}
}

public static void main (String [] args)


{
String [] st1 = { “B”, “C”, “A”, “F” };
sort(st1);

for( int i = 0; i < st1.length; i++ )


System.out.println(st1[i]);
Shape [] a = new Shape[ ]
{ new Circle( 2.0 ), new Circle( 1.0), new Circle( 3.0 )
};
sort(a);
for( int i = 0; i < a.length; i++ )
System.out.println(a[i].area());
}
21

}
This assumes that Shape implements Comparable (Figure 4.16).
4.25
public class Circle extends Shape
{
public Circle( double rad )
{
if( rad >= 0 )
radius = rad;
else
throw new InvalidArgumentException();
}

private double radius;


}

public class Square extends Rectangle


{
public Square( double side )
{
super( side, side );
}

public String toString( )


{
return “Square: “ + getLength( );
}
}

public class Rectangle extends Shape


{
public Rectangle( double len, double wid )
{
if( (len >= 0) && (wid >= 0) )
{
length = len; width = wid;
}
else
throw new InvalidArgumentException();
}

private double length;


private double width;
}

public class InvalidArgumentException


extends RuntimeException
{
}
22

4.26
class Person implements Comparable
{
public int compareTo(Object rhs)
{
Person p2 = (Person) rhs;
return (name.compareTo(p2.name));
}

// Class is identical, with changes in bold.


}
4.27

public class SingleBuffer <AnyType> {


private AnyType theItem;
private boolean isEmpty;

public SingleBuffer( ){
theItem = null;
isEmpty = true;
}
public SingleBuffer( AnyType item ){
theItem = item;
if ( theItem != null ){
isEmpty = false;
} else {
isEmpty = true;
}
}
public AnyType get( ) throws SingleBufferException {
if ( isEmpty ){
throw new SingleBufferException( "Buffer is empty!" );
}
isEmpty = true;
AnyType item = theItem;
theItem = null;
return item;
}
public void put( AnyType item ) throws SingleBufferException {
if ( !isEmpty ){
throw new SingleBufferException( "Buffer must be emptied by
insertion!" );
}
theItem = item;
isEmpty = false;
}
}

public class SingleBufferException extends Exception {

public SingleBufferException( String msg ){


super( msg );
}
23

4.28 Here is an even fancier version that uses iterators, inner classes, nested classes, and comparators. Of
course it has way too many forward references for use at this point in the course, but you can remove
complications as needed.

import java.util.*;
/**
* A SortedArrayList stores objects in sorted order.
* The SortedArrayList supports the add operation.
* size is also provided, and so is a method
* that returns an Iterator.
* Of course a get routine could be provided too
* as could numerous other routines..
*
* This example illustrates both instance inner classes
* and static inner classes.
* An instance inner class is used in the typical iterator pattern.
* A static inner class is used to define a default comparator.
*/
class SortedArrayList
{
private ArrayList data = new ArrayList( ); // The list, in sorted order
private Comparator cmp; // The comparator object

/**
* Construct the SortedArrayList with specified Comparator.
* @param compare The Comparator object.
*/
public SortedArrayList( Comparator compare )
{
cmp = compare;
}

/**
* Construct the SortedArrayList using natural ordering
* If objects are not Comparable, an exception will be
* thrown during an add operation.
*/
public SortedArrayList( )
{
this( new DefaultComparator( ) );
}

private static class DefaultComparator implements Comparator


{
public int compare( Object obj1, Object obj2 )
{
return ((Comparable) obj1).compareTo( obj2 );
}
}
24

/**
* Add a new value to this SortedArrayList, maintaining sorted order.
* @param x The Object to add.
*/
public void add( Object x )
{
data.add( x ); // add at the end for now
int i = data.size( ) - 1;
// Slide x over to correct position
for( ; i > 0 && cmp.compare( data.get( i - 1 ), x ) > 0; i— )
data.set( i, data.get( i - 1 ) );
data.set( i, x );
}

/**
* Return the number of items in this SortedArrayList.
* @return the number of items in this SortedArrayList.
*/
public int size( )
{
return data.size( );
}

/**
* Return an Iterator that can be used to traverse
* this SortedArrayList. The remove operation is unimplemented.
* @return An Iterator that can be used to traverse this SortedArrayList.
*/
public Iterator iterator( )
{
return new SortedIterator( );
}

private class SortedIterator implements Iterator


{
private int current = 0;

public boolean hasNext( )


{
return current < size( );
}

public Object next( )


{
return data.get( current++ );
}

public void remove( )


{
throw new UnsupportedOperationException( );
}
}
}
25

class TestSortedArrayList
{
public static String listToString( SortedArrayList list )
{
Iterator itr = list.iterator( );
StringBuffer sb = new StringBuffer( );

for( int i = 0; itr.hasNext( ); i++ )


sb.append( “[“ + i + “]” + itr.next( ) + “ “ );
return new String( sb );
}
// Test by inserting 20 Strings
public static void main( String[] args )
{
SortedArrayList list = new SortedArrayList( );

for( int i = 0; i < 20; i++ )


list.add( “” + i );
System.out.println( “Using iterator” );
System.out.println( listToString( list ) );
}
}

4.29
public interface Matchable
{
boolean matches(int a);
}

class EqualsZero implements EqZeroFn


{
public boolean matches(int a)
{
return a == 0;
}
}

class P428
{
public static int countMatches( int [] a, Matchable func )
{
int num = 0;
for( int i = 0; i < a.length; i++ ) if( func.matches(a[i])
)
num++;

return num;
}

public static void main (String [] args)


{
int [] a = { 4, 0, 5, 0, 0};
System.out.println(countMatches(a, new EqualsZero()));
26

}
}

4.30
class EqualsK implements Matchable
{
private int k;

public EqualsK( int initialk )


{ k = initialk; }

public EqualsK()
{ this( 0 ); }

public boolean matches(int a)


{ return a == k; }
}

class P429
{
public static void main (String [] args)
{
int [] a = { 4, 0, 5, 0, 0};
System.out.println( P428.countMatches( a, new EqualsK(5) ) );
}
}

4.3 Exam Questions


4.1. Which term describes the ability of a reference type to reference objects of several different types?
a. composition
b. encapsulation
c. information hiding
d. polymorphism
e. static binding
4.2. In the derived class, which data members of the base class are visible?
a. those in the public section only
b. those in the protected section only
c. those in the public or protected sections
d. those in the public or private sections
e. all data members are visible
4.3. For which methods is dynamic binding used?
a. all class methods
b. all static methods
c. final methods
d. non-static, non-final class methods
e. none of the above
4.4. Which of the following is true about an abstract base class?
a. no constructors should be provided
b. at least one member function should be abstract
c. no objects of the class can be created
d. exactly two of the above
e. all of (a), (b), and (c)
27

4.5. When is a method abstract?


a. When it is constant throughout the inheritance hierarchy
b. Its definition changes in the inheritance hierarchy, but there is a reasonable default
c. No reasonable default can be provided, and it must be defined in the inheritance hierarchy
d. Always
e. none of the above
4.6. Which of the following are not allowed in an interface?
a. public methods
b. static methods
c. final methods
d. public fields
e. static fields
4.7. How is generic programming implemented in Java 1.5?
a. by inheritance
b. import directive
c. package statement
d. private methods
e. using a template
4.8. Which clause is used to derive a new class?
a. extends
b. implements
c. import
d. throw
e. at least two of the above
4.9. Which of the following is false?
a. An abstract class may implement an interface.
b. A class that contains an abstract method must be declared abstract itself or a compiler message
will result.
c. A class may implement multiple interfaces.
d. A class that extends another class may implement only one interface.
e. An interface may extend another interface.
4.10. Which of (a) - (c) is false?
a. An adapter class is used to change the interface of an existing class.
b. A wrapper class stores a primitive types and add operations that primitive type does not support.
c. An adapter is used to provide a simpler interface with fewer methods
d. Both (a) and (c) are false
e. all of the above are false
4.11. Which of (a) - (d) is false?
a. A nested class is a member of its outer class.
b. A nested class must be declared static.
c. Methods of a local class can access local variables of its outer class.
d. A local class is visible only inside the method in which it is declared.
e. Both (b) and (c) are false
28

Answers to Exam Questions


1. D
2. C
3. D
4. D
5. C
6. C
7. A
8. E
9. D
10. B
11. C
29

CHAPTER

5 Algorithm Analysis

5.1 Key Concepts and How To Teach Them


This chapter is organized exactly as I teach it:
• What is algorithm analysis
• Some examples
• The maximum contiguous subsequence sum problem
• Official Big-Oh rules
• Logarithms

5.1.1 What is Algorithm Analysis


I explain the basic idea that more data means more running time, and that we can graph the running time. I then like
to draw different curves, label them as quadratic, linear, etc., and then remark that some curves are better than oth-
ers. The questions of the chapter become:
1. How do you know which curve your algorithm is on?
2. How do you design algorithms to be on the best curve?
3. How can you predict the amount of time a program will take?

5.1.2 Some Examples


Reading a file is a classic linear-time algorithm. But reading the first or last few characters of a file (as in the Unix
tail command) is not. Some other examples are in the text, and those examples include a small amount of combi-
natorics that can be skipped.

5.1.3 The Maximum Contiguous Subsequence Sum Problem


Students love this problem. I write the cubic algorithm on the board, and do the inside out analysis that there are at
most N 3 iterations. I mention that this is a factor of 6 too high, but do not prove it. If you have students of appro-
priate background, you can try a proof. I then fill in the running time in Figure 5.10 for this algorithm, and do the
10,000, 100,000, and 1,000,000 element cases. I explain how a cubic program takes 1,000 times as long for 10 times
as much data.
Next I show how we can get rid of a loop. Students see that the running time is quadratic. I then fill in the next line
in the table, starting with the base 10 element case, and doing the calculation. These two usually take almost the
whole class. I remark that the linear time algorithm would take 3 seconds for 1,000,000 elements, versus the 14,000
years for the cubic algorithm.
If the students have already had recursion, you can skip to Chapter 7 and do the N log N algorithm. I also do the lin-
ear algorithm and give a proof sketch, but I think the importance is not the proof itself, but rather that the algorithm
is not as obvious but is still much faster, which is typical.
30

5.1.4 Official Big-Oh Rules


You can sketch the Big-Oh rules, but many students will have a good feel for it at this point. Stress that nested loops
multiply but consecutive loops do not. It is also good to bring in the perspective that there will typically be only one
or two parts of the code that are the bottleneck and worth optimizing.

5.1.5 Logarithms
I spend a fair amount of time discussing logarithms — do not underestimate how difficult this topic is for many stu-
dents. I use binary search as the application that everyone seems to understand. The algorithm is somewhat like the
Price Is Right hi-lo game in which almost nobody ever loses. I rarely discuss interpolation search.

5.2 Solutions To Exercises


In Short
5.1 (a) 5, 5, 6; (b) 5, 6, 6; (c) 3, 3, 3; (d) 5, 5, 6.
5.2 Theorem 5.2 does not eliminate enough subsequences. If all the numbers are positive, for example, the
theorem is not used at all by the algorithm.
5.3 (a) is true; (b) is true; (c) is false (example: T1 ( N ) = N and T2 ( N ) = 1 ); (d) is false (same exam-
ple).
5.4 All except x belong to the same group.
5.5 (a) Program A’s guarantee is ten times better than B’s. (b) Program B’s guarantee is ten times better
than A’s. (c) the stated properties do not tell us anything about the average-case performance. (d) It is
possible that program B will run faster on all inputs; the guarantee for B may be too pessimistic.
5.6 (a) The search could fail because the loop is exited even when a sub-array of one exists but has not
been tested. (In fact, half the successful searches will be reported as unsuccessful); (b) mid will not be
in the center, and may not even be in the range low to high ; (c) Subarrays of size two which reach
line 16 will not be reduced to a smaller subarray, resulting in an infinite loop; (d) Same problem as in
part (c).

In Theory
5.7 (a) O( N ); (b) O( N 2); (c) O( N 2).
5.8 The running time is O( N ), assuming that each multiplication is unit cost. This assumption is some-
what unjustified since it is clear that the numbers are getting bigger at each step. But at this level,
deeper calculations are not needed. If we take the size of x as a constant, then the ith multiplication
costs O( i ), and the total running time is quadratic.
5.9 We need to calculate: . This is

5.10 N(N + 1 )/2 times. The calculation is similar to, but simpler than, that in the previous exercise.
31

5.11 About (a) 2.5 milliseconds (5 times longer); (b) 3.5 milliseconds (about 7 times longer
5 * log(500)/log(100)); (c) 12.5 milliseconds (25 times longer); (d) 62.5 milliseconds (125 times
longer).
5.12 In this problem, we are given 120,000 times as much time.
(a) For a linear-time algorithm, we can solve a problem 120,000 times as large, or 12,000,000 (assum-
ing sufficient resources);
(b) For an Nlog N algorithm it is a little less (about 4,000,000 or so);
(c) For a quadratic algorithm, we can solve a problem = 346 times as large, so we can solve
a problem of size 34,600;
(d) For a cubic algorithm, we can solve a problem = 49 times as large, so we can solve an
instance of size 4,900.
5.13 The cubic algorithm would take about 38 minutes for N = 10,000, 26 days for N = 100,000, and 72
centuries for N = 10,000,000. The quadratic algorithm would take 1.23 seconds for N = 10,000, about
2.25 minutes for N = 100,000 and about 16 days for N = 10,000,000. The N log N algorithm would
use about 42 seconds for N = 10,000,000. These calculations assume a machine with enough memory
to hold the array. The linear algorithm solves a problem of size 10,000,000 in about 0.32 seconds.
5.14 2 /N, 37, , N log log N, N log N, N log( N 2), N log2N, N1.5, N 2, N 2 log N, N 3, 2N/2, 2N. N log N and
2
N log( N ) grow at the same rate.
5.15 The analysis below will agree with the simulation in all cases. Fragment 1: The running time is O( N ).
Fragment 2: The running time is O( N 2 ) because of two nested loops of size N each. Fragment 3: The
loops are consecutive, so the running time is O( N ). Fragment 4: The inner loop is of size N 2 so the
running time is O( N 3 ). Fragment 5: The running time is O( N 2 ). Fragment 6: Here we have three
nested loops of size N, N 2, and N 2, respectively, so the total running time is O( N 5 ).
5.16 Here the if statement is executed at most N 3 times, but it is only true O( N 2 ) times (because it is true
exactly i times for each i). Thus the innermost loop is only executed O( N 2 ) times. Each time through
it takes O( N 2 ) time, for a total of O( N 4 ).
5.17 (a) 22N–1; (b) 1 + È log log D˘.
5.19 I suppose the simplest example is an array of one item. What I meant by this question was to construct
an example where almost every element is examined asymptotically. A sequence of N items in which
the first is 1, the others are consecutive integers ending in N 2 is an example of a bad case. For
instance, with N = 10, the sequence 1, 92, 93, 94, 95, 96, 97, 98, 99, 100 with search for 91 is bad and
results in half the array being searched (without the technical adjustment we get almost the entire
array searched). By using larger numbers, we can have even more entries searched.
5.20 Suppose for simplicity that the number of elements N is one less than a power of 2, that is, N = 2k – 1 .
Then we see that there are exactly 2i–1 items whose successful search requires exactly i element
accesses. Thus the k average cost is

.
This evaluates to log (N + 1 ) – 1 + (log ( N + 1 ) ) / N, which tells us that the cost of an average suc-
cessful search is only about 1 less than the worst case successful search.

In Practice
5.23 Note that we require integers; use a variation of binary search to get a logarithmic solution (assuming
that the array is preread).
5.24 (a) This is trial division; see Chapter 9 for a sample routine. The running time is ; (b) B = O
(log N); (c) O (2B/2); (d) If a twenty-bit number can be tested in time T, then a forty-bit number would
require about T2 time.
5.26 First, a candidate majority element is found (this is the harder part). This candidate is the only element
that could possibly be the majority element. The second step determines if this candidate is actually
the majority element and is just a sequential search through the array. First, copy the array to an array
A. To find a candidate in the array A, form a second array B. Then compare A[1] and A[2]. If they are
equal, add one of these to B; otherwise do nothing. Then compare A[3] and A[4]. Again, if they are
equal, add one of these to B; otherwise do nothing. Continue in this fashion until the entire array is
read. If A has an odd number of elements, the last element is included in B if and only if B would oth-
erwise have an even number of elements. We then move B back to A and repeat the process until even-
tually there is one element which is the candidate. If no candidate emerges, there is no majority ele-
ment. This algorithm is linear.

Programming Projects
5.29 It is very difficult to see the O( N log log N ) behavior.
5.30 Note to the instructor: See Exercise 8.20 for an alternative algorithm.

5.3 Exam Questions


5.1. Which of the following functions grows fastest?
a. N
b. N log N
c. 10
d. log N
e. N 2
5.2. Which of the following functions grows fastest?
a. N + log N
b. N log N
c. N – log N
d. N
e. There is a tie among two or more functions for fastest growth rate

The next three questions apply to the following code fragment:

1 for( int i = 0; i < n; i++ )


2 for( int j = i; j <= n; j++ )
3 for( int k = i; k <= j; k++ )
4 sum++;
5 for( int p = 0; p < n*n; p++ )
6 for( int q = 0; q < p; q++ )
7 sum—;

5.3. How many times is statement 4 executed?


a. O( N )
b. O( N 2 )
c. O( N 3 )
d. O( N 4 )
e. none of the above
5.4. How many times is statement 7 executed?
a. O( N )
b. O( N 2 )
33

c. O( N 3 )
d. O( N 4 )
e. none of the above
5.5. What is the running time of the fragment?
a. O( N 4 )
b. O( N 5 )
c. O( N 6 )
d. O( N 7 )
e. none of the above
5.6. Suppose T1( N ) = O(F( N )) and T2( N ) = O(F( N )). Which of the following are true?
a. T1( N ) + T2( N ) = O(F( N ))
b. T1( N ) × T2( N ) = O(F( N ))
c. T1( N ) / T2( N ) = O( 1 )
d. T1( N ) = O(T2( N ))
e. none of the above
5.7. Programs A and B are analyzed and found to have worst-case running times no greater than150N log N
and N 2 , respectively. Which of the following statements does the analysis imply?
a. Program A will run faster on average for sufficiently large N.
b. Program B will run faster on average for small N.
c. Program A is probably simpler to code than program B.
d. There exists some input for which program B takes longer than program A.
e. none of the above
5.8. An algorithm takes 5 seconds for an input size of 100. If the algorithm is quadratic, approximately
how long does it take to solve a problem of size 200?
a. 10 seconds
b. 15 seconds
c. 20 seconds
d. 25 seconds
e. none of the above
5.9. An algorithm takes 15 seconds to solve a problem of size 1000. If the algorithm is quadratic, how
large a problem can be solved in one minute?
a. 2000
b. 4000
c. 6000
d. 60000
e. none of the above
5.10. An algorithm takes 15 seconds to solve a problem of size 200 and two minutes to solve a problem of
size 400. What is the likely running time of the algorithm?
a. constant
b. linear
c. quadratic
d. cubic
e. none of the above
5.11. What is the approximate value of log 1000000?
a. 10
b. 20
c. 50
d. 1000
e. none of the above
5.12. If log N equals 36.5, what is the value of log (2N)?
34

a. 37.5
b. 75
c. 36.5 * 36.5
d. 365
e. none of the above
5.13. Which of (a) to (d) is false about the binary search?
a. the input array must be sorted
b. successful searches take logarithmic time on average
c. unsuccessful searches take logarithmic time on average
d. the worst case for any search is logarithmic
e. all of the above are true
5.14. Which of the following has the worst average-case time bound?
a. binary search
b. interpolation search
c. sequential search
d. two of the above have equivalent average-case bounds
e. all of the above have equivalent average-case bounds
5.15. If line 3 of the code fragment (for problem 5.3) is changed to
for( int k = j; k <= n; k++ )
how many times would statement on line 4 execute?
a. O( N2 )
b. O( N3 )
c. O( N2 log N )
d. O( N4 )
e. none of the above
5.16. Which of the following is false:
a. 2N 2+ 3N = O( N 2 )
b. N 2 + N log N = O( N log N )
c. max( N 2 + N, N 3 ) = O( N 3 )
d. ( N log N )/( log N – 1 ) = O( N )
e. N/( N + 2 ) = O(1)
35

Answers to Exam Questions


1. E
2. B
3. C
4. D
5. A
6. A
7. E
8. C
9. A
10. D
11. B
12. A
13. E
14. C
15. B
16. B
CHAPTER

6 Collections API

6.1 Key Concepts and How To Teach Them


If you are not using the separation approach, then you will probably be bouncing back and forth between this chap-
ter and Part IV. In that case, there isn’t much to discuss here, except what the basic operations are.
If you are using the separation approach, then the key point of the chapter is that we have several different data struc-
tures that work with various restrictions on what can be accessed and deleted. As long as we know what the opera-
tions are, we need not worry about how they are implemented to use them. Thus there are sample main programs
that can be compiled and run with the online code. The chapter also sketches some applications and the running
times that might be expected. Definitely assign one of the programming projects in which an inefficient data struc-
ture is implemented. For the particular data structures:
Stacks: Emphasize that push and pop are easy operations that ought to take constant time and that we can use stacks
to balance symbols, reverse things, and evaluate infix expressions.
Queues: Emphasize that they should also be constant time operations because of the similarity with stacks. Refer to
the line printer queue.
List: Describe the List interface. Discuss LinkedList as an implementation of List and illustrate the iterator con-
cept.
Set: Describe a set as a container with no duplicates.
TreeSet: A TreeSet is an implementation of SortedSet (a Set with items in order). Describe the implementation
of TreeSet using a balanced binary search tree. Mention that worst-case logarithmic performance can be achieved.
HashSet: Implements a Set. Explain the concept of a hash function but do not talk about good hash functions.
Explain how it is different from TreeSet. Describe the relationship between the hash table and dictionary.
Map: Give simple examples of usage of a map. Explain the two implementations TreeMap and HashMap of maps.
Priority queues: Give the scheduler example. Explain that a priority queue is more powerful than a queue but less
powerful than a search tree, and so its running time for an efficient implementation is likely to be between these
two.

6.2 Solutions To Exercises


In Short
6.1 (a) The removed items are 6 and then 1; (b) the removed items are 4 and then 8; (c) the removed items
are 1 and then 4.
6.2 (a).
public static int count ( Collection<Collection<String>> c, String str )
{
int count = 0;

// go through each String object


// in each Collection object in Collection c
for( Collection strC : c ){
for( Object o : strC ){
String s = (String) o;
if ( s.equals( str ) )
37

count++;
}
}
return count;
}
2
(b). O (N )
(c). 18 milliseconds
In Theory
6.3 Yes. The simplest way is with a binary search tree (appropriately maintained to ensure worst-case per-
formance).
6.4 The search tree and priority queue can be used to implement subquadratic sorts.
6.5 Let e be the extended stack (that supports the findMin ). We will implement e with two stacks. One
stack, which we will call s, is used to keep track of the push and pop operations, and the other, m,
keeps track of the minimum. To implement e.push(x) , we perform s.push(x) . If x is smaller than or
equal to the top element in stack m, then we also perform m.push(x) . To implement e.pop() , we per-
form s.pop() . If the former top item is equal to the top element in stack m, then we also m.pop(x) .
e.findMin() is performed by examining the top element in m. All these operations are clearly con-
stant time.
6.6 A double-ended queue is easily implemented in constant time by extending the basic queue operations.

In Practice
6.7
public static void printReverse( Collection c )
{
java.util.Stack s = new java.util.Stack( );
Iterator itr = c.iterator();

while( itr.hasNext() )
s.push( itr.next());

while( !s.isEmpty() )
System.out.println( s.pop() );
}

Programming Projects
6.10 getFront returns the item in position 0. This is a constant time operation. enqueue places the inserted
item in the array position indicated by the current size and then increases the current size. This is also
a constant time operation. dequeue slides all the items one array position lower and decreases the cur-
rent size. Relevant error checks are performed in all cases. dequeue takes O( N ) time.
6.11 As usual, error checks must be performed; these are omitted from the description. insert performs a
binary search to determine the insertion point; elements after the insertion point are slid one array
position higher and the current size is incremented. remove performs a binary search to find the
deleted item, and if found, items in higher array positions are slid one position lower and the current
size is decremented. Both operations take linear time.
6.12 findMin is a constant time algorithm; array position zero stores the minimum. deleteMin is a linear-
time algorithm; items in the array are slid one position lower. insert into a sorted list was done in
Exercise 6.11.
6.13 findMin and deleteMin are linear-time scans; deleteMin can be finished by moving the last item
into the deleted position. insert is implemented in constant time by placing the new element in the
next available location.
38

6.14 insert adds to the next available location and then a comparison is performed to decide if it is the
new minimum. If so, the extra member is updated. This is clearly constant time. findMin just exam-
ines the extra member. deleteMin is implemented by swapping the last item into the deleted position.
However, the extra member that stores the position of the minimum element must then be updated and
this requires a linear scan.
6.15 The smallest element is at the end, so findMin takes constant time. deleteMin can remove this ele-
ment and update the current size; since no other data movement is needed, this is also constant time.
insert is implemented by a binary search to find the insertion point and then linear data movement,
and an update of the current size.
6.16 If we use a sorted array, (smallest in position 0), all operations are linear except for the following
which are constant time: findMin , findMax , deleteMax . If we use an unsorted array, insert is con-
stant time, all others are linear. If we use an unsorted array with two variables that store the position of
the minimum and maximum, then all operations are constant time except deleteMin and deleteMax .
6.17 If a sorted array is used, then findKth is implemented in constant time and the other operations in lin-
ear time. The algorithms are similar to the previous exercises.
6.19 // Fill all positions in List l with Object value
public static void fill( List l, Object value )
{
for ( int i = 0; i < l.size( ); i++){
l.set( i, value );
}
}
6.20 public static void reverse( List l )
{
for ( int i = 0; i < ( l.size( )/2 ); i++ ){
int swapIndex = ( l.size( )-1 ) - i;
Object a = l.get( i );
Object b = l.get( swapIndex );
l.set( i, b );
l.set( swapIndex, a );
}
}
6.21 //removes every other element in the list
public static void removeOdd( List l )
{
boolean odd = false;
for ( int i = 0; i < l.size(); i++ ){
if ( odd ){
l.remove(i);
i--;
}
odd = !odd;
}
}
6.22 public static Map<String, String> swapPairs( Map<String, String> m )
{
HashMap<String, String> newMap = new HashMap<String, String>( );
Set keys = m.keySet();
for ( Object o : keys ){
String k = (String) o;
String v = m.get( k );
39

if ( newMap.containsKey( v ) ){
throw new IllegalArgumentException( "Original Map cannot con-
tain duplicate values!" );
} else {
newMap.put( v, k );
}
}
return newMap;
}

6.3 Exam Questions


6.1. Which operation is not efficiently supported by priority queues?
a. deleteMin
b. find
c. findMin
d. insert
e. all of the above are efficiently supported
6.2. Which of the following data structures does not yield an efficient sort?
a. binary search tree
b. hash table
c. priority queue
d. all can be used for efficient sorting
e. none can be used for efficient sorting
6.3. Which of the following data structures requires more than constant average time for insertions?
a. hash table
b. queue
c. search tree
d. stack
e. all of the above have constant time insertion algorithms
6.4. Which data structure is used to check for balanced parentheses?
a. binary search tree
b. hash table
c. priority queue
d. queue
e. stack
6.5. Jobs sent to a printer are generally placed on a
a. binary search tree
b. hash table
c. priority queue
d. queue
e. stack
6.6. Which data structure is generally used to implement a symbol table?
a. binary search tree
b. hash table
c. priority queue
d. queue
e. stack
6.7. Which data structure maintains the event set in an event driven simulation?
a. binary search tree
b. hash table
c. priority queue
40
d. queue
e. stack
6.8. Which of the following could be used as a priority queue?
a. binary search tree
b. hash table
c. linked list
d. queue
e. stack
6.9. Which of the following does the binary heap implement?
a. binary search tree
b. hash table
c. priority queue
d. queue
e. stack
6.10. 6, 8, 4, 3, and 1 are inserted into a data structure in that order. An item is deleted using only a basic
data structure operation. If the deleted item is a 1, the data structure cannot be a
a. hash table
b. priority queue
c. queue
d. search tree
e. stack
6.11. Which operation is more efficient in a LinkedList as compared to an ArrayList ?
a. addFirst(Object element): add at the first position
b. get(int id): fetch item at position id
c. getFirst() : fetch the first item
d. removeLast() : remove the last element
e. Both (a) and (d) are more efficient in a LinkedList
6.12. Which of (a)–(d) is false about an iterator?
a. The remove method can be called only once between two calls to next .
b. Addition of objects to a container will invalidate its existing iterator.
c. The toFirst method of the iterator interface resets the iterator to the beginning.
d. If an iterator exists, elements of the container can only be removed via the remove method of the
iterator
e. all of the above are true
Answers to Exam Questions
1. B
2. B
3. C
4. E
5. D
6. B
7. C
8. A
9. C
10. C
11. A
12. C
41

CHAPTER

7 Recursion

7.1 Key Concepts and How To Teach Them


There are many theories on how to teach recursion. The presentation in the text follows the theory that recursion
should be viewed as a mathematical construct, and specifically avoids attempting to trace through the sequence of
recursive calls. As a result, this chapter will require that the instructor digress and provide a short math lesson. The
chapter also provides a peek into several algorithm design techniques, namely divide-and-conquer, dynamic pro-
gramming, and backtracking. Here’s the order of topics:
• What is recursion?
• Proof by induction
• Basic recursion
• Numerical Applications (optional but great if you can do)
• Divide-and-Conquer
• Dynamic Programming
• Backtracking

7.1.1 What is Recursion?


It always helps to provide a few natural examples first. Historically, many presentations of recursion have bad exam-
ples, such as the factorial, Fibonacci numbers, etc. I love the #include example and think it makes a good quick
assignment. Whenever I give long projects such as a word processor, I always ask students to allow nested inclu-
sion. The directory example is also quite natural. Most systems allow recursive directory listings, so the student can
see right away what is going on.

7.1.2 Proof by Induction


Emphasize the base case and the inductive hypothesis. Keep the proof simple; don’t do anything more complex than
Theorem 7.2.

7.1.3 Basic Recursion


I like to explain that recursion is nothing special: It is just a method calling another method. It happens that the other
method has the same name. One source of confusion is that the result appears to be an infinite loop. Explain that
the method is calling a clone, and not itself. Absolutely, positively, emphasize the four fundamental rules. Give an
example where the rules are violated, even in subtle ways.
The number printing routine is a great example. It is useful, since printing in binary is a common requirement that
is solved easily with recursion. Challenge your students to print in any base without using recursion! Emphasize
how the tests for the validity of base are required to ensure correctness. Then prove by induction that the recursive
routine must work. This will allow you to justify the third rule of recursion. This is the only way to design recur-
sive routines. You can show them once how it works, but after that, tell the students to forget recursive traces.
Code note: Making printIntRec a private method means that its name is not visible outside of the class in which
it is declared. This is the preferred mechanism for hiding helper methods.
42

7.1.4 Recursive Drawings


These are nice illustrations of recursions. By putting in sleep statements, the drawing can be slowed down to see
the order of recursive calls.

7.1.5 Numerical Applications


If the students have the mathematical background, this material is great for recursion. In practice, these routines
would be written nonrecursively for speed, but they illustrate how compact a recursive routine tends to be. These
are nice examples of relatively efficient algorithms. Many systems have a big-integer package that is efficient
enough to allow implementation of a mini-RSA system. You’ll need to grab the primality tester from Chapter 9 if
you want to assign this as a project.

7.1.6 Divide-and-Conquer
This is the classic recursive technique. You can cover it here or wait until you get to the sorting chapter. Depending
on the mathematical sophistication you can do one of several things: Mention Theorem 7.4, prove Theorem 7.4,
Mention Theorem 7.5, or prove Theorem 7.5. I recommend doing the maximum subsequence sum example because
it illustrates a more complex usage of recursion that is thematic in algorithm design. If you don’t want to do the
math, wave your hands or use Figure 7.21.

7.1.7 Dynamic Programming and Backtracking


These are optional topics, that will probably not be included in most CS-2 courses. If you have the time at the end
of the semester, you may want to come back to it. Backtracking makes sense to cover if you plan on exploring the
tic-tac-toe case study or another similar game. In that case, see also Section 10.2.

7.2 Solutions To Exercises


In Short
7.1 1. Base cases: Always have at least one case that can be solved without using recursion; 2. Make
progress: Every recursive call must make progress toward a base case; 3. Always assume the recursive
call works; 4. Compound interest rule: Never duplicate work by solving the same instance of a prob-
lem in separate recursive calls.
7.3 In the first case, the %p is missing (see what happens when N is a power of 2). Even if the %p is placed
at the end of the line, it is no good because the intermediate results become too large. In the second
and third cases, when N is 2, we do not make progress. In the fourth case, we violate the compound
interest rule and the result is O( N ) multiplications.
7.4 263mod 37 = (2 431 37mod ))mod 37
463mod 37 = (4 1615mod 37))mod 37
1663mod37 = ( 16 (2567mod 37 ))mod 37
= (16 (347mod 37))mod 37
347mod 37 = (34 (11563mod 37))mod 37
= (34 (93mod 37)) mod 37
93mod 37 = (9 (811mod 37))mod 37
= 26
347mod 37 = (34 · 26)mod 37 = 33
1663mod 37 = (16 · 33)mod 37 = 10
463mod 37 = (4 · 10 )37 mod = 3
263mod 37 = (2 · 3)37 mod = 6
43

7.5 gcd(1995,1492) = gcd(1492,503) = gcd(503,486) = gcd(486,17) = gcd(17,10) = gcd(10,7) = gcd(7,3)


= gcd(3,1) = 1.
7.6 N = 1517 , N´= 1440 . Suppose e = 11 . Then d = 131 .
7.7 If nickels are not part of United States Currency, then 30 cents is changed with a quarter and five pen-
nies using the greedy algorithm, while 3 dimes would be optimal.

In Theory
7.8 The basis ( N = 0 and N = 1 ) is easily verified to be true. So assume that the theorem is true for all
0 ≤ i < N and we will establish for N. Let and . Observe that both
φ and φ satisfy φ = φ + φ
N N–1 N–2 (this is verified by factoring and then solving a quadratic equation).
1 2
Since FN = FN–1 + FN–2, by the inductive hypothesis we have

7.9 All of these identities are easily proved by induction.


7.10 (a) True because N clearly divides (A + C) – (B + C)= A – B; (b) True because N clearly divides AD –
BD = (A – B)D; (c) True because AP – BP has A – B as a factor, so N must divide it.
7.11 If B ≤ A / 2 , then the remainder must be less than A / 2 (by definition). Otherwise, the quotient is 1
and the remainder is B – A < A / 2 . This implies that after two iterations, A has been at least halved,
so the running time is logarithmic by the halving principle.
7.12 The proof is straightforward.
7.13 The proof is straightforward.
7.14 The proof is straightforward.
7.15
public static long gcd( long a, long b )
{
if( a < b )
return gcd( b, a );
// a >= b
if( b == 0 )
return a;

boolean aIsOdd = a % 2 == 1;
boolean bIsOdd = b % 2 == 1;

if( !aIsOdd && !bIsOdd )


return 2 * gcd( a/2, b/2 );
if( !aIsOdd && bIsOdd )
return gcd( a/2, b );
if( aIsOdd && !bIsOdd )
return gcd( a, b/2 );
return gcd( (a+b)/2, (a-b)/2 );
}
7.16 The solution is
We prove the second case; the first and third are left to the reader. Observe logPN = logP(BM) =
MPlogPB. Working this through as in the text proof, we obtain

If A = Bk, then

Since M = (logN) / (logB) and AM = Nk and B is a constant, we obtain T( N ) = O( NklogP+1N ).


7.17 The recurrence is T( N ) = 7T( N / 2 ) + O( N 2 ). The solution is thus T( N ) = O( Nlog27 ) = O( N 2.81 ).

In Practice
7.18 The problem is that the absolute value of the most negative int is larger than the most positive int .
Thus a special case must be added for it.
7.19 The method below assumes that N is positive.

public static int numOnes( long n )


{
if( n <= 1 )
return n;
else
return numOnes( n / 2 ) + n % 2;
}

7.20 A driver routine calls binarySearch with 0 and n-1 as the last two parameters.

public static int binarySearch( Comparable [ ] a,


Comparable x, int low, int high ) throws ItemNotFound
{
if( low == high )
if( a[ low ].compares( x ) == 0 )
return low;
else
throw new ItemNotFound( );

int mid = ( low + high ) / 2;

if( a[ mid ].compares( x ) < 0 )


return binarySearch( a, x, mid + 1, high );
else
return binarySearch( a, x, low, mid );
}

7.22 Let Ways(x, i) be the number of ways to make x cents in change without using the first i coin types. If
there are N types of coins, then Ways(x, N) = 0 if x π 0 and Ways(0, i) = 1. Then Ways(x, i–1) is equal
to the sum of Ways(x – pci, i), for integer values of p no larger than x/ci (but including 0).
45

7.23 Let Sum(x, i) be true if x can be represented using the first i integers in A. We need to compute Sum(N,
K ). This is easily done using a dynamic programming algorithm.
7.24 The following method returns true if there is a subset of a that sums to exactly K. Because there are
two recursive calls of size N–1, the running time is O(2N ).

public static boolean sum( int [ ] a, int n, int k )


{
// Base Case
if( n == 1 )
return a[ 0 ] == k;
// If sum exists without last item, return true
if( sum( a, n - 1, k ) == 1 )
return true;
// Otherwise, use last item,
// see if remaining sum exists
return sum( a, n -1, k - a[ n - 1 ] );
}

7.25 The nonrecursive routine is straightforward. The recursive routine is shown below.

private void permute( char [ ] str, int low, int high )


{
if( low < high )
println( str );
for( int i = low; i <= high; i++ )
{
char [ ] tmp = str.clone( ); // tmp will be str
tmp[ i ] = str[ low ]; // with i and low
tmp[ low ] = str[ i ]; // swapped
permute( tmp, low + 1, high );
}
}

7.26 The squares will appear to move toward the viewer.

Programming Projects
7.31 This is the classic blob problem. The size of a group is one plus the size of all its neighbors and can be
computed recursively. We must mark each neighbor as it is processed to avoid overcounting (and infi-
nite recursion). The process can be viewed as a depth-first search.

7.3 Exam Questions


7.1. When performing a proof by induction, which is the case that is trivially true?
a. the basis
b. the inductive hypothesis
c. the lemma
d. the theorem
e. none of the above
46

7.2. Which data structure is used by the compiler to implement recursion?


a. hash table
b. priority queue
c. queue
d. search tree
e. stack
7.3. The following routine violates which rule(s) of recursion?

static int recurse( int n )


{
if( n == 0 )
return 0;
else
return n + recurse( n/2 )+ recurse( n/2+1 );
}

a. No base case
b. Fails to make progress
c. Performs redundant work
d. two of the above
e. all of (a), (b), and (c)
7.4. Which of the following is the most likely result of failing to make progress towards a base case in a
recursive call?
a. compiler enters into an infinite loop
b. virtual machine throws an exception when running the program
c. error at compilation time
d. recursive routine enters an infinite loop when it runs
e. recursive routine terminates with bad value but no other error
7.5. In the most common scenario, what happens when function F calls G?
a. An activation record for F is pushed
b. An activation record for F is popped
c. An activation record for G is pushed
d. An activation record for G is popped
e. none of the above
7.6. What is the running time of the following routine?

// Check if n is prime
public static boolean isPrime( int n )
{
if( n == 2 || n == 3 )
return true;
if( n % 2 == 0 )
return false;
for( int i = 3; i <= squareRoot( n ); i+=2 )
if( n % i == 0 )
return false;
return true;
}
47

a. constant time
b. O(log N )
c. O( N )
d.
e. none of the above
7.7. Which of the following constants can be made public in the RSA cryptosystem?
a. d
b. e
c. N´
d. p
e. q
7.8. Which problem is presumed hard, thus making the RSA cryptosystem safe?
a. division
b. exponentiation
c. factoring
d. computation of greatest common divisors
e. primality testing
7.9. Which of the following can be done inO( log N ) arithmetic operations?
a. Raising a number to the Nth power
b. Computing the greatest common divisor of some integer and N
c. Computing the multiplicative inverse of some number A mod N
d. two of the above
e. all of (a), (b), and (c)
7.10. A recursive algorithm works by solving two half-sized problems recursively, with an additional linear-
time overhead. The total running time is most accurately given by
a. O(log N)
b. O( N)
c. O( N log N)
d. O( N 2 )
e. none of the above
7.11. The solution to T ( N ) = T ( Î N / 2 ˚ ) + N is most accurately given by
a. O(log N )
b. O( N )
c. O( N log N )
d. O( N 2 )
e. none of the above
7.12. Which of the following strategies do not directly invoke recursion?
a. backtracking
b. divide-and-conquer
c. dynamic programming
d. two of the above do not directly invoke recursion
e. none of (a), (b), and (c) directly invoke recursion
7.13. Consider the following algorithm for searching in an unsorted array. If the size of the array is 1, then
check if it contains the element to be searched. Otherwise, divide the array into two halves, and recur-
sively search both halves. Which of (a)–(c) is false?
a. The running time of this algorithm is O( N).
b. The actual running time of this algorithm is likely to be better than sequential search.
c. This is an example of a divide-and-conquer algorithm.
48

d. all of the above are true


e. none of the above is true
7.14. Which of (a)–(d) is false for a binary search:
a. The running time is O( log N )
b. Binary search can be applied to a sorted array.
c. Binary search can be applied to a sorted linked list.
d. Recursive binary search will take longer in practice as compared to non-recursive binary search.
e. all of the above are true

Answers to Exam Questions


1. A
2. E
3. D
4. B
5. C
6. D
7. B
8. C
9. E
10. C
11. B
12. C
13. B
14. C
49

CHAPTER

8 Sorting

8.1 Key Concepts and How To Teach Them


The chapter on sorting must be covered after recursion, but one might elect the following sequence: Chapter 5,
Chapter 7, then Chapter 8, and then data structures material, with the idea that sorting does not depend on any
knowledge of data structures, but it does have lots of analysis that can tie in with the material in Chapter 5.
The material in this chapter is more or less standard, but there is more than the usual amount of analysis (for CS-
2). Few instructors will want to cover everything in this chapter; rather you must pick and choose. External sorts
and heapsort are both in Chapter 21. If you want to cover them, it makes sense to do sorting last, after all the data
structures. The topics:
• Motivation for sorting
• Insertion sort analysis
• Shellsort
• Mergesort
• Quicksort
• Selection
• Lower bound for sorting

8.1.1 Motivation for Sorting


The text provides several examples of sorting. Most students will easily accept sorting as a fundamental and impor-
tant problem. The duplicate example is worthwhile because although we generally view sorting as a postprocessing
step, the duplicate finding algorithm shows that sorting is an important preprocessing step too.

8.1.2 Insertion Sort Analysis


Students should have no trouble seeing that insertion sort is quadratic. The lower bound proof can be handled in
several ways. Option 1 is to skip it. Option 2 is to discuss it immediately. Option 3 is to discuss it in conjunction
with the more general lower bound in Section 8.8. I recommend option 2 because it gives insight into what the more
sophisticated sorts are doing.

8.1.3 Shellsort
Shellsort is a great algorithm, but it takes time to cover, and its main lesson is probably that three loops are not
always worse than two. Even so, I recommend at least mentioning it as a good alternative that is easy to code.

8.1.4 Mergesort
The concept of merging is important, so mergesort should be covered. The analysis may have already been done in
Chapter 7, depending on the instructor. I tend to not talk much about the mergesort details, and prefer to concen-
trate on merging.
50

8.1.5 Quicksort
This is a must do, and quicksort illustrates many beautiful points. The most important, I think, is that by perform-
ing an analysis, we can decide how to implement several details (duplicate handling, pivot selection, etc.).
When discussing quicksort, use the third rule of recursion. Do not draw any recursion trees; this confuses more than
helps. Early on, give the intuition that equal partitions are good and unequal partitions are bad. Skip the average-
case analysis if it is too complex for your students and give the old hand-wave: since the splits are pretty good over-
all, the running time is about N log N overall.
I discuss the pivot selection first, and then the median-of-three partitioning. As is done in the book, do the parti-
tioning modification that occurs with median-of-three last.

8.1.6 Selection
This is well-worth doing because it does not take too much time.

8.1.7 Lower Bound for Sorting


The proof in the text avoids the typical decision tree argument for two reasons. First, I didn’t want to depend on
trees at this stage. Second, the decision tree argument seemed to confuse students. In class I now state the sorting
problem as one of gathering information (eliminating N! – 1 possibilities) when initially there is no information.
Each comparison can only guarantee to eliminate at most half of the remaining possibilities, from which the result
follows.
A common point of confusion is that the lower bound implies an algorithm. Emphasize that this is not true.
The lower bound means that an algorithm must take a certain amount of time; all algorithms will use at least that
much, but that does not mean that there is an algorithm that uses only exactly that much.

8.2 Solutions To Exercises


In Short
8.1 (a) After P=2, the sequence is 1, 8, 4, 1, 5, 9, 2, 6, 5. After P=3, the sequence is 1, 4, 8, 1, 5, 9, 2, 6, 5.
After P=4, the sequence is 1, 1, 4, 8, 5, 9, 2, 6, 5. After P=5, the sequence is 1, 1, 4, 5, 8, 9, 2, 6, 5.
After P=6, the sequence is unchanged. After P=7, the sequence is 1, 1, 2, 4, 5, 8, 9, 6, 5. After P=8,
the sequence is 1, 1, 2, 4, 5, 6, 8, 9, 5. After P=9, the sequence is sorted as 1, 1, 2, 4, 5, 5, 6, 8, 9. (b)
After the 5-sort, the sequence is unchanged because it is already 5-sorted.After the 3-sort, the
sequence is 1, 1, 4, 2, 5, 9, 8, 6, 5. The 1-sort completes the sort. (c) First the sequence 8, 1, 4, 1 is
recursively sorted as 1, 1, 4, 8. Then the sequence 5, 9, 2, 6, 5 is recursively sorted as 2, 5, 5, 6, 9. The
result is merged into a final sorted sequence. (d) is omitted because (e) is more informative: (e)
After sorting the first, middle, and last elements, we have 5, 1, 4, 1, 5, 9, 2, 6, 8. Thus the pivot is 5.
Hiding it gives 5, 1, 4, 1, 6, 9, 2, 5, 8. The first swap is between 6 and 2. The next swap crosses. After
5 is swapped back, we obtain 5, 1, 4, 1, 2, 5, 6, 9, 8. The first five elements are recursively sorted and
so are the last three elements.
8.2 Insertion sort and mergesort are stable as long as the comparisons for equality do not destroy the
order. The code in the text is stable. Neither quicksort nor shellsort are stable. It is easy for duplicate
items to be disordered when one of them is compared against a third item.
8.3 First, selection via median-of-three gives a better than average pivot. Second, it avoids the need for a
test to make sure that j does not run past the start of the array. Third, random number generation is
expensive and occasionally the random number generators are faulty.

In Theory
8.4 (a) O( N); (b) O( N log N), assuming the increment sequences described in the text (a logarithmic
number of increments); (c) O( N log N); (d) O( N log N) for the implementation in the text in which
both i and j stop on equality.
8.5 These answers are identical to the previous exercise: (a) O( N); (b) O( N log N), assuming the incre-
ment sequences described in the text (that is, a logarithmic number of increments); (c) O( N log N);
(d) O( N log N) for the implementation in the text in which both i and j stop on equality.
8.6 (a) O( N2 ); (b) O( N log N), assuming the increment sequences described in the text (the exact require-
ment is that each increment is within some multiplicative constant of the previous); (c) O( N log N);
(d)O( N log N) for the implementation in the text in which both i and j stop on equality.
8.7 The inversion that existed between a[i] and a[i+k] is removed. This shows that at least one inver-
sion is removed. For each of the k–1 elements a[i+1] , a[i+2] , ... a[i+k-1] , at most two inversions
can be removed by the exchange. This gives a maximum of 2k – 1.
8.8 (b) For 20 elements, here is a bad permutation for the median-of-three quicksort: 20, 3, 5, 7, 9, 11, 13,
15, 17, 19, 4, 10, 2, 12, 6, 14, 1, 16, 8, 18. To extend to larger amounts of data for even N: The first
element is N, the middle is N–1, and the last is N–2. Odd numbers (except 1) are written in starting to
the left of center in decreasing order. Even numbers are written in decreasing order by starting at the
rightmost spot, and always skipping one available empty spot, and wrapping around when the center is
reached. This method is suitable for a hand calculation, but takes O( N log N) time to generate a per-
mutation. By inverting the actions of quicksort, it is possible to generate the permutation in linear
time.
8.9 The recurrence is with an initial condition T(1)= 0 (this describes
the number of comparisons performed on average). The solution is T( N ) = 2N + O( log N ) and is
obtained in the same way as the bound for quicksort (the math is slightly simpler).
8.10 T( N ) @ N log N – Nlog e (where e = 2.71828...).
8.11 Because È log (4!) ˘ = 5, at least 5 comparisons are required by any algorithm that sorts 4 elements.
Let the four elements be A, B, C, and D. Compare and exchange, if necessary, Aand B, and then C and
D, so that A < B and C < D. Then compare and exchange Aand C, and then B and D so that A < Cand
B < D. At this point, A is the smallest and D the largest of the four elements. A fifth comparison estab-
lishes the order between B and C.
8.12 (a) In this algorithm, all elements up to left are equal to pivot and all elements after right are also
equal to pivot . While partitioning, when a[i] is compared with a[pivot] , if they are equal, swap
a[i] with a[left] . Do the same when a[j] is compared with a[pivot] . As a result, we will get an
array in which all elements up to (but not including) left are equal to pivot , all elements between
left and up to i as less than pivot, all elements from j onwards to right as greater than pivot and all
elements from right onwards as equal to pivot . Now swap elements in the range a[low..left-1]
with a[i +low -left...i-1]. Do a similar swap for elements equal to pivot on the right. (b) Each
partition groups elements equal to the pivot at one place.If there are only d different values, then after
d iterations of the partitioning algorithm, we will have all elements with same values grouped together
in the sorted order. Since each partitioning takes O(N) time, the total running time will be O(dN).
8.13 The algorithm maintains two pointers, pointA and pointB, in arrays A and B respectively with an
invariant (number of elements <= A[pointA] in A+ number of elements <= B[pointB] in B) = N).
Initially, both point to the middle of the respective arrays. In each iteration, we first check if any one
of A[pointA] or B[pointB] is the median. If not, then if A[pointA] is greater than B[pointB], then
pointA is decreased and pointB is increased. Else, the opposite is done. The amount of increment to
pointA or point B is N/(2i+1) in iteration i.

In Practice
52

8.14 No; when i and j are both at items equal to the pivot, the result is an infinite loop.
8.15 Maintain an array indexed from 0 to 65,535 and initialized with all zeros. For each item read, incre-
ment the appropriate array entry. Then scan through the array and output the items in sorted order.
Note that 65,535 is a constant, so anything that depends on it is a constant.
8.16
private static void quicksort( Comparable [ ] a, int low, int high )
{
while ( low + CUTOFF <= high )
{
// Sort low, middle, high
int middle = ( low + high ) / 2;
if( a[ middle ].compareTo( a[ low ] ) < 0 )
swapReferences( a, low, middle );
if( a[ high ].compareTo( a[ low ] ) < 0 )
swapReferences( a, low, high );
if( a[ high ].compareTo( a[ middle ] ) < 0 )
swapReferences( a, middle, high );

// Place pivot at position high - 1


swapReferences( a, middle, high - 1 );
Comparable pivot = a[ high - 1 ];
// Begin partitioning
int i, j;
for( i = low, j = high -1; ; )
{
while( a[ ++i ].compareTo( pivot ) < 0 );
while( pivot.compareTo( a[ —j ] ) < 0 );
if( i >= j )
break;
swapReferences( a, i, j );
}
// Restore pivot
swapReferences( a, i, high - 1 );
quicksort( a, low, i - 1 );
low = i + 1;
}
insertionSort( a, low, high );
}

8.17 The number of recursive calls is logarithmic because the smaller partition will always be less or equal
to half the original size.

private static void quicksort( Comparable [ ] a, int low, int high )


{
while ( low + CUTOFF <= high )
{
// Sort low, middle, high
int middle = ( low + high ) / 2;
53

if( a[ middle ].compareTo( a[ low ] ) < 0 )


swapReferences( a, low, middle );
if( a[ high ].compareTo( a[ low ] ) < 0 )
swapReferences( a, low, high );
if( a[ high ].compareTo( a[ middle ] ) < 0 )
swapReferences( a, middle, high );

// Place pivot at position high - 1


swapReferences( a, middle, high - 1 );
Comparable pivot = a[ high - 1 ];

// Begin partitioning
int i, j;
for( i = low, j = high -1; ; )
{
while( a[ ++i ].compareTo( pivot ) < 0 );
while( pivot.compareTo( a[ —j ] ) < 0 );
if( i >= j )
break;
swapReferences( a, i, j );
}
// Restore pivot
swapReferences( a, i, high - 1 );
if( i - low > high - i )
{
quicksort( a, low, i - 1 );
low = i + 1;
}
else
{
quicksort( a, i+1, high );
high = i -1;
}
}
insertionSort( a, low, high );
}

8.18 (a) The modification is straightforward.Add a new parameter depth which is decremented in each
recursive call and a call to mergesort is made if depth is 0. (b) There can be at mostO(log N) recursive
calls to quicksort. Each recursive call (at each level) requiresO( N) time to partition. Hence, calls to
quicksort will account for O( N log N) time. Mergesort requires O( N log N) in the worst case.
Hence, the total running time will be O( N log N). (e) The technique of 8.17 is not needed as the num-
ber of recursive calls do not exceed O(log N).
8.19 (a) The quadratic algorithm is to try all pairs. (b) AnO( Nlog N) algorithm is as follows: sort the num-
bers in increasing order. Let i=0 and j=N-1 . Repeat the following until either i and j cross, or
a[i]+a[j] is the required sum: if a[i]+a[j]>K , decrement j; if a[i]+a[j]<K , increment i. (c) For N
= 10000 and the case in which there does not exist two (numbers that add up to K (which is the worst
case), the O( N 2 ) algorithm took about 11 sec whereas O( N log N ) algorithm took about 1 sec.
8.20 Divide the N numbers into two nearly equal groups. For each group, compute all possible sums of two
elements. The size of each group is thus O( N 2). Sort each group; the cost of the two sorts is O( N2log
N). Place i at the lowest position in the first group, and j at the highest position in the second group
and use the same algorithm as in Exercise 8.19; this scan takes O( N 2 ), so the total is dominated by
the sort.
8.21 The algorithm can be succinctly stated as follows: for each p, see if there are two numbers that sum to
exactly -a[p] . The initial sort costs O( N log N ), the remaining work is N iterations of anO( N ) loop,
for a total of O( N2 ). Note that in this algorithm, numbers may be repeated. It is possible to alter the
algorithm to work if repeated numbers are not allowed.
8.22 This algorithm would requireO( N3 ) space and a sort of these items would use N 3log N time.

8.3 Exam Questions


8.1. Which of the following algorithms requires the most extra space when implemented as in the text?
a. insertion sort
b. mergesort
c. quicksort
d. shellsort
e. all use only constant extra space
8.2. Which of the following is the strongest lower bound for sorting when ordering information is obtained
only by adjacent comparisons?
a. O( N log N )
b. O( N 2 )
c. Ω( N log N )
d. Ω( N 2 )
e. none of the above is a valid lower bound for this problem
8.3. Which of the following algorithms runs in quadratic average time?
a. insertion sort
b. mergesort
c. quicksort
d. shellsort
e. none of the above
8.4. Which of the following algorithms runs in N log Naverage time but quadratic worst-case time?
a. insertion sort
b. mergesort
c. quicksort
d. shellsort
e. none of the above
8.5. Which of the following algorithms, implemented as in the text, runs in o( N log N) time when pre-
sented with an array of N identical elements?
a. insertion sort
b. mergesort
c. quicksort
d. shellsort
e. none of the above
8.6. Which of the following algorithms has the largest big-Oh differential between average case and worst
case performance?
a. insertion sort
b. mergesort
c. quicksort
d. quickselect
e. shellsort
8.7. For quicksort, what do i and j do when they see keys equal to the pivot?
a. i stops, j stops
b. i stops, j goes
c. i goes, j stops
d. i goes, j goes
55

e. i and j alternate between stopping and going


8.8. In median-of-three partitioning, where is the pivot placed before partitioning begins?
a. at the start of the array
b. at the middle of the array
c. at the end of the array
d. in a temporary variable
e. none of the above
8.9. Which of the following statements about sorting five elements is the strongest statement that is
directly implied by the information theoretic lower bound?
a. 6 comparisons are sufficient
b. 6 comparisons are necessary and sufficient
c. 7 comparisons are necessary
d. 7 comparisons are sufficient
e. 7 comparisons are necessary and sufficient
8.10. If two elements of a sorted array are swapped, how much time will it take to sort the array again using
insertion sort?
a. O( 1 )
b. O( log N )
c. O( N )
d. O( N log N )
e. O( N 2 )
8.11. If the array is already sorted, which algorithm will perform best?
a. insertion sort
b. selection sort
c. merge sort
d. quicksort
e. All will take same amount of time

Answers to Exam Questions


1. B
2. D
3. A
4. C
5. A
6. D
7. A
8. E
9. C
10. C
11. A
56

CHAPTER

9 Randomization

9.1 Key Concepts and How To Teach Them


This probably would be an appendix chapter except that I wanted to discuss permutation generation, the primality
testing algorithm, and randomized algorithms in general. If you don’t want to do that, you can skip this chapter.
There are three basic topics:
• linear congruential generators
• permutation generation
• randomized algorithms

9.1.1 Linear Congruential Generators


You can begin with a discussion of random numbers and explain why we might need to generate them. Move on to
explain that random numbers satisfy certain statistical properties, and show that some sequences are not very
random.
You can give a small example of the linear congruential generator. If the audience is mathematically sophisticated,
you can get into the nitty gritty. Most likely, you will just want to show the final computation and discuss how the
Random class works.
The discussion on non-uniform generators is present mostly for reference. I would not expect it to be covered in a
CS-2 class. Remember, this could have been an appendix if not for the next two topics.

9.1.2 Permutation Generation


This is a nifty algorithm that is worth covering, especially if you assign a program to compare various sorting algo-
rithms. Line 11 is subtle and you may want to discuss it. Alternatively, you may just tell your students to use the
routine without understanding the details.

9.1.3 Randomized Algorithms


Randomized algorithms are becoming very popular. If you discuss dynamic programming and backtracking during
the course, you will also want to talk about randomized algorithms. The math may be a little overwhelming, but the
principle is not. By the way, I really do flip the coin in class. You should do it; the students love it. Always let the
students choose. I also do it right before the final: three consecutive calls and the final is off.
Students tend to think that any algorithm that might make an error is unacceptable. Counter that by arguing that the
probability of an error is much smaller than other phenomena, such as a power surge or hardware error.

9.2 Solutions To Exercises


In Short
9.1 48271, 182605794, 1291394886, 1914720637, 2078669041, 407355683, 1105902161, 854716505,
564586691, 1596680831.
9.2 If A = 2, then although 2560 ; 1 (mod 561), 2280 ; 1 (mod 561) proves that 561 is not prime. If A = 3
then 3560 ; 375 ( mod 561 ), which proves that 561 is not prime. A = 4 does not fool the algorithm;
as we saw above, 4140 ; 1 ( mod 561 ). However A = 5 is a false witness: 51 ; 5 ( mod 561 ),
52 ; 25 ( mod 561 ), 54 ; 64 ( mod 561 ), 58 ; 169 ( mod 561 ), 516 ; 511 ( mod 561 ),
517 ; 311 ( mod 561 ), 534 ; 229 ( mod 561 ), 535 ; 23 ( mod 561 ), 570 ; 529 ( mod 561 ),
5140 ; 463 ( mod 561 ), 5280 ; 67 ( mod 561 ), 5560 ; 1 ( mod 561 ).
9.3 The expected number of winners is 3 and satisfies a Poisson distribution. The probability of exactly 0
winners is e –3 = 0.0498. The probability of exactly 1 winner is 3e –3 = 0.149.
9.4 If seed is 0, then all future values of seed will also be 0.

In Theory
9.5 The proof of correctness can be found in the paper by Park and Miller or in my textbook Data
Structures and Algorithm Analysis in Java.
9.6 We only have to show that there is a sequence of swaps in the routine that corresponds to the genera-
tion of any permutation. This is easily seen inductively: if N is in position i, then the last swap was
between position i and N–1. We work this logic backwards from N to 1, and conclude inductively that
every permutation can be generated.
9.7 Flip the coins twice, number the outcomes. If both coins are identical, reflip both coins until they are
not. If the latter flip is heads generate a 0, otherwise generate a 1.

Programming Projects
9.11 Th e expe cted numbe r o f r andom numb ers to fill the ith item is N/ ( N – i + 1). Summing this gives
a total of O( N log N )
9.12 (a) Immediate from the description of the algorithm. (b) This can be shown by induction.

9.3 Exam Questions


9.1. 1000 random integers are generated randomly with a uniform distribution over the range 1 to 1000
inclusive. Which of the following would indicate a poor generator?
a. the average of the numbers is about 499
b. each number appears exactly once
c. no four consecutive numbers are all even
d. two of the above
e. all of (a), (b), and (c)
9.2. The seed of a linear congruential generator is
a. always zero
b. occasionally zero, depending on other random events
c. the initial value
d. the multiplier
e. the period of the generator
9.3. The number of winning lottery tickets satisfies which distribution?
a. Gaussian
b. negative exponential
c. normal
d. Poisson
e. uniform
9.4. Approximately how many random numbers are used in the permutation generation algorithm?
a. 1
58

b. log N
c. N
d. N log N
e. none of the above
9.5. Which of the following is a bad case for randomized quickselect?
a. any input with K = 1
b. reverse ordered input
c. sorted input
d. there are no bad inputs
e. none of the above
9.6. If the randomized primality testing algorithm (with one iteration) declares that P is prime and C com-
posite, then which of the following is most accurate?
a. There is at most a 25% chance that P has been declared prime falsely and there is at most a 25%
chance that C has been declared composite falsely
b. P is prime with 100% certainty but there is at most a 25% chance that C has been declared com-
posite falsely
c. There is at most a 25% chance that P has been declared prime falsely, but C is composite with at
least 100% certainty
d. P is prime with 100% certainty and C is composite with 100% certainty
e. all of the above statements are factually incorrect
9.7. Let x1, x2, ..... be a sequence of random numbers generated based on uniform distribution in the range
0..19. Which of (a)–(c) is false?
a. Each number in the range 0..19 is equally likely to appear at any position in the sequence.
b. y1, y2,....., where yi = 2xi is also a sequence of random numbers with uniform distribution in the
range 0..38.
c. y1, y2,....., where yi = xi mod 15 is also a sequence of random numbers with uniform distribution in
the range 0..14.
d. Two from the above are false
e. All of (a)–(c) are true
9.8. A sequence of random numbers is generated in the range 0..30 using the following generators. Which
one of the following best approximates a uniform distribution?
a. ( Current time in microseconds ) mod 31
b. ( Current time in milliseconds) mod 31
c. Roll a dice and multiply the result by 5
d. all of the above are equally good
e. none of the above is a good generator

Answers to Exam Questions


1. D
2. C
3. D
4. C
5. D
6. C
7. D
59

8. A
60

CHAPTER

10 Fun and Games

10.1 Key Concepts and How To Teach Them


This chapter contains two case studies. The first illustrates a use of the binary search; the second describes game
playing and uses a backtracking algorithm and a hash table. The chapter title refers to the particular applications:
students like playing games with the computer, so they relate well to this material.

10.1.1 Word Search Puzzle


First, describe the basic problem and examine the simple algorithm. Then show how the binary search can be used,
and finally describe the prefix test improvement. The code has several points worth examining:
The data file is checked to make sure it is sorted. Other error checks are performed, and although some are omitted
for brevity, it is important for students to understand that error checking is important.
The solvePuzzle routine calls solveDirection eight times. Emphasize that this makes things simpler. Some stu-
dents will try to argue that there is a lot of overhead incurred by eight method calls, and the repeated adding of zero
and +/– 1 and that the tests at line 14–16 in Figure 10.8 are excessive (i.e. if we wrote 8 versions of
solveDirection , we’d have less testing). It’s not true, and if they don’t agree, maybe it would be a good assign-
ment.

10.1.2 Tic-tac-toe
The general minimax strategy should be discussed first. Next, illustrate alpha-beta pruning. This takes some getting
used to for the students; in particular it is hard for them to see how alpha and beta change during the recursion. View
alpha and beta as forming a window that shrinks as the recursive calls descend.
The code is not too great, but it does fit in a page.
Transposition tables illustrate how hash tables are used to implement a dictionary. This is the first example of defin-
ing an object that is inserted into a data structure. Position is package-friendly, indicating that it is a throwaway
object. Notice that we define equals and provide a hash function.
If you have the time and can set up group projects, a game playing program is ideal. It is challenging, requires team-
work, and can be graded by running a tournament (A+ for the winner, A for the runner up, etc.).

10.2 Solutions To Exercises


In Short
10.1 No check is performed to see if a line is too long. No check is made to make sure that the number of
rows is sufficiently small. No checks are performed to make sure the characters are letters.
10.2 (a) H2C and H2D are refutations because a draw has already been established by C1. (b) The position is
a draw.

In Theory
10.3 Let a[i] be the smallest entry in a that stores a prefix of x. Since all larger positions store values
greater than x, a[mid]>x for mid>=i . Thus the largest value low can assume is i; furthermore all
smaller positions are greater than x. Thus, when the search is narrowed to one position, it must be nar-
rowed to low=mid , at which point a prefix test can be performed.
10.4 (a) The running time doubles. (b) The running time quadruples.
In Practice
10.5 As the analysis in the text suggests, for large dictionaries, the sequential search will take significantly
longer than the binary search.
10.6 The absence of the prefix test will affect performance, but not as significantly as if a sequential search
was used instead of a binary search.
10.7 The difference in the performance is not very significant since the number of entries stored are
roughly 300–400.

Programming Projects
10.14 The final score is 20-16 in favor of black.

10.3 Exam Questions


10.1. Suppose the dictionary is randomly ordered. Asymptotically (that is, in terms of a likely Big-Oh run-
ning time), which of the following alternatives is the fastest if we assume that no words are longer
than ten characters?
a. Sort the dictionary, and then use the algorithm in the text
b. Sort the dictionary, but then use interpolation search
c. Place the dictionary items in a binary search tree; the prefix test must be performed explicitly
d. Place the dictionary items in a hash table; the prefix test must be performed explicitly
e. Use a sequential search of the dictionary
10.2. Which of the following is true about the backtracking algorithm when implemented with alpha-beta
pruning and a transposition table?
a. All terminal nodes must be processed to guarantee optimal play
b. All recursive calls must lead to either a terminal node, a stored position, or a pruning to guarantee
optimal play
c. Even if all terminal nodes are reached, play might not be optimal
d. Even if all recursive calls lead to either a terminal node, a stored position, or a pruning, play might
not be optimal
e. If computing power is unlimited, the computer will always find a win for the first player
10.3. In terms of D, which is the number of levels of lookahead (or the depth of the search), which of the
following best describes the running time of the alpha-beta pruning algorithm for most games?
a. exponential
b. linear
c. logarithmic
d. quadratic
e. none of the above
10.4. Which of the following statements about alpha-beta pruning is true?
a. it computes exactly the answer that would be computed without alpha-beta pruning
b. it applies to all recursive calls
c. it is used to record previously visited positions
d. exactly two of the above statements are true
e. all of (a), (b), and (c) are true
10.5. At some point, alpha = 0.0 and beta = 10.0. A possible move evaluates to -6.0. Which of the follow-
ing is true:
a. alpha is changed to –6.0 if we are evaluating a move for the computer
b. alpha is changed to –6.0 if we are evaluating a move for the human
c. beta is changed to –6.0 if we are evaluating a move for the computer
62

d. beta is changed to –6.0 if we are evaluating a move for the human


e. Neither alpha nor beta is changed
10.6. We need to determine whether all words in list A of M words are present in another list B of N words.
Assume that words can be of length 1 to k. Consider the following two algorithms: In Algorithm 1, for
each word in A, scan B to find the word. In Algorithm 2, we first decompose B into k lists, L1,...,Lk,
where Li contains words of length i. For each word w in A, search for w in list Lx, where x is the length
of w. Which of (a)–(c) is false?
a. The running time of Algorithm 1 is O(NMk).
b. Algorithm 1 is likely to be better than 2 if M = 1.
c. Algorithm 2 is likely to be better than 1 if k is large.
d. all of the above are true
e. none of the above is true
10.7. Transposition tables are used in optimization of games. Which of (a)–(d) is false?
a. A transposition table stores previously evaluated positions.
b. Use of transposition tables may require more memory.
c. Use of transposition tables results in less overall execution time.
d. Positions in transposition tables must be stored in ordered manner.
e. all of the above are true

Answers to Exam Questions


1. D
2. B
3. A
4. D
5. D
6. D
7. D
63

CHAPTER

11 Stacks and Compilers

11.1 Key Concepts and How To Teach Them


My all-time favorite example is probably the balanced symbol program. It is easy to describe the algorithm, and
because of some weak error reporting by some compilers, it is extremely useful. The infix-to-postfix conversion
algorithm is another favorite, though it is less intuitive for the students. These case studies are excellent for illus-
trating the basics of how stacks are used, and the infix conversion program has two stacks of different types, thus
providing a powerful example of the power of generic programming.

11.1.1 Balanced Symbol Checker


The algorithm is simple and the students generally have no trouble with it. Error recovery is much more challeng-
ing, and you can ask the students to try it, but this is not so easy.
Also introduced in this section is the state machine. You can show finite state machines, but I have not done it in my
presentation because there are only three states. Go through the state machine algorithm twice, because my experi-
ence is that students don’t really see it the first time.
This is one of the few places in which I use the words precondition and post-condition. I have tried not to over-
comment, but parsing is a clear case where you need to know what you’ve read before coming in and what will have
been read when you are done.

11.1.2 Infix to Postfix Conversion


Begin by explaining how a postfix machine works. Students generally have no problem with this. I like to begin the
infix algorithm by giving some intuition about why the stack is used; this is the first few paragraphs of Section
11.2.2. We can then logically analyze the situation and come up with the rules on Page 385. You will want to do sev-
eral examples of the algorithm.
The implementation has several interesting facets including the two types of stacks. Point out how much error
checking is being performed (the exercises indicate that some checks are still missing). Explain how the precedence
table is being used. These kinds of tables are important design tools because they make it easy to add new cases.
There are several logical extensions that are proposed in the programming projects section. Pick one.

11.2 Solutions To Exercises


In Short
11.1 (a) An extraneous } is detected. (b) The } is detected to not match (. (c) Three messages are printed to
indicate that [ is unmatched. (d) The ) is extraneous and the ( is unmatched. (e) The ) does not
match the [ and the ] is extraneous.
11.2 (a) 1 2 + 3 4 ^ -; (b) 1 2 ^ 3 4 * -; (c) 1 2 3 * + 4 5 ^ - 6 +;
(d) 1 2 + 3 * 4 5 6 - ^ -
11.3 (a) See Figure 11.1 (b) See Figure 11.2; t1 through t9 are temporaries. (c) See Figure 11.3.
Figure 11.1 Infix-to-postfix for Exercise 11.3

Figure 11.2 Postfix machine steps for Exercise 11.3


65

Figure 11.3 Expression tree for Exercise 11.3

In Theory
11.5 Unary minus and binary minus are considered different symbols. The unary minus operator pops only
one symbol from the postfix stack instead of two and has precedence higher than the binary minus
operator. It is right to left associative. To recognize a unary minus operator, we must remember if the
last token matched was an operator; if so, we have a unary minus operator; otherwise we have a binary
minus operator.

In Practice
11.6 The main difficulty is that the scanning routine must be modified to recognize the ** token. This
involves using lookahead when a * is seen, and pushing back a character if the next character is not
the second *.
11.7 (a) 7; (b) Don’t allow an operand to be accepted unless there has been an operator prior to it.

11.3 Exam Questions


11.1. The infix expression 1^2-3*4 is converted to postfix. What is the order in which operators are popped
from the stack in the infix to postfix algorithm?
a. ^ - *
b. * - ^
c. - * ^
d. ^ *
e. none of the above
11.2. Which of the following types of expressions requires knowledge of precedence rules?
a. infix and postfix
b. infix only
c. postfix only
d. neither infix not postfix
11.3. What postfix expression does the expression tree below represent?
66

a. a + b * c - d
b. ( a + b ) * ( c - d )
c. a b + c d - *
d. a b c d + - *
e. none of the above
11.4. Which of the following represents an infix expression followed by the postfix equivalent?
a. a+b-c and a b c - +
b. a+b*c and a b c * +
c. a+b*c and a b c + *
d. a+b*c and a b + c *
e. more than one of the above
11.5. For which ( input symbol, the symbol at the top of the stack ) pair is the symbol at the top of the stack
not popped?
a. (+, *)
b. (+, +)
c. (*, +)
d. (*, *)
e. more than one of the above
11.6. Which one of (a)–(d) does not indicate an error when checking for balanced parenthesis?
a. In the end, the stack contains one left parenthesis.
b. In the end, the stack contains one right parenthesis.
c. In the end, stack is empty
d. The next symbol is right parenthesis and the stack is empty.
e. all of the above indicate an error
11.7. Which of (a)–(d) is false?
a. A postfix expression does not require parenthesis to specify evaluation order
b. For every infix expression, there exists an equivalent postfix expression.
c. For every postfix expression, there exists an equivalent infix expression.
d. Evaluation of a postfix expression can be done in linear time.
e. all of the above are true

Answers to Exam Questions


1. D
2. B
3. C
4. B
5. A
6. C
7. E
CHAPTER

12 Utilities

12.1 Key Concepts and How To Teach Them


Huffman’s algorithm is a classic and is worth discussing. If, however, you are concerned with seeing code that uses
the data structures, then stick with the cross-reference generator.

12.1.1 Huffman’s Algorithm


The motivation for this has become much easier with the widespread use of home computers. Many students will
have used file compression on their home system. Most modems have compression built in. Many files that are
obtained from the internet are compressed.
The students do not seem to have difficulty with Huffman’s algorithm. If you assign it for a programming project,
you may want to provide the students with a BitStream class. Alternatively, this is a great excuse to have the stu-
dents write a BitStream class.

12.1.2 Cross-reference Generator


This is another easy motivation, since students see the practicality. The code illustrates another state machine.
This code is not as bad as it looks, and every line of code does something very identifiable.
I like to lay out all of the abstractions when I first describe what is happening. But towards the end, I like to show
the students what would be going on in a language like C if things were not private. It is truly scary, because there
are references everywhere, particularly if we use the linked list implementation of the queue. Once the students
see this, then when they can get the cross-reference generator to work, they are sold on the idea of encapsulation
and information hiding for life.

12.2 Solutions To Exercises


In Short
12.1 A Huffman tree is shown in Figure 12.1.
12.2 On UNIX, using compress , compression seems worthwhile for moderately large text files such as 50K
or more. For large postscript files or Frame files, the savings can be 75% or more.
12.3 Because only leaves imply a code, Huffman’s algorithm tends to notice missing bits quickly, and also
tends to resynchronize quickly. Thus a few characters may be lost, but the rest of the transmission is
likely to be intelligible.

In Theory
12.4 (a) Follows from the text discussion; (b) otherwise, we could swap a deep and more shallow node and
lower the cost of the tree; (c) true, since the cost of the tree depends on the depth and frequency of a
node.
12.5 (a) A 2-bit code is generated if a symbol is involved in the last merge. For this to occur, it must have
frequency greater than 1/3 (so that when three symbols remain, it is not one of the smallest two trees).
However, it is still possible that even with frequency less than 1/2, a node will have more than a two
bit code. (b) Let FN be the Nth Fibonacci number. If the character distribution is 1, 1, 1, 2, 3, 5, 8, 13,
..., then the first character will have a long code. Specifically, we can construct a Huffman tree of
68

length N if the total character frequency is FN . This is seen to be a worst-case scenario, so for a 20-bit
code to occur, the frequency of a character must be at most 1/F20 (or about .0001), and probably some-
what less.

Figure 12.1 Huffman tree for Exercise 12.1

12.6 Maintain two queues Q1 and Q2. Q1 will store single-node trees in sorted order, and Q2 will store
multi-node trees in sorted order. Place the initial sin-gle-node tree into Q1, enqueueing the smallest
weight tree first. Initially Q2 is empty. Examine the first two entries of each of Q1 and Q2, and
dequeue the two smallest. (This requires an easily implemented extension of the queue class). Merge
the tree and place the result at the end of Q2. Continue this step until Q1 is empty and there is only
one tree left in Q2.
12.7 In any coding scheme, the output file must contain the encoding table and then the compressed file.
If the original file contains all symbols with the same frequency, there will not be any savings in terms
of storage. The encoding table will be an extra overhead.

12.3 Exam Questions


12.1. Which of the following must be true about file compression in general?
a. All files can be compressed
b. For any file, there must be some codes that are longer than others
c. The code must be a prefix code
d. all of the above
e. none of (a), (b), and (c)
12.2. Which of the following algorithm types classifies Huffman’s algorithm?
a. backtracking algorithm
b. divide-and-conquer algorithm
c. dynamic programming algorithm
d. greedy algorithm
e. none of the above
12.3. Which of the following characterizes a Huffman coding tree?
a. all items are stored at the leaves
b. no nodes have one child
c. the tree is balanced
d. (a) and (b) only
e. all three of (a), (b), and (c)
69

12.4. Which of the following is not an application of cross-reference generation?


a. indexing a textbook
b. listing all occurrences of each misspelled word in a document
c. listing all uses of each object in a program
d. transmitting large files over a phone line
e. all of the above are applications of a cross-reference generator
12.5. Which of the following is a valid prefix code for a file containing only the characters, a, b, c and d?
a. a = 1, b = 0, c = 01, d = 10
b. a = 10, b = 01, c = 00, d = 11
c. a = 1, b = 01, c = 101, d = 0001
d. a = 1, b = 10, c = 100, d = 1000
e. All of these are valid.
12.6. Which one of (a)–(d) is false for Huffman coding?
a. Huffman coding results in a full tree
b. Nodes at greater depth in the tree have larger code.
c. Huffman’s algorithm constructs an optimal prefix code.
d. For a file containing only k characters, Huffman coding can result in a code of length k.
e. all of the above are true

Answers to Exam Questions


1. E
2. D
3. D
4. D
5. B
6. E
70

CHAPTER

13 Simulation

13.1 Key Concepts and How To Teach Them


The two case studies are the Josephus problem and discrete-event simulation.

13.1.1 Josephus Problem


Two implementations are discussed. First, we show how to use linked lists. A circular linked list would be a better
alternative, and is a good exercise. Because we are not using a circular linked list, we have a special case when we
are at the end.
The alternative method is to use a binary search tree with order statistics. This method is interesting because it shows
(for large M) how to collapse M operations into a single searching operation whose running time does not depend
on M. Without drawing the search tree, explain how we decide which player is next to be eliminated. Then describe
the josephus method in Figure 13.3. After that, you can discuss the details of ensuring a reasonably balanced tree.
The tree construction algorithm in the text builds a perfectly balanced tree. Show how the tree is constructed for
seven elements (show the result, but do not do it by tracing the recursion). If you are using the separation approach,
you will need to merely state that it is true, and defer the details until the tree implementation is discussed in Chapter
19. At that point you can explain that the remove algorithm never makes the tree deeper, and thus each remove is
guaranteed to be logarithmic.

13.1.2 Discrete-event Simulation


I have given the simplest example of maintaining the event-set. There is no queue (this addition is an exercise). The
easiest way to teach this is to simulate the simulation. Show the state of the priority queue (as in Figure 13.10).
As the text mentions, simulation is a prime application area of object-ori-ented technology. I think the details of a
more object-oriented approach would be too much for a CS-2 course. So are the details of random number genera-
tion.

13.2 Solutions To Exercises


In Short
13.1 Player N wins.
13.2 Figure 13.1 shows that the order of elimination is 4, 1, 6, 5, 7, 3.
13.3 7 and 27 are two such values. They were obtained by writing a program to solve the Josephus problem
for N= 30 and all M between 0 and 30.
13.4 (This is done in the text).

In Theory
13.5 After the first N/2 iterations, we are back at player 1, and half the players have been eliminated. In the
next N/4 iterations, we eliminate another quarter of the players, and return back to player 1.
Continuing in this way, we see that player 1 never gets eliminated and is thus the winner.
13.6 (a) Suppose Nis even. After N/2 iterations, we are back at player 1 and only odd-numbered players
remain. Renumber the players so that original player i becomes new player (i + 1)/2. The winner of
71

the remaining game is new player J ( N/2 ), which corresponds to old player 2J ( N/2 ) – 1. (b) and (c)
use the same logic.
13.7 A simple recursive algorithm will solve the problem inO( log N ) time.
13.8 The solution uses the same ideas as Exercise 13.6 but is more complicated. The subproblems are of
size 2N / 3.
13.9 10, 5, 2, 1, 3, 4, 7, 6, 8, 9, 15, 12, 11, 13, 14, 18, 16, 17, 19, 20. In other words, the insertion order is
the same as a preorder traversal of the final tree.

Figure 13.1 Stages of BinarySearchTreeWithRank when N = 7 and M = 3

In Practice
13.10 The running time of the algorithm would be O( N log N ). The changes will require implementation of
findKth element in a tree.

13.3 Exam Questions


13.1. What is the running time of the Josephus algorithm for N players with M passes per turn, if imple-
mented with an efficient search tree that supports order statistics?
a. O ( N)
b. O ( N log N )
c. O ( N + M )
d. O ( N · M )
e. none of the above
13.2. In the FIU simulation in which 96 modems are available, how large can the event set be?
a. 2
b. 96
c. 97
d. the answer cannot be determined from the above information
e. the answer can be determined from the above information, but none of the numbers above is
correct
13.3. In the modem example, when is a hangup event generated and added to the priority queue?
a. when a dial-in event either enters or leaves the priority queue
b. when a dial-in event leaves the priority queue, only
72

c. when a dial-in event enters the priority queue, only


d. when a hangup event leaves the priority queue, only
e. none of the above
13.4. Which of (a)-(d) is false for an event driven simulation?
a. eventSet contains the set of events that will happen in the future.
b. eventSet is sorted according to the time of occurrence of events.
c. The simulation terminates when eventSet is empty.
d. If e.time = 5 for an event e, then e must be queued in eventSet for 5 time units.
e. all of the above are true
13.5. In the modem example, if a new event type, Foo , has to be introduced, which of the following modifi-
cations is needed?
a. A new class for event type Foo needs to be written.
b. A new variable Foo needs to be introduced in class Event .
c. The while loop in runSim needs to be changed to check if the next event from eventSet is of
type Foo .
d. All changes (a)–(c) are needed.
e. Only two changes from (a)–(c) are needed.

Answers to Exam Questions


1. B
2. C
3. B
4. D
5. E
73

CHAPTER

14 Graphs and Paths

14.1 Key Concepts and How To Teach Them

Graph theory is one of my favorite applications of data structures. The algorithms themselves are relatively
straightforward, but without proper data structures they are inefficient for large inputs. You should discuss Exercise
14.7 in class, or give it as a homework. This chapter discusses various shortest path algorithms. Students have a
good feel for why this is important, so motivation is generally not a problem. The exercises provide several appli-
cations. Check out Knuth’s book The Stanford Graphbase for some data files and additional applications. The top-
ics in this chapter are:
• Definitions and implementation
• Unweighted shortest paths
• Positive-weighted shortest paths
• Negative-weighted shortest paths
• Acyclic graphs

14.1.1 Definitions and Implementation


The graph definitions are mostly straightforward and intuitive. Explain the difference between dense and sparse
graphs, and point out that most interesting graphs are sparse. Describe the adjacency matrix, and explain its limita-
tions. Then show the adjacency list representation.
Figures 14.4 and 14.6–7 show the internal representation of a typical graph. Explain that the dictionary maps names
to numbers, and that to get names from numbers, we examine the graph table. Also explain that in the graph table,
dist and prev are initialized and change when a shortest path calculation is performed, while everything else is
a constant once the graph is read.
An interesting routine is printPath and its recursive companion. The recursive algorithm is quite nifty. Note that
a stack could be used instead of recursion; this is a nice little exercise.

14.1.2 Unweighted Shortest Paths


The description of the algorithm uses the notion of an eyeball to represent the current vertex. You may want to give
a short demonstration of correctness by drawing an example similar to Figure 14.25, but without edge weights. In
that picture, if w’s distance could be lowered, then u would have a higher distance than v, which is a contradiction.
After the algorithm is described explain the quadratic running time without a queue, and then show why a queue
works and works in linear time. In doing this, you have to explain why
for each v in some order
for each w adjacent to v
...
is only linear in the number of edges, even though it is two loops. The reason is that those loops essentially say, in
a controlled way
for each edge
View it as traversing each adjacency list in order. Also explain that the prev member is easily maintained by the
shortest path algorithm.
14.1.3 Positive-Weighted Shortest Paths
I generally describe the algorithm without a formal proof. Then I show with a simple example that a queue does not
work. Then I describe the priority queue. Showing what goes into the priority queue typically requires two expla-
nations.

14.1.4 Negative-Weighted Shortest Paths


I usually mention that Dijkstra’s algorithm does not work with negative edges and the problem with negative-cost
cycles. Sometimes I also mention the running time of the algorithm described in the text, but I rarely describe the
algorithm. It is present mostly for completeness.

14.1.5 Acyclic Graphs


First, describe the topological sort. I like to do the proof that if there are no vertices left with indegree zero then
there is a cycle. You need to draw a picture showing that as you trace backwards, you eventually hit a vertex you’ve
already been to. Many students get the proof, and feel good about it.
The shortest path algorithm examines the vertices in topological order. More important than the particular algorithm
is the concept, and how it applies to longest path calculations and critical path analysis. However, the text only pro-
vides a sketch, rather than a long discussion.

14.2 Solutions To Exercises


In Short
14.1 V3->V4, V3->V6, V3->V5, V3->V2, V3->V2->V0, V3->V2->V0->V1.
14.2 V3->V4, V3->V2, V3->V6, V3->V6->V5, V3->V2->V0, V3->V2->V0->V1.
14.3 Dijkstra’s algorithm can be used to compute shortest paths from a single source to all other nodes.
14.4 Changes to Figure 14.5: The adjacency lists will change as follows: E will have no outgoing edges, the
first entry in the list for D will change to 43 with a pointer to E and the list for C will contain an addi-
tional entry of 10 with pointer to D. The first entry of E will be deleted. The shortest paths will
change as follows: A to B is 12 with prev as A, A to C is infinity, A to D is 87 with prev as A and A to E
is 23 with prev as B. The result of running the topological sort algorithm will be C A D B E.
14.5 The adjacency lists will change as follows: The list for C will have one more entry of 11 with pointer
to B, and the list for B will have one more entry of 10 with pointer to F. The list for F will be empty.
The shortest paths will be as follows: A to B is 12 with prev as A, A to C is 76 with prev as D, A to F is
22 with prev as B, A to E is 23 with prev as B, and A to D is 66 with prev as E.

In Theory
14.6 Keep an extra stack and add to each adjacency matrix entry a member which we call whereOnStack .
Suppose a[i][j] is inserted. Push the pair i,j onto the stack, and then have the whereOnStack
member point at the top of the stack. When we access an adjacency matrix entry, we check that
whereOnStack references a valid part of the stack and that the pair stored there corresponds to the
matrix entry that we are trying to access.
14.7 A queue will no longer work. We store the weighted cost of the path. Then, instead of testing if w is at
distance infinity, also allow that w is at distance dv+1 but with a total weight that would be smaller.
Note that a queue no longer works; we need a priority queue again, and the weights must be non-
negative. If there was an additional field called weight , this would be implemented as:

if( table[ w ].dist == Infinity ||


table[ w ].dist == table[ v ].dist + 1 &&
75

table[ w ].weight > table[ v ].weight + cvw )


{
table[ w ].dist = table[ v ].dist + 1;
table[ w ].weight = table[ v ].weight + cvw;
...
}

14.8 Use an array count such that for any vertex u, count[u] is the number of distinct paths from s to u
known so far. When a vertex v is marked as known, its adjacency list is traversed. Let w be a vertex on
the adjacency list. If Dv+c v,w =D w, then increment count[w] by count[v] , because all shortest paths
from s to v with last edge (v,w) give a shortest path to w. If Dv+c v,w <D w, then Dw and the previous ver-
tex info get updated. All previously known shortest paths to w are now invalid, but all shortest paths to
v now lead to shortest paths to w, so set count[w] to equal count[v] . Note: zero cost edges mess up
this algorithm.
14.9 Use an array numEdges such that for any vertex u, numEdges[u] is the shortest number of edges on a
path of distance Du from s to u known so far. When a vertex v is marked as known, its adjacency list is
traversed. Let w be a vertex on the adjacency list. If Dv+c v,w =D w, then change the previous vertex to v
info an d se t numEdges[w] to numEdges[v]+1 if numEdges[v]+1<numEdges[w] . If Dv+c v,w <D w, then
D and the previous vertex info get updated and we set numEdges[w] to numEdges[v]+1 .
14.10 In the graph below, Dijkstra’s algorithm gives a shortest path from A to D of 5, even though the short-
est path is 4.

14.11 This transformation adds more weight to paths that have many edges than it does to paths that have
few edges; thus the resulting shortest paths do not necessarily correspond to the original.
14.12 We define a pass of the algorithm as follows: Pass 0 consists of marking the start vertex as known and
placing its adjacent vertices on the queue. For j > 0 , pass j consists of marking as known all vertices
on the queue at the end of pass j–1 . Each pass requires O( |E| ) time, since during a pass, a vertex is
placed on the queue at most once. It is easy to show by induction that if there is a shortest path from s
to v containing k edges, then dv will equal the length of this path by the beginning of pass k (because
the shortest path to its predecessor has length k–1 , and has already been found, by the inductive
hypothesis). Thus there are at most |V| passes, giving an O( |E| |V| )time bound.
14.13 The phrasing in the text is a bit misleading—the question is still a single source problem. The longest
path problem can be solved recursively: for each w adjacent to v compute the longest path that begins
at w. Then the computation for v becomes simple. This works in acyclic graphs because the recursion
is guaranteed to terminate (there are no cycles) and each edge is visited only once.
14.15 The maximum bottleneck problem can be solved by modifying the shortest path algorithm as follows:
We define the cost of a path as the weight of the shortest edge in the path. In the algorithm of Figure
14.27, modify lines 35..38 as follows:

if( w.dist < min(v.dist,cvw) )


w.dist = min(v.dist, cvw)
w.prev = v;

14.16 (a) True. In an acyclic graph, there is either a path from uto v or from vto uor no path. If there is no
path, then an edge can be added in either direction without creating a cycle. If there exists a path from
76

uto v, then we can add an edge from uto v. Otherwise, an edge can be added from vto u. (b) True. If
adding (u,v) creates a cycle, then there exists a path from vto uin G. If adding (v,u) creates a cycle,
then there exists a path from uto vin G. Thus, there is a cycle in G.

Programming Practice
14.19 Each team is a vertex; if X has defeated Y, draw an edge from X to Y.
14.20 Each word is a vertex; if X and Y differ in one character, draw edges from X to Yand Y to X.
14.21 Each currency is a vertex; draw an edge of ( –log C ) between vertices to represent a currency
exchange rate, C. A negative cycle represents an arbitrage play.
14.22 Each course is a vertex; if X is a prerequisite for Y, draw an edge from X to Y. Find the longest path in
the acyclic graph.
14.23 Each actor is a vertex; an edge connects two vertices if the actors have a shared movie role. The graph
is not drawn explicitly, but edges can be deduced as needed by examining cast lists.

14.3 Exam Questions


14.1. Which of the following is a synonym for an edge?
a. arc
b. node
c. path
d. vertex
e. none of the above
14.2. Which of the following problems is not known to be solvable in linear time?
a. topological sort
b. unweighted shortest path in general graphs
c. weighted shortest path in acyclic graphs
d. weighted shortest path in cyclic graphs
e. all are solvable in linear time
14.3. Which of the following does not use a queue?
a. negative-weighted shortest path algorithm
b. positive-weighted shortest path algorithm
c. topological sort
d. unweighted shortest path algorithm
e. all of the above use a queue
14.4. Which of the following algorithms solves the unweighted single-source shortest path problem?
a. breadth first search
b. Dijkstra’s algorithm
c. Kruskal’s algorithm
d. all of the above
e. none of (a), (b), and (c)
14.5. Which of the following algorithms solves the positive-weighted single-source shortest path problem?
a. breadth first search
b. Dijkstra’s algorithm
c. Kruskal’s algorithm
d. all of the above
e. none of (a), (b), and (c)
14.6. When the edge (u,v) is added to a directed graph which of the following does not occur?
a. u is added to the dictionary if it did not already exist
b. v is added to the dictionary if it did not already exist
77

c. v is added to u’s adjacency list


d. u is added to v’s adjacency list
e. all of the above occur
14.7. In a graph with v vertices and e edges, which of the following maximum sizes is not correct for a
shortest path computation?
a. v for the number of adjacency lists
b. e for the total size of all adjacency lists
c. e for the size of the dictionary
d. v for the size of the queue
e. all of the above are correct
14.8. In a connected graph with no loops or multiple edges, which of the following inequalities is not cor-
rect? (v is the number of vertices, e is the number of edges)
a. e ≤ v 2
b. e ≥ v –1
c. v ≤ e2
d. v ≥ e / 2
e. all of the above are correct
14.9. If the shortest path algorithm is run and a vertex is not reachable from the starting point, what
happens?
a. a distance of infinity is reported
b. a distance of –1 is reported
c. a distance of zero is reported
d. the algorithm enters an infinite loop
e. the algorithm’s results are undefined
14.10. For the weighted shortest path problem, let dv be the cost of reaching the current vertex v, let w be
adjacent to v and assume the edge cost is cv,w. , Suppose that dw was the cost of reaching w prior to
examining v. (Ties are broken in favor of the first path seen). Then under what circumstances is w’s
distance lowered?
a. dw > dv
b. dw > dv + 1
c. dw > dv + cv,w
d. dv > dw + cv,w
e. none of the above
14.11. Which of the following statements is true?
a. A topological ordering cannot exist in a graph that has a cycle
b. Every acyclic graph has at least one topological ordering
c. Every acyclic graph has exactly one topological ordering
d. Every acyclic graph has at most one topological ordering
e. none of the above
The next three questions refer to the following graph:
78

14.12. The shortest weighted path fromV4 toV5 has weight


a. 2
b. 4
c. 7
d. 8
e. none of the above
14.13. If the start vertex is V4 , then using the standard weighted shortest path algorithm, which is the last
vertex to be declared known?
a. V0
b. V1
c. V2
d. V4
e. none of the above
14.14. If the start vertex is V4 , then using the acyclic weighted shortest path algorithm, which is the last ver-
tex to be declared known?
a. V0
b. V1
c. V2
d. the graph is not acyclic, so the acyclic algorithm should not be used
e. none of the above
14.15. Which of (a)–(d) is false?
a. Adjacency list representation is preferred over adjacency matrix if the graph is sparse.
b. The storage requirement of adjacency matrix is O( N2 ).
c. Accessing the weight of a specific link in adjacency list representation can take O( N ) time.
d. The weight of a link in adjacency list representation can be changed in O(1) time.
e. all of the above are true
14.16. Which of the following is false for a topological sort of the graph in Figure 14.30 in which the edge
from V3 to V6 and the edge from V0 to V1 are deleted?
a. V1 is a possible first vertex in the sort
b. V0 is not a possible first vertex in the sort
c. V5 must be the last vertex in the sort
d. In any topological sort, V2 must precede V1
e. In any topological sort, V3 must precede V6

Answers to Exam Questions


1. A
2. D
3. B
4. A
5. B
6. D
7. C
8. D
9. A
10. C
11. B
12. C
13. B
14. C
15. D
16. D
80

CHAPTER

15 Inner Classes and


Implementation of ArrayList

15.1 Key Concepts and How To Teach Them


The goal of this chapter is to discuss the usage and syntax of inner classes, implementation of AbstractCollection
class and implementation of ArrayList using an inner class.

15.1.1 Nested Classes


Using the Iterator example, explain that nested classes can be used to hide implementation details. The nested
class is a member of its outer class and can access private data of the outer class. Furthermore, the nested class can
be declared private to hide its implementation details.

15.1.2 Inner Classes


An inner class is similar to a non-static nested class. It contains an implicit reference to the outer class object that
created it. Thus, Outer.this.x can be used to refer to variable x of the Outer class object. In fact, Outer.this is
optional. Use the iterator example to illustrate this difference. Mention some of the issues that arise if an inner class
is declared public.

15.1.3 AbstractCollection
This implements some of the methods in the Collection interface. Briefly go through the implementation of this
class. The students should be able to understand it easily.

15.1.4 ArrayList
The implementation in this chapter extends the AbstractCollection class and implements the List interface.
Go through the implementation of the class ArrayListIterator which is an inner class.

15.2 Solutions To Exercises


In Short
15.1 When an inner class object is constructed, it has an implicit reference to the outer class whereas this is
not the case for a nested class. Inner class is like a non-static nested class.
15.2 Private members of an inner class are public to the outer class.
15.3 The declaration of a is legal and b is illegal because Inner1 is non-static (inner) class whereas Inner2
is static.
15.4 An object of type Inner1 can be created using a
Inner1 in1 = new Inner1();

statement in any method of class Outer . An object of type Inner2 can be created using a statement
Inner2 in2 = new Inner2(this);
A constructor needs to be added to class Inner2 .

In Theory
15.6 To refer to I, we will need to specify O.I . However, this may be problem if O does not exist. Hence,
81

we may need syntax such as implements o.I, where o is a class O object.


15.7 The running time of clear is O(1). The running time of the inherited version would be O( N ), where
N is the size of the list.

In Practice
15.8
public class MyContainer
{
private Object [ ] items = new Object[ 5 ];
private int size = 0;

public Object get( int idx )


{
if( idx < 0 || idx >= size )
throw new ArrayIndexOutOfBoundsException( );
return items[ idx ];
}

public boolean add( Object x )


{
if( items.length == size )
{
Object [] old = items;
items = new Object[ items.length * 2 + 1 ];
for( int i = 0; i < size; i++ )
items[ i ] = old[ i ];
}

items[ size++ ] = x;
return true;
}

public Iterator iterator( )


{
return new LocalIterator( );
}

private class LocalIterator implements Iterator


{
private int current = 0;

public boolean hasNext( )


{
return current < size;
}

public Object next( )


{
return items[ current++ ];
}
public boolean hasPrevious()
{
return current > 0;
82

public Object previous()


{
return items[current—];
}
}
}

15.9 This is an example of an adaptor pattern.

import java.util.*;
public class BetterIterator
{
Iterator itr;

public BetterIterator()
{
Iterator itr = new Iterator();
}

public boolean isValid()


{
return itr.hasNext();
}

public void advance()


{
itr.next();
}

public Object retrieve()


{
return itr.next();
}
}

15.10 The first one will throw a ConcurrentModificationExceptio(n ).


15.3 Exam Questions
15.1. Which of (a)–(c) is false?
a. A nested class can only access public members of its outer class.
b. A private nested class is package visible
c. A nested class has an implicit reference to its outer class.
d. all of the above are true
e. all of the above are false
15.2. Which of (a)–(d) is false?
a. An inner class is similar to a non-static nested class.
b. An inner class has an implicit reference to its outer class.
c. The constructor of a public inner class can only be invoked from its outer class.
d. Inner classes are typically declared private
e. all of the above are true
15.3. Which of (a)–(c) is false?
a. A local class in a static method behaves like a nested class.
b. A local class declared inside an instance method has an implicit reference to the invoker of the
method.
c. A local class has no visibility modifier.
d. all of the above are true
e. all of the above are false
15.4. Which of the following method is not implemented in AbstractCollection?
a. clear
b. toArray
c. contains
d. size
e. remove
15.5. Which of (a)–(c) is false for Figure O.1?
a. Line 6 is illegal
b. Line 7 is illegal
c. Line 13 is illegal
d. all of the above are true
e. all of the above are false
15.6. In Figure 15.1, if class Inner1 is declared static, which line has to be changed?
a. Line 6
b. Line 7
c. Line 13
d. All of 6, 7 and 13 have to be changed.
e. Two of 6, 7 and 13 have to be changed

1 class Outer
2 {
3 private int x = 0;
4 private class Inner1
5 {
6 public int y = Outer.this.x;
7 private int z = x;
8 }
9
10 public void foo()
11 {
12 Inner1 in1 = new Inner1();
13 x = in1.y;
14 }
15 }
Figure 15.1

15.7. Which of (a)–(d) is false?


a. AbstractCollection is an abstract class
b. AbstractCollection implements the Collection interface.
c. AbstractCollection implements some methods of the Collection interface in terms of other
methods of the same interface.
d. AbstractCollection is also an interface class.
e. all of the above are true
15.8. Which class does ArrayList implement?
84

a. AbstractCollection
b. List
c. AbstractList
d. LinkedList
e. none of the above
15.9. Which of (a)–(c) is true regarding class ArrayListIterator ?
a. It is an example of the adapter pattern.
b. It implements the ListIterator interface
c. It implements the LinkedListIterator interface
d. all of the above are true
e. two of the above are true
15.10. What is the running time of findPos in ArrayList ?
a. O( N )
b. O( log N )
c. O( N ) if the element is found.
d. O( 1 ) if the element is found.
e. none of the above

Answers to Exam Questions


1. E
2. C
3. D
4. D
5. E
6. E
7. D
8. B
9. B
10. A
85

CHAPTER

16 Stacks and Queues

16.1 Key Concepts and How To Teach Them


Array and linked list implementations of the stack and queue are presented in this chapter. A new data structure, the
double-ended queue is also described. The instructor may discuss both stack implementations followed by both
queue implementations, or may discuss both array implementations followed by both linked list implementations.
In either scenario, be sure to compare the relative merits of an array and linked list implementation. I tend to dis-
cuss the stack implementations and then the queue implementations.

16.1.1 Array-based Stack


Students rarely have problems with this. You should explain why array doubling is cheap. Contrast it with expand-
ing the array by a fixed amount (such as adding five).

16.1.2 Array-based Queue


This is more complex than the stack because of the wraparound. The text description matches my presentation.
Copying of queues and doubling is messy because of the wraparound (and a desire to avoid excessive copying).
Students do not seem to have problems with the principles, but the coding seems to be difficult.

16.1.3 Linked list-based Stack


I placed stacks and queues before the general list discussion because these are easier cases. An important detail that
is seen throughout is the package-friendly ListNode . Mention that its members need not be private because since
it is not a public class, it is already invisible outside of the package.

16.1.4 Linked list-based Queue


I like to explain that since a dequeue yields a new front, we must have things set up so that it is easy to determine
who is second in line. This means that the front must be the first node in the list, not the last. Also, there is a spe-
cial case for enqueue that deals with an empty queue. Be sure to explain it. Some people like to use header nodes
for this; It does not seem justified to me.

16.1.5 Double-ended Queue


Maybe this isn’t the greatest example of inheritance, but I wanted to show something. If we are concerned with effi-
ciency, we must derive from the array-based class because as remarked above, the singly-linked list version does
not support deletion at both ends of the list.

16.2 Solutions To Exercises


In Short
16.1 Figure 16.1 shows the states of the data structures. The first line illustrates the state of the array-based
stack. Next is the array-based queue. The boldfaced item is the rear, while the italicized item is the
front. The third picture shows the state of the stack when implemented as a list. The final picture
shows the queue implemented as a list.
86

Figure 16.1 Stages of stacks and queues for Exercise 16.1

In Practice
16.4
public static void main( String [ ] args )
{
Stack intStack = new StackAr( );
Stack doubleStack = new StackAr( );

intStack.push( new Integer( 5 ) );


doubleStack.push( new Double( 7.0 ) );
// etc...
}
16.5 For this inheritance to work, the previous private section of the queue class must be protected and we
need to use the array implementation (because the node prior to the back is not easily accessible).
Assuming that this is done,

public void addFront( Object x )


{
if( currentSize == array.length )
doubleQueue( );
front = decrement( front );
array[ front ] = x;
currentSize++;
}

public void removeBack( ) throws Underflow


{
if( isEmpty( ) )
throw new Underflow( “Empty Deque” );
currentSize—;
decrement( back );
}

public Object getBack( ) throws Underflow


{
if( isEmpty( ) )
throw new Underflow( “Empty Deque” );
return array[ back ];
}
87

16.6 push(x) is implemented as add(x) , pop() is implemented as remove(size() - 1) and top() as


get(size() -1). The ArrayList has an automatic array doubling feature.
16.7 ArrayList is not a good choice to implement to a queue because when the array size is doubled the,
ArrayList implementation copies elements at the same location. In the case of a queue (see array
based implementation of queue), in the new doubled array, the elements are copied so that the front of
the queue is at location 0. This allows the new element to be added at the end of the array. The
ArrayList implementation will require extra work in this case.
16.8 This is done in the Java library. Because of wraparound, after the array has been doubled, either the
elements from the start of the array until position rear must be relocated or the elements from posi-
tion front to the position that is representative of the end of the original array must be relocated to the
end of the array. A simple calculation chooses the alternative that involves the fewer number of ele-
ments.

16.3 Exam Questions


16.1. For the array implementation of the stack, what is the worst-case cost of a single push operation if
array doubling is used?
a. O( 1 )
b. O( log N )
c. O( N )
d. O( N log N )
e. none of the above
16.2. For the array implementation of the stack, what is the worst-case total cost of any N consecutive stack
operations if array doubling is used?
a. O( 1 )
b. O( log N )
c. O( N )
d. O( N log N )
e. none of the above
16.3. What happens when wraparound is implemented for a queue?
a. If front advances past the last array position, it is reset to the first array position.
b. If rear advances past the last array position, it is reset to the first array position.
c. Both (a) and (b)
d. Neither (a) nor (b)
16.4. If front and rear have identical values, what is the size of the queue?
a. 0
b. 1
c. 2
d. the answer cannot be determined
e. none of the above
16.5. For the linked list implementation of the stack, where are the pushes and pops performed?
a. Push in front of the first element, pop the first element
b. Push after the last element, pop the last element
c. Push after the last element, pop the first element
d. Push in front of the first element, pop the last element
e. Push after the first element, pop the first element
16.6. For the linked list implementation of the queue, where are the enqueue and dequeues performed?
a. Enqueue in front of the first element, dequeue the first element
b. Enqueue after the last element, dequeue the last element
c. Enqueue after the last element, dequeue the first element
d. Enqueue in front of the first element, dequeue the last element
e. Enqueue after the first element, dequeue the first element
16.7. For the linked list implementation, in a stack method, if the stack is not empty, which of the following
accesses the top element?
a. element
b. topOfStack
c. topOfStack.element
d. topOfStack.next.element
e. none of the above
16.8. For the linked list implementation, which of the following is true if the queue is empty?
a. front==rear
b. front.equals(rear )
c. front==null
d. rear==null
e. none of the above
16.9. Which operation is not supported in constant time by a double-ended queue (deque)?
a. Insertion at the front or rear item
b. Access of the front or rear item
c. Deletion of the front or rear item
d. Access and deletion of the minimum item
e. all of the above are supported
16.10. Which of the following operations has the least running time in a Stack (assume that only Stack
interface operations are available; some operations may require use of an additional temporary stack)?
a. Deleting the element at the bottom of the stack.
b. Swapping the top two elements of the stack.
c. Checking if an element x is present in the stack.
d. Finding the number of elements in the stack
e. all of the above have identical worst case running time
16.11. Which of the following operations has the least running time in a Queue (assume that only Queue
interface operations are available; some operations may require use of an additional temporary queue)?
a. Deleting the element at the rear of the queue.
b. Swapping the front two elements of the queue.
c. Checking if an element x is present in the queue.
d. Finding the number of elements in the queue.
e. all of the above have identical worst case running time

Answers to Exam Questions


1. C
2. C
3. C
4. B
5. A
6. C
7. C
8. C
9. D
10. B
11. E
89
90

CHAPTER

17 Linked Lists

17.1 Key Concepts and How To Teach Them

The concept of a list iterator was discussed in Chapter 6. In this chapter we implement the linked list and iterator,
and examine variations such as the doubly linked list, circular linked list, and sorted linked list. Here is a basic
game plan:
• basic ideas: header nodes and iterator classes
• implementation details
• doubly linked lists
• circular linked lists
• sorted linked lists
• implementation of Collections API LinkedList class

17.1.1 Basic Ideas: Header Nodes and Iterator Classes


Review the basics of inserting and deleting a node into a linked list. I like to then explain the need for header nodes
as follows: in a list with N items, there are N+1 places to insert a new item, so one of these places has to be a spe-
cial case unless we use a header. Similarly, for a deletion, deletion of the first node would be a special case. I explain
how special cases always take more time and cause more trouble. Show what an empty list looks like with a header
node (which I call the zeroth node) and explain that a deletion of the first node now makes sense since we can bypass
it. For the iterator class, reiterate that it is used to store a current position and that all access to the list must be
through some iterator. Also mention that the iterator is bound to the list at construction and cannot be rebound, and
that a list may have several iterators.

17.1.2 Implementation Details


Before getting to code, remark that we must clearly state what each operation does and how errors are handled.
This is especially important for lists because there are so many reasonable possibilities.

17.1.3 Doubly Linked Lists and Circular Linked Lists


Generally I discuss these briefly and go into details only if I assign it as a programming project. There isn’t much
that is really new.

17.1.4 Sorted Linked Lists


This is a nice example of inheritance, if you want to discuss inheritance. Even if you do not, this can be discussed
as an example where inheritance makes sense in principle, without going into coding details.
This example illustrates the tricky concept that even when dynamic binding is used, parameters are statically
matched.

17.1.5 Collections API LinkedList implementation


Briefly go through some portions of the implementation. Students should be able to understand it easily by reading
it themselves.
91

17.2 Solutions To Exercises


In Short
17.1 See Figure 17.5 on page 540 of the text.
17.2 See Figure 17.16 on page 550 of the text.

public void reverseList( )


{
ListNode previousPosition;
ListNode currentPosition;
ListNode nextPosition;

currentPosition = header.next;
nextPosition = currentPosition.next;
previousPosition = null;

while( nextPosition != null )


{
currentPosition.next = previousPosition;
previousPosition = currentPosition;
currentPosition = nextPosition;
nextPosition = nextPosition.next;
}
currentPosition.next = previousPosition;
header.next = currentPosition;
}

In Theory
17.3 Reversal of a linked list in constant extra space can be done by maintaining 2 references:
previousPosition and currentPosition . At any time, the list from the start to previousPosition
is already reversed, while the rest of the list, from currentPosition to the end is normal. Each itera-
tion of a loop extends the reversal by one position while maintaining these invariants. The code is
shown above.
17.4 (a) Scan the list and mark each node as you visit it. If you reach an already visited node, there is a
cycle. The marking will take O(N) extra space. (b) Have two iterators itr1 and itr2 , where itr1
moves twice as fast as itr2 . In each iteration, itr1 is moved twice and itr1 is moved once. At each
node visited by itr1 (itr2 ), we check whether itr2 (resp itr1 ) is pointing to the same node. If yes,
then there exists a cycle.
17.5 Maintaining an iterator that corresponds to the last item will provide constant time access to the last
item as well as the first item. Hence, we can perform queue operations in constant time.
17.6 Copy the value of the next node in the current node and then delete the next node.
17.7 (a) Add a new node after position p and copy the element in position p to the new node. Then, change
data of position p to the new item. (b) Copy the next element to position p and delete the next node.

In Practice
17.9 remove is shown below.

public void remove( ) throws ItemNotFound


{
boolean removedOK = false;
92

for( ListNode p = theList.header; p.next != null; )


if( !p.next.element.equals( x ) )
p = p.next;
else
{
removedOK = true;
p.next = p.next.next;
}

if( removedOK )
current = theList.header;
else
throw new ItemNotFound( “Remove fails” );
}

17.11 Since it is not part of the iterator class, it cannot access private iterator members.
17.14 To implement retreat, we save the original position and then traverse the list until we are right in front
of the original position.

public void retreat( )


{
ListNode original = current;
current = theList.header;
while( current.next != original )
current = current.next;
}

17.3 Exam Questions


17.1. Which of the following operations is not efficiently supported by a singly-linked list?
a. accessing the element in the current position
b. insertion after the current position
c. moving to the position immediately prior to the current position
d. moving to the position immediately following the current position
e. all of the above are efficiently supported
17.2. Which statement inserts an item x after position current ?
a. current = new ListNode( x, current );
b. current = new ListNode( x, current.next );
c. current.next = new ListNode( x, current );
d. current.next = new ListNode( x, current.next );
e. none of the above
17.3. The header node
a. simplifies deletion
b. simplifies insertion
c. uses only constant extra space
d. two of the above
e. all three of (a), (b), and (c)
17.4. If a header node is used, which of the following indicates a list with one item?
a. header==null
b. header!=null
93

c. header!=null && header.next!=null


d. header!=null && header.next!=null && header.next.next==null
e. none of the above
17.5. Which of the following is not true about the iterator?
a. the current position is stored in it
b. the current list (in some form) is stored in it
c. it is in the same package as the list class
d. the list class can access its private members
e. all of the above are true
17.6. Which condition, if true, guarantees that a doubly linked list with head and tail nodes is empty?
a. head==null
b. tail==null
c. head==tail
d. head.next==tail.prev
e. none of the above
17.7. Insertion of a node into a doubly linked list requires how many changes to next and prev references?
a. no changes
b. 1 next , 1 prev
c. 2 next , 2 prev
d. 3 next , 3 prev
e. none of the above
17.8. What operation is supported in constant time by the doubly linked list, but not by the singly linked
list?
a. first
b. retreat
c. advance
d. retrieve
e. all of the above are always constant time
17.9. If a sorted linked list is derived from a linked list, what method has to change?
a. constructor
b. find
c. insert
d. two of the above must change for a legal implementation
e. all of the above must change for a legal implementation
17.10. The Unix editor vi allows searching in both directions, with wraparound if necessary. If the sequence
of lines is stored as a linked list, which of the following is most reasonable?
a. singly linked list
b. doubly linked list
c. circular singly linked list
d. circular doubly linked list
e. none of the above
17.11. What would be running time for merging two linked lists into a single linked list?
a. O( N )
b. O( N 2 )
c. O( N log N )
d. O( 1 )
e. none of the above
17.12. Consider an operation in which you need to return the elements immediately preceding and following
a given item x. Which variation of lists would be most appropriate?
a. singly linked list
b. singly linked list with header and trailer nodes,
c. circular list
d. doubly linked list
e. all of the above are equally good

Answers to Exam Questions


1. C
2. D
3. E
4. E
5. D
6. D
7. C
8. B
9. D
10. D
11. A
12. D
95

CHAPTER

18 Trees

18.1 Key Concepts and How To Teach Them

This chapter describes trees and shows their recursive definition. The most important concept is the various tra-
versals. Here are some important topics:
• general trees and recursion
• binary trees and recursion
• tree traversal

18.1.1 General Trees and Recursion


Begin by giving some basic definitions of child, parent, etc., and show the first child/next sibling implementation. I
love to use the file system as an example, and show recursive traversals.

18.1.2 Binary Trees and Recursion


Show the binary tree with examples of the expression tree and Huffman tree. Depending on how you have proceeded
so far, you may have already covered these! The text contains a binary tree class, but it is somewhat difficult to use.
Its main purpose is to provide a framework in which to write the subsequent traversals. I think that the merge rou-
tine is very interesting however, because it illustrates various special cases that must be considered. If you have time,
examine it.
Definitely cover the recursive implementations of size , height , and so on. Do this by invoking the third rule of
recursion and using the pictures in the text. Finally, show the recursive tree traversals.

18.1.3 Tree Traversal


The tree iterator classes are important because they show how a stack is used to implement recursion, and more
importantly, confirm the linearity of the tree traversals. The classes are all derived from a single abstract base class
and are arranged from most complex to least complex. Go through the examples twice. The level order traversal
should be covered because it is similar to the preorder traversal and also is similar to the unweighted shortest path
algorithm.

18.2 Solutions To Exercises


In Short
18.1 (a) The root is A; (b) Leaves are G, H, I, L, M, and K; (c) The depth is 4; (d) preorder: A, B, D, G, H,
E, I, J, L, M, C, F, K; postorder: G, H, D, I, L, M, J, E, B, K, F, C, A; inorder: G, D, H, B, I, E, L, J,
M, A, C, F, K; level order: A, B, C, D, E, F, G, H, I, J, K, L, M.
18.2 See Figure 18.1.
18.3 abbdddbaceeecca (with appropriate newlines).
18.4 The stack states are shown in Figures 18.2 and 18.3.

In Theory
18.5 Proof is by induction. The theorem is trivially true for H = 0 . Assume that it is true for H = 0, 1, 2, ...,
96

k ; we show that this implies it is true for H = 1 + k. This follows because a tree of height k + 1 has
two subtrees whose height is at most k. By the induction hypothesis, these subtrees can have at most 2k
+ 1 –1 nodes each. These 2k + 2 –2 nodes plus the root prove the theorem for k + 1 and hence for all

heights.

Figure 18.1 Solution to Exercise 18.2

Figure 18.2 Stack states during inorder traversal

Figure 18.3 Stack states during preorder traversal

18.6 Let N = the number of nodes, F = the number of full nodes, L = the number of leaves, and H = the
number of nodes with one child. Clearly, N = F + L + H . Further, the number of non-null refer-
ences in the tree is exactly N – 1 since each such pointer connects a node to its parent and every node
except the root has a parent. Thus N – 1= 2F + H. Subtracting yields 1= L – F so F + 1 = L.
18.7 As mentioned in the previous exercise, there are N – 1 non-null references in any tree. Since a binary
97

tree has 2N references, this leaves N + 1 null references. An M-ary tree has MN references, so
( M – 1 ) N + 1 of them are null .
18.8 This can be shown by induction. In a tree with no nodes, the sum is zero, and in a one node tree, the
root is a leaf at depth zero, and so the claim is true. Suppose the theorem is true for all trees with at
most k nodes. Consider any tree with k + 1 nodes. Such a tree consists of an i node left subtree and a k
– i node right subtree for some i. By the inductive hypothesis, the sum for the left subtree leaves is at
most one with respect to the left subtree root. Because all leaves are one deeper with respect to the
original tree root than with respect to the subtree, the sum is at most 1 / 2 with respect to the root.
Similar logic implies that the sum for the leaves in the right subtree is also at most 1 / 2, proving the
theorem. The equality is true if and only if there are n nodes with one child. If there is a node with one
child, the equality cannot be true, because adding a second child would increase the sum to higher
than 1. If no nodes have one child, then we can find and remove two sibling leaves, creating a new
tree. It is easy to see that this new tree has the same sum as the old. Applying the step repeatedly, we
arrive at a single node, whose sum is 1. Thus the original tree had sum 1.

In Practice
18.9
public static int numLeaves( BinaryNode t )
{
if( t == null )
return 0;
int lf = ( t.left==null && t.right==null ) ? 1 : 0;
return numLeaves(t.left) + numLeaves(t.right) + lf;
}

public static int numOne( BinaryNode t )


{
if( t == null )
return 0;
int on = ( t.left==null != t.right==null ) ? 1 : 0;
return numOne( t.left ) + numOne( t.right ) + on;
}

public static int numFull( BinaryNode t )


{
if( t == null )
return 0;
int fl = ( t.left!=null && t.right!=null ) ? 1 : 0;
return numFull( t.left ) + numFull( t.right ) + fl;
}

18.3 Exam Questions


18.1. Which of the following, as illustrated in this Chapter, is a binary tree?
a. Expression tree
b. Huffman coding tree
c. Unix file system
d. two of the above
e. none of (a), (b), and (c)
18.2. Which of the following traversals requires more than linear time in the worst case?
a. inorder
b. level order
c. postorder
98

d. preorder
e. all of these traversals are linear time
18.3. In which of the following traversals is the node processed after the recursive calls to the children are
complete?
a. inorder
b. level order
c. postorder
d. preorder
e. none of the above
18.4. What is the minimum number of nodes in a binary tree with L leaves?
a. log L
b. 2L – 1
c. 2L
d. 2 L
e. none of the above
18.5. Which of the following is true about the height of a node?
a. The height of a node is one less than the height of its parent
b. The height of an empty tree is 0
c. The height of a leaf is 0
d. The height of a tree can be larger than its depth
e. all of the above are false
18.6. The first child/next sibling implementation
a. allows easy access to the parent
b. is appropriate for binary trees
c. uses C references per node, where C is the number of children
d. all of the above
e. none of (a), (b), and (c)
18.7. Which traversal computes the total size of each directory in the Unix file system?
a. inorder
b. level order
c. postorder
d. preorder
e. two or more of the above traversals could be used
18.8. Let c(x) be the number of leaves in a binary tree rooted at t. Assume that isLeaf(t) returns 1 if t is
a leaf and 0 otherwise. Which of the following observations leads to a recursive implementation?
a. c(t)=c(t->left)+c(t->right)
b. c(t)=c(t->left)+c(t->right)+1
c. c(t)=c(t->left)+c(t->right)+isLeaf(t)
d. c(t)=c(t->left)+c(t->right)+isLeaf(t)+1
e. none of the above
18.9. In the inorder iterator, how many times is a node placed on the stack during a single traversal?
a. 0
b. 1
c. 2
d. 3
e. none of the above
18.10. Which traversal does not use a stack?
a. inorder
b. level order
99

c. postorder
d. preorder
e. all of these traversals use a stack
18.11. How many N node binary trees with items 1, 2, ..., N have identical preorder and inorder traversals?
a. 0
b. 1
c. N
d. N!
e. none of the above

The next four questions relate to the tree below:

18.12. Which of the following traversals yields ABCDE?


a. inorder
b. level order
c. postorder
d. preorder
e. two of the above
18.13. Which of the following is an inorder traversal of the tree?
18.14. The height of the tree is
a. 0
b. 1
c. 2
d. 3
e. none of the above
a. ABCDE
b. ABDCE
c. BDECA
d. EDCBA
e. none of the above
18.15. Suppose the postorder iteration is performed on the tree. At the time that D is output, what symbols
are still on the stack?
a. A only
b. A and B, only
c. A and C, only
d. A, B, and C
e. none of the above
18.16. Which of the following can be done in O(log N) time?
a. Find the height of a tree.
b. Find the number of leaves in a tree.
100

c. Find the number of full nodes.


d. Finding the number of nodes with one child.
e. none of the above
18.17. Given a node X, using the standard left child, right sibling representation, what would be the worst
case running time of an algorithm to find the parent of X, where H is the height of an N-node tree?
a. O( 1 )
b. O( H )
c. O( N )
d. O( log H )
e. none of the above

Answers to Exam Questions


1. D
2. E
3. C
4. B
5. C
6. E
7. C
8. C
9. C
10. B
11. B
12. E
13. E
14. C
15. C
16. E
17. C
CHAPTER

19 Binary Search Trees

19.1 Key Concepts and How To Teach Them

The focus of this chapter is the binary search tree and its balanced variants. B-trees are also discussed at the end
of the chapter, even though it is clearly not a binary search tree. The basic concepts are:
• the basic binary search tree
• order statistics
• AVL trees
• red-black trees
• AA-trees
• B-trees

19.1.1 The Basic Binary Search Tree


When I teach the binary search tree I sometimes begin by showing a decision tree for the binary search algorithm,
which in effect branches exactly like the binary search tree.This is one possible motivation for the structure.
Other times (usually when I am pressed for time), I just start with the basic definitions.
The code in the text uses recursion for the dynamic operations (such as insertion, deletion), and implements search-
ing nonrecursively. Thus, you need to show the recursive view of a binary search tree as either empty or a root with
two trees. Once again I have public drivers calling private recursive routines.
Students don’t seem to have problems understanding the basic binary search tree algorithms. The most difficult cod-
ing problem concerns the return of the new BinaryNode object in the recursive insert , removeMin , and remove
routines. You will need to explain this; this is mentioned in the text on pages 609–611. Line 22 in Figure 19.12 is a
concise use of the ?: operator. It’s the same as an if/else.

19.1.2 Order Statistics


This can be skipped if you want to. I tend to discuss either this or lazy deletion as simple extensions to the basic
binary search tree. (It is used in Chapter 13 only for the Josephus case study.)

19.1.3 AVL Trees, Red-black Trees, and AA-trees


You will almost certainly want to cover balanced search trees in some form. I usually spend one lecture and discuss
AVL trees. If you have more time, you can pick one of the others.
My feeling is that although the AVL tree is no longer used much, besides being of historic interest, it is easy to dis-
cuss and is thematic of the operations used in almost all balanced search tree schemes. In fact, I tell that to the class
and urge them to at least skim through the rest of the chapter. For all the balanced search tree schemes, my class
presentation follows the text almost exactly.
If you want to cover red-black trees or AA-trees but not AVL trees first, all you will need to do is discuss the basics
of tree rotations.
If you cover AVL trees and do not want to get into the details of the red-black trees or AA-trees, you should men-
tion their existence and point the students towards them. AA-trees are a variation of BB-trees that are somewhat sim-
102

pler because there are fewer cases.

19.1.4 B-trees
This takes one lecture for me to cover. If you are going to do an example on the board, be sure to tell your students
to rotate their papers (so it is 8.5 inches high and 11 inches wide) and have them start drawing from the bottom. The
presentation in the text is exactly how I do it in class.

19.2 Solutions To Exercises


In Short
19.1 To conserve space, I list the trees by supplying a preorder traversal. Since an inorder traversal is given,
this can be used to construct the tree. For this example, the resulting tree (shown below) would be
31246597; after the deletion it is 4126597 if the smallest node in the right subtree is used to replace
the root.

19.2 In parentheses I list the number of possible insertion sequences. 1234 (1), 1243 (1), 1324 (2), 1423
(1), 1432 (1), 2143 (3), 2134 (3), 3214 (3), 3124 (3), 4321 (1), 4312 (1), 4213 (2), 4123 (1), 4132 (1).
19.3 Only one tree is possible: 213.
19.4 Only four trees are possible, and each are equally likely: 2134 (6), 2143 (6), 3124 (6), and 3214 (6).
19.5 For the AVL tree, 42136597. For the red-black tree, 21543769. The red nodes are 5, 3, 6, 9.
19.6 For three nodes, only one tree is possible: 213. However, in four instances nodes 1 and 3 are red,
while in two instances they are both black. In all six cases, the root is black. The four node case is left
to the reader.

In Theory
19.7 When there are zero nodes, the internal and external path lengths are equal. Observe that when a node
is added to a binary search tree at depth d, the internal path length increases by d. The external path
length increases by 2( d + 1 ) because of two new null references, but decreases by d (because a
null reference is converted to the new node), for a new increase of d + 2. Thus each insertion into the
tree increases EPL( T ) – IPL( T ) by 2. After N inserts, EPL( T ) – IPL( T ) = 2N.
19.8 The tree that results is perfectly balanced (there is only one such tree for N = 2k – 1 nodes). This can
be proved by induction. It is easy to verify the claim for 1 ≤ k ≤ 3. Suppose it is true for k = 1, 2, 3, ...,
h . Then after the first 2h – 1 insertions, we know by the induction hypothesis that 2h – 1 is at the root,
and the right subtree is a balanced tree containing 2h – 1 – 1 through 2h – 1. Each of the next 2h – 1 inser-
tions, namely 2h through 2h + 2h – 1 –1 insert a new maximum and get placed in the right subtree, even-
tually forming a perfectly balanced right subtree of height h – 1. This follows by the induction hypoth-
esis because the right subtree may be viewed as being formed from the successive insertion of 2h – 1 +
1 through 2h + 2h – 1 –1. The next insertion forces an imbalance at the root, and creates a perfectly bal-
anced left subtree of height h – 1. The new key is attached to the perfectly balanced right subtree of
height h – 2 as the last node in the right path. Thus the right subtree is exactly as if the nodes 2h + 1
through 2h + 2h – 1 were inserted in order. By the inductive hypothesis, the subsequent successive inser-
tions of 2h + 2h – 1 +1 through 2h + 1 – 1 will create a perfectly balanced right subtree of height h – 1.
Thus after the last insertion, both the left and the right subtrees are perfectly balanced, and of the same
height, so the entire tree of 2h + 1 – 1 nodes is perfectly balanced (and has height h).
19.9 A deletion algorithm is provided in Knuth, Volume III.
103

19.10 Let B equal the number of black nodes on the path to the bottom. It is easy to see by induction that
there must be at least2B – 1 black nodes in the tree. Let H be the height of the tree. Since consecutive
red nodes are not allowed, B ≥ È H / 2 ˘. Thus 2 È H / 2 ˘ – 1 ≤ 2B – 1≤ N. Consequently,
H ≤ 2log ( N + 1 ). As an example of a bad case which we call BAD ( B ), let MIN ( B ) be the
red–black tree of fewest nodes with B nodes on the path to the bottom. This is a perfect tree of 2B – 1.
Construct a bad tree as follows: the root is red, the left child is a MIN ( B ) tree and the right child is
black. For this black child, its left child is a MIN ( B – 1 ) tree and its right child is a BAD ( B – 1 )
tree. A base case is a complete 7 node tree with black nodes on the middle level. Let T(H) be the num-
ber of nodes in a bad tree of (odd) height h. T(3) = 7 and T ( H ) = T ( H – 2 ) and 2 Î H / 2 ˚ +
2 Î H / 2 ˚ – 1. The solution of this recurrence is T ( H ) = 3 · 2 È H / 2 ˘ + 1, so H = 2 ( log( N – 1 ) – log3)
+ 1.
19.11 Color a nonroot node red if its height is even and its parent’s height is odd; otherwise color a nonroot
node black. This coloring satisfies all the red-black properties. Not all red-black trees are AVL trees,
since the deepest red-black tree is deeper than the maximum height proven for an AVL tree.
19.13 Since 8 bits can store heights up to 127 (unless tricks are used to reuse the negative numbers), and an
AA-tree has the same height properties as a red–black tree, trees of roughly 263 nodes can be stored.

In Practice
19.17 It avoids some additions in the searching routine.
19.18 The method below performs an inorder traversal but tests to make sure that a recursive call is not
made in vain (implementation of a public driver is left to the reader). As a result, when these tests fail
and a recursive call is not made, the effect of the routine is the same as if the portion of the subtree
that would be visited did not exist. As a result, the nodes that are actually visited are the K nodes that
are output plus the nodes on the path to the root of the range that is output. This additional path has
average length of O( log N ), giving an average running time of O ( K + log N ).

public void printInRange( BinaryNode t Comparable low, Comparable high )


{
if( t != null )
{
if( t.element.compares( low ) >= 0 )
printInRange( t.left, low, high );
System.out.println( t.element );
if( t.element.compares( high ) <= 0 )
printInRange( t.right, low, high );
}
}
19.19 The public driver is assumed already written.

BinaryNode buildTree( int low, int high )


{
if( low > high )
return null;

int middle = ( low + high ) / 2;


BinaryNode newNode = new BinaryNode( new MyInteger( middle ) );
newNode.left = buildTree( low, middle - 1 );
newNode.right = buildTree( middle + 1, high );
newNode.size = high - low + 1;
return newNode;
}
104

19.20
BinaryNode doubleRotateWithLeftChild( BinaryNode k3 )
{
BinaryNode k1 = k3.left;
BinaryNode k2 = k1.right;

k1.right = k2.left;
k3.left = k2.right;
k2.left = k1;
k2.right = k3;
return k2;
}

Programming Projects
19.23 The basic problem is that the minimum element in the tree may be marked deleted, as may many of
the next smallest elements. A recursive implementation of findMin is easiest.

BinaryNode findMin( BinaryNode t )


{
if( t == null )
return null;

BinaryNode lt = findMin( t.left );


if( lt != null )
return lt;

if( t.count != 0 ) // try current item


return t;
return findMin( t.right );
}

19.3 Exam Questions


19.1. Approximately what is the maximum height of a binary search tree of N nodes?
a. log N
b. 1.38 log N
c. 1.44 log N
d. 2 log N
e. none of the above
19.2. The following items are inserted into a binary search tree: 8, 4, 3, 2, 5, 9, 6, 1, 7. Which item is placed
at a root?
a. 1
b. 4
c. 8
d. 9
e. none of the above
19.3. The following items are inserted into a binary search tree: 3, 6, 5, 2, 4, 7, 1. Which node is the deep-
est?
105

a. 1
b. 3
c. 4
d. 7
e. none of the above
19.4. In a tree with N nodes, what is the value of EPL(T) – IPL(T)?
a. 1
b. N
c. N + 1
d. 2N
e. none of the above
19.5. Which of the following trees can have depth that is not logarithmic?
a. AA tree
b. AVL tree
c. B-tree of order 4
d. red-black tree
e. all of the above trees must have logarithmic depth
19.6. If only good average-case, as opposed to worst-case, performance is required, which of the following
is stored in each node in the Ordered-SearchTree ?
a. coloring information
b. its depth
c. its height
d. its size
e. none of the above
19.7. Which of the following statements is true about deleting the root of a binary search tree?
a. the root reference always changes
b. the root reference changes if it does not have two children
c. if the root has two children, its item is replaced by the largest element in the right subtree
d. all of the above
e. none of (a), (b), and (c)
19.8. For an insertion of a single item into an AVL tree, the maximum number of rotations (double rotations
count as one rotation) is
a. 1
b. 2
c. approximately log N
d. approximately 1.44 log N
e. none of the above
19.9. The following items are inserted into an AVL tree: 1, 2, 3, 8, 6. How many rotations are performed?
a. no rotations
b. 1 single rotation only
c. 1 double rotation only
d. 1 single rotation and 1 double rotation
e. none of the above
19.10. Items 7, 3, 11, 9, and 13 are inserted into an AVL tree. What happens when 12 is inserted?
a. no rotation is needed
b. a single rotation between some node and its left child is performed
c. a single rotation between some node and its right child is performed
d. a double rotation with a node, its left child, and a third node is performed
e. a double rotation with a node, its right child, and a third node is performed
19.11. Which of the following is not a red-black tree property?
a. the root is black
b. all leaves are black
c. consecutive red nodes are disallowed
d. every path from a node to an external node must contain the same number of black nodes
e. all of the above are red-black tree properties
19.12. What is stored in the header node in the red-black tree?
a. A key of negative infinity
b. A right pointer that points to the real root
c. A left pointer that points to the real root
d. (a) and (b)
e. all three of (a), (b), and (c)
19.13. Which data structure uses Skew and Split ?
a. AA-tree
b. AVL tree
c. B-tree
d. red-black tree
e. none of the above
19.14. Which of the following data structures has the strongest height guarantee?
a. AA-tree
b. AVL tree
c. B-tree of order 3
d. B-tree of order 4
e. red-black tree
19.15. Skew is implemented as a
a. single rotation with a left child
b. single rotation with a right child
c. double rotation with a left child and a third node
d. double rotation with a right child and a third node
e. triple rotation
19.16. Suppose a disk block stores 8192 bytes and the basic key size is 96 bytes. Assuming that child refer-
ences cost 4 bytes, what is the correct choice of M for a B-tree?
a. 81
b. 82
c. 85
d. 96
e. none of the above
19.17. Let lh(n) and rh(n) denote the height of the left and right subtrees respectively of a node n. Which of
(a)–(c) is false for an AVL tree T?
a. If for all nodes n, lh(n) = rh(n) , then deletion of a leaf node will not require any rebalancing.
b. If for all nodes n, lh(n) = rh(n) except for one node x for which lh(x) = rh(x) + 1 then adding a
node to right subtree of x will not require any rebalancing.
c. If for all nodes n, lh(n) = rh(n) except for one node x for which lh(x) = rh(x) + 1 then deleting a
node from the left subtree of x will not require any rebalancing.
d. all of the above are false
e. all of the above are true
19.18. Which of the following operations will require most time in a balanced binary search tree?
a. findMin
b. findMax
c. Find the least 3 elements.
d. Find all elements at depth log log N
107

e. all of the above require equal amount of time

Answers to Exam Questions


1. E
2. C
3. C
4. D
5. E
6. D
7. B
8. A
9. D
10. C
11. B
12. D
13. A
14. D
15. A
16. A
17. E
18. E
108

CHAPTER

20 Hash Tables

20.1 Key Concepts and How To Teach Them

This is a single topic chapter: Hash tables. Generally, I avoid the details of the analysis, but if you have time and
a mathematically sophisticated class, you can try it. I try to do the material in two classes, but it can be squeezed
into one if you hurry. I discuss things in the same order as the chapter:
• basic ideas
• hash function
• linear probing
• quadratic probing
• other implementations

20.1.1 Basic Ideas


I like to begin by explaining the limitations of the binary search tree, namely the problem with sorted input and the
practical annoyance of balancing the tree. I also like to show that constant time per operation is doable, by giving
as an example the bit array. I follow the text pretty closely.

20.1.2 Hash Function


You may want to review what the mod operator means. An amazing number of students think that n%11 is 0 if n is
less than 11. The implementation of the hash function is relatively simple for the students. I like to show them the
bad functions too, and then discuss the Horner’s rule computation that forms the basis of the good hash function.
Make sure the students understand that if overflow is allowed, a negative number may result and must be converted
into the proper range.

20.1.3 Linear Probing


Most students understand linear probing pretty well. One thing they forget is that a table of size n goes from 0 to
n-1 , and that the hash function tells you what n is. Explain that lazy deletion must be used. They usually see it
after you tell them.
I generally do not do the analysis, but I do mention what primary clustering is and why it occurs and I do give the
formulas for the expected number of cell probes.

20.1.4 Quadratic Probing


My discussion of quadratic probing follows the text. I generally see two problems (in addition to some of those men-
tioned above). First, some students will compute the new position as i2 past the previous position instead of 2i – 1
from the previous position. This is in effect cubic probing and does not work (though it will appear to). Second,
some students compute the hash function, and if there is a collision, they try locations 1, 4, 9, instead of 1, 4, 9 away
from the original probe.
Don’t forget to make sure the students know that the table size must be prime. They can use the simple primality
test in Figure 9.7. Also make sure they remember that the load factor must be no larger than 0.5. Finally, you must
109

explain how rehashing works.

20.1.5 Other Implementations


If I have the time, I discuss secondary clustering and mention double hashing. It does not appear to me that double
hashing is faster in practice than quadratic probing. Ditto for separate chaining, which I always mention, but never
implement. You should mention that the load factor for separate chaining can be larger than 1, and it is not as cru-
cial for performance as in probing tables.

20.2 Solutions To Exercises


In Short
20.1 0 through 10.
20.2 23, because the load factor should be less than 0.5 and the table size should be prime.
20.3 Lazy deletion must be used: The items are marked deleted.
20.4 The cost of an unsuccessful search when the load factor is 0.25 is 25/18. The cost of a successful
search is 7/6.
20.5 The hash tables are shown in Figure 20.1.
20.6 When rehashing, we choose a table size that is roughly twice as large, and prime. In our case, an
appropriate new table size is 19, with hash function hash( x ) = x mod 19.

Figure 20.1 (a) linear probing; (b) quadratic probing; (c) separate chaining

a. The new locations are 9679 in bucket 8, 4371 in bucket 1, 1989 in bucket 13, 1323 in bucket 12,
6173 in bucket 17, 4344 in bucket 14 because both 12 and 13 are already occupied, and 4199 in
bucket 0.
b. The new locations are 9679 in bucket 8, 4371 in bucket 1, 1989 in bucket 13, 1323 in bucket 12,
6173 in bucket 17, 4344 in bucket 16 because both 12 and 13 are already occupied, and 4199 in
bucket 0.
c. The new locations are: 4371 in list 1, 6173 in list 17, 1323 in list 12, 4344 in list 12, 1989 in list
13, 9679 in list 8, and 4199 in list 0.

In Theory
20.8 It is true. However, that does not mean that the expected cost of the last insertion is obtained by plug-
110

ging in 0.375 for the load factor. Instead, we must average the insertion cost over all load factors from
0.25 to 0.5 as

.
20.9 As in the previous example, we want to compute the average cost of inserting an element into the new
table. This is the same as the cost of an average successful search in the new table with load factor
0.25, namely 1.167. Thus the expected number of probes in the insertion sequence is 1.167N.
20.10 (a) The expected cost of an unsuccessful search is the same as an insertion;
(b)

20.11 (a) The hash table size is a prime near 25,013. (b) The memory usage is the memory for 10,000
String objects (at 8 bytes plus additional storage to maintain members of the String class (includ-
ing the length, etc., and depends on the specific implementation)). (c) The memory usage for the hash
table is one reference and a boolean per array item. Likely, this is 8 bytes for each array item, or
approximately 200,000 bytes. (d) Add all this up for the total memory requirements. (e) The space
overhead is the number in part (d) minus the number in part (b) and is probably considerable.

In Practice
20.12 Use the following version of findPos :

private int findPos( Object x )


{
int currentPos = ( x == null ) ?
0 : Math.abs( x.hashCode( ) % array.length );
while( array[ currentPos ] != null )
{
if( x == null )
{
if( array[ currentPos ].element == null )
break;
}
else if( x.equals( array[currentPos].element ) )
break;
if( ++currentPos == array.length )
currentPos = 0;
}
return currentPos;
}

20.3 Exam Questions


20.1. What is the range of values computed by the hash function hash( x )= x mod 100?
a. 0 to 99
b. 0 to 100
c. 1 to 99
d. 1 to 100
e. none of the above
20.2. Which of (a) to (d) is false: The size of a hash table
a. should be a power of 2 for quadratic probing
b. should be a prime number for linear probing
c. should be about 2N for quadratic probing
d. should be about N for separate chaining
e. two or more of the above are false
20.3. How are elements deleted in linear probing?
a. the element reference array[i].element is made null
b. the HashEntry reference array[i] is made null
c. they are changed to zero
d. they are marked deleted
e. none of the above
20.4. Suppose we are implementing quadratic probing with a hash function hash( x ) = x mod 100. If an ele-
ment with key 4592 is inserted and the first three locations attempted are already occupied, then the
next cell that will be tried is
a. 0
b. 1
c. 9
d. 95
e. none of the above
20.5. In a separate chaining hash table with load factor λ = 1.25 , what is the average length of a list?
a. 0.8
b. 1.0
c. 1.25
d. there is not enough information
e. there is enough information, but none of the above are correct
20.6. Which of the following costs are equal in a hash table?
a. insertion and successful search
b. insertion and unsuccessful search
c. successful search and unsuccessful search
d. insertion, successful search, and unsuccessful search
e. none of the above
20.7. Which of the following statements about quadratic probing is true (expensive does not include trivial
operations such as multiplication or division by powers of 2; hash function computation is not
included in the cost)?
a. an expensive division must be performed
b. an expensive mod operator must be performed
c. an expensive multiplication must be performed
d. all of the above
e. none of (a), (b), and (c)
20.8. Linked lists are used in
a. double hashing
b. linear probing
c. quadratic probing
d. separate chaining
112

e. all of the above


20.9. Primary clustering occurs in
a. linear probing
b. quadratic probing
c. separate chaining
d. all of the above
e. none of (a), (b), and (c)
20.10. Rehashing can be used in
a. linear probing
b. quadratic probing
c. separate chaining
d. all of the above
e. none of (a), (b), and (c)
20.11. We need to store information regarding 100 students in a class. Let d1 and d2 denote the first and sec-
ond digits of a student id respectively. Which one of the following is the best hash function for a table
of size 200?
a. d1 + d2
b. 2(10d1 + d2)
c. 20d1 + d2
d. 10d1 + d2 + 50
e. All are equally good
20.12. In separate chaining, an alternative is to use an array of binary search tree. Which of (a)–(c) is false if
the load factor is L?
a. The expected number of probes for insertion is log L.
b. The expected number of probes for unsuccessful search is L
c. The average number of entries in each tree is L.
d. all of the above are true
e. all of the above are false

Answers to Exam Questions


1. A
2. A
3. D
4. B
5. C
6. B
7. E
8. D
9. A
10. D
11. B
12. B
113

CHAPTER

21 A Priority Queue: The Binary Heap

21.1 Key Concepts and How To Teach Them


The main topic of this chapter is the binary heap, and I usually spend two lectures on it. There are two secondary
topics, namely heapsort and external sorting. How you handle those two topics depends on when you cover sorting.
If you do sorting after data structures (as many people do), then heapsort is a great lead-in into sorting algorithms.
When I teach this way, I do heapsort, and then go directly to quicksort (stopping to do mergesort or shellsort). If
you do sorting early, then heapsort emerges as an interesting alternative that, like Shellsort, is easy to implement.
External sorting seems to me to be an interesting algorithmic topic, but I am usually too pressed for time to get to
it.

21.1.1 Binary Heap and Heapsort


I like to begin by explaining that the structure should have properties that are better than a binary search tree but
worse than a queue. Then I describe the structure property, and show the implicit mapping. The structure property
is natural because we know we are looking for logarithmic worst case, and the complete tree does it.
I describe the heap-order property by first showing that the minimum must be in some special place. The three pos-
sibilities are the root, last node, and leftmost child. The last two are dismissed because the second smallest element
must also be near the minimum for updating. For the leftmost child, there’s no obvious way to do that. For the last
node, that places the next smallest at the next to last node, and continuing this way, we arrive at a reverse-ordered
structure, which will not be easily updated during an insertion. Mention that as long as each operation is along a
path from the root to a leaf, the result will be logarithmic.
Once we’ve settled on the heap-order property, I draw a few heaps, and then describe insertion and minimum dele-
tion with several examples. The running time is immediately clear to most of the class. I usually do not describe the
toss routine until coding begins. buildHeap is discussed below. I don’t talk about decreaseKey and merge ; you
can do it if you plan on looking at Chapter 23.
I think the linear-time heap construction is one of the more interesting algorithms so I always cover it. One moti-
vation is to show that the binary heap can be used to provide an O( N log N ) sort. Then I mention that the process
of inserting items can be done with tosses and a fixheap. Other motivations, such as the initial group of processes
in the scheduling priority queue, are possible.
After many different approaches, I’ve decided that I like showing the recursive algorithm and then replacing it with
a different iterative algorithm as is done in the text. I also prove the linear time bound by using the marking algo-
rithm because the students do not relate to the deeper math. If you have more mathematically sophisticated students,
you can do the direct summation.
After the time bound is done, simply mention that the sorting algorithm described uses an extra array but that it is
avoidable by reusing space.

21.2 Solutions To Exercises


In Short
21.1 The structure property is that a heap is a complete binary tree — all nodes are present as we go from
top to bottom, left to right. The ordering property is that the value stored in a node’s parent is no larger
than the value stored in a node.
21.2 The parent is in location Îi / 2˚, the left child is in location 2i , and the right child is in location 2i + 1.
21.3 The resulting heaps are shown in Figure 21.1.
21.4 In the percolateDown(3) step, the percolation could have gone one level deeper.
21.5 Reverse the heap-order property, change the direction of the element comparisons, and place infinity
as the sentinel.
21.6 See Figure 21.2.
21.7 Heapsort is not stable.

Figure 21.1 Sequential insertion (left); after buildHeap (right) Solutions To Exercises

Figure 21.2 Exercise 21.6 after two deleteMin s: Starting from sequential insertion (left); starting from
buildHeap (right)

In Theory
21.8 (a) 4 times as large; (b) The array must then have size O( N2 ); (c) O( N4.1 ); (d) O( 2N ).
21.9 (a) Any nonleaf node is a parent of a leaf and cannot be larger than the leaf. (b) Follows from earlier
results on binary trees. (c) Let all leaf nodes except the unexamined leaf have positive value 3x, and let
all the leaf parents have the value x. If the unexamined leaf is not declared the maximum, the answer
is wrong if it has value 4x. If the unexamined leaf is declared the maximum, the answer is wrong if it
has value 2x. Thus no matter what answer is output, there is an input for which the algorithm will fail.
21.10 (Note that this question applies for complete trees). The summation is solved by letting S be the sum
, letting 2S be twice the sum, and subtracting. As a result, many terms cancel and what
is left is a geometric sum that is evaluated by a standard formula.
21.11
21.12 Observe that for N = 0 and N = 1 , the claim is true. Assume that it is true for values of k up to and
including N– 1. Suppose the left and right sub-trees have L and R nodes respectively. Since the root
has height Î log N ˚, we have

H ( N ) = Î log N ˚ + H ( L ) + H ( R )
= Î log N ˚ + L – v ( L ) + R – v ( R )
= N – 1 + ( Î log N ˚ – v ( L ) – v ( R ) )

The second line follows from the inductive hypothesis, the third follows because L + R= N – 1. Now
115

the last node in the tree is either in the left sub + tree or the right subtree. If it is in the left subtree,
then the right subtree is a perfect tree, and v ( R ) = Î log N ˚ – 1. Further, the binary representation of
N and L are identical with the exception that the leading 10 in N becomes 1 in L. (For instance if N =
37 = 100101, L = 10101.) It is clear that the second digit of N must be zero if the last node is in the
left subtree. Thus in this case, v ( L ) = v ( N ), and H ( N ) = N – v ( N ).
If the last node is in the right subtree, then v ( L ) = Î log N ˚. The binary representation of R is identi-
cal to N except that the leading 1 is not present. (For in stance if N = 27 = 101011, L = 01011 ). Thus
in this case, v ( L ) = v ( N ) – 1, and again, H ( N ) = N – v ( N )
21.13 The leading term is 2N log N because the number of comparisons in a deleteMin is 2log N.
21.14 See the paper in the references by Schaffer and Sedgewick.
21.15 The left child is at r + 2( i – r ) + 1, right child is at r + 2( i – r ) + 2 and the parent is at
r + ( i – r ) / 2 – 1 if i is even and r + ( i – r – 1 ) / 2 if i is odd.
21.17 (a) Create a new node with the root of the combined heap. Remove the rightmost element of the rhs
and put at the root. Then, percolate the root to its correct position. (b) Merge the left subtree of lhs
with rhs as described in (a). Then, percolate the root to its correct position. (c) If l > r, follow the left
links l – r times from the root to reach a subtree T of size 2r – 1. Merge – this subtree with rhs . Then
percolate each of the nodes in the path from the root to the root of the merged subtree.
21.18 insert takes O ( logdN ).deleteMin takes O ( dlog dN ).

In Practice
21.23
private static void percDown( Comparable [ ] a, int i, int N )
{
int child;
Comparable tmp = a[ i ];

for( ; i * 2 + 1 <= n; i = child )


{
child = i * 2 + 1;
if( child != n && a[ child ].lessThan( a[ child + 1 ] ) )
child++;
if( tmp.lessThan( a[ child ] ) )
a[ i ] = a[ child ];
else
break;
}
a[ i ] = tmp;
}

21.3 Exam Questions


21.1. Every node in a (min) binary heap
a. has two children
b. is no larger than its children
c. is no smaller than its children
d. has a smaller left child than right child
e. two or more of the above
21.2. If an element in a binary heap is stored in position iand the root is at position 1, then where is the par-
ent stored?
116

a. Îi/2˚
b. Èi/2˘
c. 1+Îi/2˚
d. 2i
e. 2i + 1
21.3. The running time of fixHeap is
a. O( N ) worst case andO( N ) average case
b. O( N ) worst case andO( log N ) average case
c. O( N ) worst case andO( N log N ) average case
d. O( N log N ) worst case and O( N ) average case
e. O( N log N ) worst case and O( N log N ) average case
21.4. N elements are inserted one by one into an initially empty binary heap. The total running time is
a. O( N ) worst case andO( N ) average case
b. O( N ) worst case andO( log N ) average case
c. O( N ) worst case andO( N log N ) average case
d. O( N log N ) worst case and O( N ) average case
e. O( N log N ) worst case and O( N log N ) average case
21.5. How much extra space is used by heapsort?
a. O( 1 )
b. O( log N )
c. O( N )
d. ON
e. none of the above
21.6. Which sorting algorithm has the same average and worst case time bounds (in Big-Oh) as heapsort?
a. insertion sort
b. mergesort
c. quicksort
d. shellsort
e. none of the above
21.7. Heapsort uses
a. percolate up only
b. percolate down only
c. both percolate up and percolate down
d. neither percolate up nor percolate down
21.8. Which of the following data structures uses a sentinel?
a. binary heap
b. hash table
c. queue
d. stack
e. none of the above use sentinels
21.9. A node with key 8 has a left child with key 10. Which of the following objects could this node be
found in?
a. binary search tree
b. max heap
c. min heap
d. two of the above
e. none of (a), (b), and (c)
21.10. Percolate up and down are used for
a. AVL trees
b. B-trees
c. circular queue
d. binary heaps
e. none of the above
21.11. What is the basic algorithm used for external sorting?
a. finding the median
b. merging
c. selection
d. all of the above
e. none of (a), (b), and (c)
21.12. Replacement selection is
a. arranging the initial runs on the tape in an optimal way
b. constructing the runs so they have expected length 2M
c. using K-way merging instead of 2-way merging
d. using K + 1 tapes instead of K tapes
e. none of the above
21.13. What is the worst case running time for finding the maximum element in a minheap?
a. O( log N )
b. O( N )
c. O( log2N )
d. O( 1 )
e. none of the above

Answers to Exam Questions


1. C
2. A
3. A
4. D
5. A
6. B
7. B
8. A
9. C
10. D
11. B
118

12. B
13. B
119

CHAPTER

22 Splay Trees

22.1 Key Concepts and How To Teach Them


At the CS-2 level, it is reasonable to cover splay trees if you have time, since the basic concepts are not that much
more difficult than AVL trees or red-black trees.
Begin by explaining the limitations of traditional balanced search trees and explain what amortization is and why it
is useful. You’ll need to then explain that for binary search trees, for a decent amortized bound to occur, you’ll need
self-adjustment, though self-adjustment does not guarantee a good bound. You can work through the rotate-to-root
example. Next, show the basic bottom-up splay and do a few examples. The deletion algorithm is particularly nifty.
Emphasize that a splay must occur on EVERY access, even unsuccessful ones.
The proof is well beyond the CS-2 level for most universities and should be skipped unless you have an exception-
ally gifted class. You can show the top-down splay tree algorithm, but you won’t be able to explain why the partic-
ular rotations are chosen. You will be able to confirm, however, that the algorithms preserve search tree order. The
top-down code is present because I wanted to provide a practical implementation as reference material. You can have
students use the code and compare the splay trees with other search trees and priority queues.

22.2 Solutions To Exercises


In Short
22.1 For the bottom-up splay, the preorder traversal yields 8, 6, 5, 2, 1, 4, 3, 9 (as in Chapter 18, this
defines a unique binary search tree). For the top-down splay, the result is 8, 6, 4, 2, 1, 3, 5, 9.
22.2 For the bottom-up splay tree, deletion of the 3 requires that it be splayed to the root and deleted.
The left subtree’s maximum is splayed to the root (simple in this case) and then the right subtree is
attached to the left subtree’s new root as its right subtree. The result is 2, 1, 8, 5, 4, 6, 9. For the top-
down splay tree, the result is 2, 1, 6, 4, 5, 8, 9.

In Theory
22.3 This result is shown in the Sleator and Tarjan paper.
22.4 This is easily shown by induction.
22.5 No. Consider a worst-case chain of right children. If the splaying access is on the root, and the non-
splaying access on the deep node, then the cost of the two accesses is O( N ), and this can be repeated
forever.
22.6 (a) 523776; (b) 262166, 133114, 68216.
120

22.7 This result is shown in the Sleator and Tarjan paper.

In Practice
22.8 deleteMin is implemented by searching for negative infinity (bringing the minimum to the root) and
then deleting the item at the root. Note that findMin is not a constant member function.
22.9 Add a field to store the size of a tree. Note that the size changes during a rotation and must be main-
tained there in addition to during the insertion and deletion.

22.3 Exam Questions


22.1. In addition to the data and left and right references, what is stored in each node of a splay tree?
a. a color
b. the node’s height
c. the node’s level
d. the node’s parent
e. none of the above
22.2. What is the amortized cost of a basic binary search tree operation using rotate-to-root?
a. O ( 1 )
b. O( log N )
c. O( N )
d. O( N log N )
e. none of the above
22.3. An access of a splay tree of N nodes results in a completely identical tree. For how many different
nodes would this be possible?
a. 0
b. 1
c. 2
d. N – 1
e. none of the above
22.4. What is the worst-case height of a splay tree?
a. log N
b. 1.38 log N
c. 2 log N
d. N – 1
e. N
22.5. Which of the statements (a) to (d) about splay trees is false?
a. a single access operation could examine every node in the tree
b. any N consecutive operations from an initially empty splay tree must take at mostO( N log N )
time
c. inserting the items 1, 2, ..., N into an initially empty splay tree takesO( N ) total time.
d. the most recently accessed item is at the root
e. none of (a) to (d) is false
22.6. Which of the following splay tree rotations in effect distinguishes it from rotate-to-root?
a. zig only
b. zig-zag only
c. zig-zig only
d. zig-zig and zig-zag only
e. all of the above
22.7. What item is at the root after the following sequence of insertions into an empty top-down splay tree:
8, 4, 6, 5, 7, 9, 2?
a. 2
121

b. 4
c. 8
d. 9
e. none of the above
22.8. How is deletion performed in a top-down splay tree?
a. If the node is found, it is replaced with the smallest node in its right subtree, which itself is recur-
sively deleted.
b. If the node is found, it is replaced with the largest node in its left sub-tree, which itself is recur-
sively deleted.
c. A single splay is performed which places the deleted node in a leaf; that node is then easily
removed
d. A single splay is performed which places the deleted node at the root; it is deleted and the two
subtrees are reattached by using a second splay
e. none of the above
22.9. In a splay tree, how is the rank of a node stored?
a. an extra array stores the information
b. a linked list stores the information
c. directly, in each node
d. indirectly, by storing the size in each node
e. the rank is not stored at all
22.10. Which of the following alternatives preserves the logarithmic amortized time bound for the splay tree?
a. do not splay on unsuccessful searches
b. do not splay if an access path has fewer than log N nodes
c. replace the zig-zig with two single rotations (bottom-up)
d. splay on every other access
e. none of the above
22.11. Which of the following operations does not involve a splay?
a. find
b. deleteMin
c. insert
d. all of the above operations require a splay
e. find requires a splay only if it is successful.
22.12. Consider a database in which 90% of the operations are insert s and 10% of the operations are find s.
Which of the following data structures would not exhibit quadratic worst-case behavior for the
sequence of N operations?
a. Splay tree
b. Binary search tree
c. Unsorted Linked List
d. Sorted ArrayList
e. all of the above exhibit quadratic behavior for this sequence

Answers to Exam Questions


1. E
2. C
3. B
4. D
5. E
6. C
7. A
8. D
9. E
10. B
11. D
12. A
123

CHAPTER

23 Merging Priority Queues

23.1 Key Concepts and How To Teach Them


The material in this chapter is not as complex as it would appear because the merging algorithms are fairly simple
in principle. You can cover either data structure. I recommend avoiding the analysis of the skew heap, and it is not
really worthwhile to examine the details of the pairing heap implementation.
I teach the material exactly as it is written in the text.

23.2 Solutions To Exercises


In Short
23.1 The resulting skew heaps are shown in Figure 23.1.
23.2 (a) the root is 1 and all other nodes are children in the following order: 7, 6, 5, 4, 3, 2. (b) The root is
1 and all other nodes are children in the following order: 2, 7, 6, 3, 5, 4.
23.3 See Figure 23.2 and Figure 23.3.

Figure 23.1 After skew heap insertions 1, 2, 3, 4, 5, 6, 7 (left);


after skew heap insertions 4, 3, 5, 2, 6, 7, 1 (right)

Figure 23.2 Skew heaps in Figure 23.1 after two deleteMin s each
Figure 23.3 Pairing heap that results from two deleteMin s in pairing heaps of Exercise 23.2

In Theory
23.4 Insert the sequence N, N + 1, N – 1, N + 2, N – 2, N + 3, ……, 2N into an initially empty skew heap.
The right path has N nodes, so any operation could take Ω ( N ) time.
23.5 We implement decreaseKey(x) as follows: if lowering the value of x creates a heap order violation,
then cut x from its parent, which splits the skew heap into two. merge these heaps. The cost is
O( log N ) for the merge plus the initial increase in potential created by the cut. This increase is only-
log N because at most that many nodes in the original heap can become heavy as the result of losing a
portion of its left subtree. (This last claim requires a short proof by induction).
23.6 Break the skew heap into N single node heaps and then use the normal lin-ear-time binary heap algo-
rithm. Alternatively, place these N single node heaps on a queue, and repeatedly dequeue two heaps
and enqueue the result until only one final merged heap remains. One can verify that the result is a
linear-time algorithm.
23.7 The basic algorithm modifies the skew heap by swapping left and right children only when needed to
restore the balancing condition. Because the right path length will be logarithmic, the worst case for
each operation will be logarithmic.
23.8 Consider the pairing heap with 1 as the root and children 2, 3, ..., N. A deleteMin removes 1, and the
resulting pairing heap has 2 as the root with children 3, 4, ..., N; the cost of this operation is N units. A
subsequent deleteMin sequence of 2, 3, 4, ... will take a total time Ω ( N2 ).
23.9 After increasing the value at a node, cut all the children and add them as children of the root. The cost
is comparable to a deleteMin .

23.3 Exam Questions


23.1. Which of the following is true about the skew heap?
a. it is balanced
b. each node stores nothing besides an item and two references
c. the right path contains at most a logarithmic number of nodes
d. two of the above
e. all of (a), (b), and (c)
23.2. Which of the four operations below can be used to implement the other three for the skew heap?
a. decreaseKey
b. deleteMin
c. insert
d. merge
e. none of the above
23.3. Which of the following is not a binary tree?
a. binary heap
b. pairing heap
c. skew heap
d. splay tree
e. all of the above are binary trees
23.4. Which of the following has been shown to be an asymptotically bad implementation of
combineSiblings ?
a. one pass merge, left to right
b. two pass merge
c. using a queue
d. all of the above
e. none of (a), (b), and (c)
125

23.5. Which operation does not take O(1) time in pairing heaps?
a. findMin
b. deleteMin
c. merge
d. all of the above operations require O( log N ) time
e. all of the above operations require O( 1 ) time

Answers to Exam Questions


1. B
2. D
3. B
4. A
5. B
126

CHAPTER

24 The Disjoint Set Class

24.1 Key Concepts and How To Teach Them


The disjoint set class is a classic and can be taught at the CS-2 level. The analysis is beyond the reach of CS-2, how-
ever.
You will first need to motivate the disjoint set by picking one or two exam-ples.The maze example is intuitive and
easy to understand. If you use the minimum spanning tree example, be sure to differentiate the trees that are formed
by the union/find from the spanning tree. For some reason, many students get this confused.
You can mention the quick-find strategy, but I have found that it is not motivational, and that the best way is to get
right to the quick-union. I like to view union-by-height (or by-size) as being the same as a typical corporate takeover
in which a big company swallows up a smaller company. This seems to make sense to my students. Path compres-
sion is also not too difficult, although one must explain the interaction between union-by-height and path compres-
sion. As mentioned in the text, the heights become ranks, which are now estimates of the real height.

24.2 Solutions To Exercises


In Short
24.1 We assume that parameters to the union s are roots. Also, in case of ties, the second tree is made a
child of the first. Arbitrary union and union-by-height give the same answer, shown as the top tree in
Figure 24.1. Union-by-size gives the bottom tree.
24.2 In both cases, change the parent of nodes 16 and 17 to the tree root.
24.3 Edges of cost 1, 2, 5, 6, 7, and 9 comprise the minimum spanning tree. The total cost is 30.

In Theory
24.5 Initially, each room is a node in a graph. Each connected component of the graph represents a set.
Knocking a wall is equivalent to merging two connected components by add an edge. In this algo-
rithm, a wall is knocked down only if it merges two connected component. Hence, it can be seen that
the corresponding graph will be acyclic. Therefore, there is a unique path from start to ending point.
24.6 Modify the algorithm in Section 24.2.1 so that the component containing start and ending rooms are
not merged. Thus, in the end, only two components remain, one containing the starting point and the
other containing the ending point. One can then find a wall that can be knocked down to create a
unique path
127

Figure 24.1 Result of unions for Exercise 24.1

24.7 The proof does not require the use of nonnegative edges. Here is an informal proof: if T is a spanning
tree, then the addition of any edge e creates a cycle. Removal of any edge on the cycle reinstates the
spanning tree property. The cost of the spanning tree is lowered if e has lower cost than the edge that
was removed. Let K be the tree generated by Kruskal’s algorithm. Suppose it is incorrect to use an
edge that is accepted by Kruskal’s algorithm. Let T be the spanning tree created by another algorithm
and assume that it has lower cost. Add the smallest cost edge suggested by Kruskal’s algorithm that is
not found in T. This creates a cycle. Break the cycle by removing an edge that Kruskal’s algorithm did
not suggest (some edge must exist); if this edge has higher cost, then the replacement lowers the cost
of T, contradicting its optimality. If it is equal, then continue with the replacement process. Eventually,
either the contradiction will be reached, or we will see that T has the same cost as K, showing the
Kruskal’s algorithm is correct.
24.8 This is easily proved by induction because the depth can increase only when a tree is merged with a
tree of equal height. So if we look at the smallest tree of height H, it is the result of merging the two
smallest trees of height H – 1. Thus a tree of height H can be shown to have at least 2H nodes.
24.9 Suppose there are u union and f find operations. Each union costs constant time, for a total of u. A
find costs one unit per vertex visited. Charge under rule (A) if the vertex is a root or child of the root
and under rule (B) otherwise. In other words, all vertices are in one rank group, so the total (A)
charges are 2f. Notice that a vertex can be charged at most once under rule (B), so the total (B)
charges are at most u. All the charges thus add up to 2f + 2u, which is linear.
24.10 We assume that the tree is implemented with node references instead of a simple array. Thus find
will return a reference instead of an actual set name. We will keep an array to map set numbers to their
tree nodes. union an d find are implemented in the standard manner. To perform remove(x) , first
perform a find(x) with path compression. Then mark the node containing x as vacant. Create a new
one node tree with x and have it referenced by the appropriate array entry. The time to perform a
remove is the same as the time to perform a find , except that there could potentially be a large num-
ber of vacant nodes. To take care of this, after N remove s are performed, perform a find on every
node, with path compression. If a find(x) returns a vacant root, then place x in the root node, and
128

make the old node containing x vacant. The results of Exercise 24.9 guarantee that this will take linear
time, which can be charged to the N remove s. At this point, all vacant nodes (indeed all nonroot
nodes) are children of the root, and vacant nodes can be disposed. This all guarantees that there are
never more than 2N nodes in the forest and preserves the time bound.
24.11 For each vertex v let the pseudorank Rv be defined as Î log Sv ˚, where Sv is the number of descendents
(including itself) of v in the final tree, after all union s are performed, ignoring path compression.
Although the pseudorank is not maintained by the algorithm, it is not hard to show that the pseudo-
rank satisfies the same properties as the ranks do in union-by-rank. Clearly a vertex with pseudo-rank
Rv has at least 2Rv descendents (by its definition), and the number of vertices of pseudo-rank R is at
most N / 2R. The union-by-size rule ensures that the parent of a node has twice as many descendents as
the node, so the pseudo-ranks increase monotonically on the path towards the root if there is no path
compression. Path compression does not destroy this property.
24.12 This is most conveniently implemented without recursion, and is faster because even if full path com-
pression is implemented non-recursively, it requires two passes up the tree. Path halving requires only
one. We leave the coding details to the reader. The worst-case running time remains unchanged
because the properties of the ranks are unchanged. Instead of charging one unit to each vertex on the
path to the root, we can charge two units to alternating vertices (namely those vertices whose parents
are altered by path halving). These vertices get parents of higher rank, as before, and the same kind of
analysis bounds the total charges.

In Practice
24.14 (a) If path compression is implemented, the strategy does not work because path compression moves
elements out of subtrees. For instance, the sequence union(1,2) ,union(3,4) ,
union(1,3) ,find(4) ,deunion(1,3) will leave 4 in set 1 if path compression is implemented.

24.3 Exam Questions


24.1. Which of the following properties is not required for an equivalence relation?
a. reflexive
b. symmetric
c. transitive
d. all of these properties are required
e. none of these properties is required
24.2. Which of the following is an equivalence relationship?
a. a R b if there is a path from a to b in a directed graph G
b. a R b if class a is derived from class b
c. a R b if a and b end in the same two digits
d. all of the above
e. none of (a), (b), and (c)
24.3. What is the cost of the minimum spanning tree for the following graph?

a. 6
b. 26
c. 27
d. 28
129

e. none of the above


24.4. Which algorithm is used to compute minimum spanning trees?
a. breadth first search
b. Dijkstra’s
c. Kruskal’s
d. Tarjan’s
e. none of the above
24.5. Which of the following, when performed by itself, is sufficient to ensure a bound of O ( M log N ) for
M operations?
a. path compression
b. union by height
c. union by size
d. all of the above
e. none of (a), (b), and (c)
24.6. Path compression is
a. performed during union s to make union s faster
b. performed during union s to make find s faster
c. performed during find s to make find s faster
d. performed during find s to make union s faster
e. performed during find s to make both find s and union s faster
24.7. What is the value of log*65536?
a. 1
b. 4
c. 16
d. 32
e. none of the above
24.8. Which of (a)–(d) is false?
a. The relation is ≥ transitive
b. The relation is ≥ an equivalence relation
c. The relation = is symmetric
d. The relation “is not less than” is transitive
e. all of the above are true
24.9. How many find operations are required in the maze application?
a. O( log N )
b. O( N )
c. O( 1 )
d. O( N log N )
e. none of the above

Answers to Exam Questions


1. D
2. C
3. B
4. C
5. D
6. C
7. B
130

8. B
9. B
131

APPENDIX

A Sample Syllabi

The basic model I am assuming is 28 class meetings of 75 minutes for lectures. I am assuming that there are addi-
tional meetings for exams. Here is typical coverage for three scenarios; you can modify this as appropriate for the
background of your students.
Figure A.1 illustrates the separation approach for students who have not had any Java or object-based language.
Figure A.2 illustrates the approach when the CS-1 course is a class-oriented Java presentation. Students whose back-
ground is somewhere in between can be accommodated by removing the material on graphs and/or some of the case
studies.
Figure A.3 shows the more traditional approach for students with a solid Java background. As with the separation
approach, students with a weaker Java background will need additional material at the start of the course and some
topics will have to be removed at the instructor’s discretion.
TIP: you may want to go with the traditional approach the first time you teach so that you are familiar with the text.
The separation approach could then be used the second time—the material is just moved, but much of the course
preparation is unchanged.

Figure A.1 Separation approach for Java newbies


132

Figure A.2 Separation approach if CS-1 is in Java, and the basics of classes have been discussed

Figure A.3 Traditional approach if CS-1 is in Java, and the basics of classes have been discussed
133

APPENDIX

B Sample Assignments

Nine additional assignments follow. I have avoided using any of the programming projects already listed in the text.

1. Basic assignment
Write a program to perform a simple encryption.

2. Specifications
There are two input files, and two output files, all of which you will prompt the user to specify. The input files are
the text file and the conversion file and the output files are the encrypted file, and the decryption formula file.
The conversion file consists of a sequence of lines each with two characters (no blanks or white space separate
these). The idea is that the input file is converted to the encrypted file by a character for character substitution dic-
tated by the conversion file. Therefore, a line
aA
specifies that all lower case as get converted to upper case As. By default, a character is converted to itself, so an
empty conversion file implements a copy command. You will need to also write a file that tells how to get back to
the original input: This is the decryption formula.
If a line has anything but two characters on it, report an error, and terminate. Note that a character can include punc-
tuation or white space. Any character which does not appear as a first character on some line in the conversion file
is to be converted to itself.
Needless to say, you must check for all possible errors. This includes, but is not limited to, failure to open files, as
well as an inconsistent conversion file. For all types of errors, you must provide a line number, as well as a mean-
ingful error message; for errors in the conversion file you may terminate. The conversion file is inconsistent if there
exists any character c such that the conversion either from or to c is ambiguous.
For instance, the conversion file
aA
aC
bA
is inconsistent because there is ambiguity on what a should be converted to, and also because both a and b want to
be converted to A (which would be impossible to decode). The conversion file
aC
bA
Aa
Cb
is ok, however, and generates the decryption file
aA
bC
Ab
Ca
134

1. Basic Assignment
Write a simple word processor.

2. Specifications
The user is to enter the name of a file that is to be processed. The input file consists of a sequence of words and
commands. The user will also specify where the output is placed.

3. Commands
Commands are placed one to a line. Unless changed by one of the commands, there are 72 characters per line, 66
lines per page, and paragraphs are indented by five spaces. All pages except the first must have a page number sur-
rounded by two dashes on the top line, followed by a blank line. You must right justify all lines, using the min-
imum amount of extra spacing. A period at the end of an input line indicates the end of a sentence; you must leave
at least one space (preferably two) after it. Periods anywhere else do not indicate ends of sentences.
All commands reside by themselves on a line. The commands you must support are listed below.
Command Function
.PP Begin a new paragraph.
.SP x Skip x lines (default is 1).
.BP Begin a new page.
.CE x Center the next x input lines (default is 1).
.SO filename Include filename in the formatting stream.1
.cp x2 Set the current page number to x.
2
.ll x Set the number of characters per line to x.
2
.pl x Set the number of lines per page to x.
.in x2 Set the number of spaces for indenting to x.

4. Notes
• The .CE command reads the next x input lines and centers them, as is, without attempting to coalesce lines
together.
• To begin a new page, you do not need to use a form feed character. Just put the page header on top.
• Any sequence of one or more blanks, tabs or newlines are to be treated as one word separator.
• You must provide reasonable error checking for all of these commands.

1. These may be nested (i.e. filename may have .SO commands in it).

2. x is either an absolute number or a number prefixed by + or – to indicate a relative change.


135

1. Basic Assignment
Given a set of required courses and a prerequisite list, you are to determine the minimum number of semesters
required to graduate (assuming that you can take as many courses in a semester as you like, provided you do not
violate a prerequisite requirement).

2. Brief Description
The input file is of the following format: The first k lines contain one entry each, indicating a required course.
Course names consist of letters and digits only, and are not case sensitive. The first k lines are sorted. (A later
assignment can remove this and use a binary search tree). After that, all lines are of the form
course: prereq1 prereq2 ... prereqn

You must output the list of courses which can be taken in semester1, then semester2, etc., and you must use the min-
imum number of semesters. The answer is unique, though courses in a given semester may be output in any order.

3. Basic Algorithm
Throughout the algorithm, refer to any course by its position in the list of required courses. You can find the posi-
tion quickly by reading the courses into an array and applying binary search whenever necessary.
Next, for each course, keep a linked list of all the prerequisites. Since, as per the previous paragraph, courses are
represented by a natural number between 1 and k, what you have is an array of list of int s.
Also, for each course, attach a boolean, initially marked false, and the semester in which it can be taken, initially
marked true. Finally, keep an array which indicates, for each course, the number of postrequisites (the number of
courses for which it is a prerequisite).
Thirdly, verify that the prerequisite structure is not bogus (that is, there is no cyclic chain of prerequisites). This can
be done by a topological sort. See Chapter14. You will need a queue for this part. If the prerequisite listing is bogus,
then stop. Finally, compute the answer by applying the following recursive algorithm, which is applied to all courses
which are not prerequisites to any other course:
int computeSemester( int course )
{
if course is marked known then
return the known information;

mark course as known;


int howMany = 1;
for each prerequisite course p loop
howMany = max( computeSemester(p)+1, howMany );
return howMany;
}
136

1. Basic Assignment
The data is a list of N points (in two dimensions). You must write a program that will repeatedly ask for values ,
xmin, xmax , ymin, ymax, and find all points ( x, y ) that simultaneously satisfy xmin ≤ x ≤ xmax and y min ≤ y ≤ ymax. The
points are in random order.

2. Data Structure
The data structure you will use is a 2-d tree. A 2-d tree is like a binary search tree except that on even levels of the
tree, the branching decision is determined by the x coordinates and on odd levels, the branching is determined by
the y coordinates.

3. Specifications
Ask the user for the input file name containing the points. Ask the user if all points that match the query descrip-
tion should be printed or just a total. You can do fancier than this if you like, but one of the queries you should test
is –∞ ≤ x ≤ ∞ and –∞ ≤ y ≤ ∞ , which should find every point but not print them unless the user asks.
Check the request for validity and process it if it is legitimate. Output the CPU time required to process each request.
137

1. Basic Assignment
You have a list of items i1, i2, ..., iN which have weights w1, w2, ..., wN . You need to place the items into boxes but
each box can hold at most 100 lbs. The bin-packing problem is to find an arrangement of the items so that the fewest
number of boxes (bins) are used.

2. Theory
Bin-packing is one of many problems that are NP-complete. The most famous NP-complete problem is the travel -
ing salesperson problem; other examples include many operating systems scheduling problems, many problems
from Boolean algebra and many important problems in graph theory. There is no known algorithm that is guaran-
teed to solve the bin-packing (or any other NP-complete) problem in polynomial time. Most researchers in the area
believe that exponential running time is intrinsic to NP-complete problems.
These problems still need to be solved, so algorithms have been developed to give good, fast approximations.

3. An Approximation Algorithm
The algorithm you will use is simple and fast. When an item comes in, it is placed into the most empty box. If this
box cannot hold the item, it is placed in a new box. Keep the boxes stored in a priority queue with the most empty
box at the root.

4. Specifications
Ask the user for the input file name. Ask the user if she/he wants the play-by-play description. If so, then as each
item is processed, print out which box it goes into.
At the end of the program, print out the total number of boxes used and the total weight of all items in the boxes.
138

1. Basic Assignment
Write a program that will interactively perform polynomial arithmetic.

2. Specifications
Use a linked list, sorted by exponents to represent a polynomial. Then write routines to input, add, subtract, and mul-
tiply polynomials, as well as a routine to raise a polynomial to a power and to print it out. Keep track of all of the
polynomials which are input and output during the program. The user should be able to specify commands such as
add polynomial#4 and polynomial#0, with the answer going into the next available polynomial. Of course you may
use any reasonable method to interface the program to the user.
139

1. Basic Assignment3
Write a program that will repeatedly insert elements into an initially empty binary search tree and then print out a
graphical representation of the tree.

2. Specifications
Start with an empty tree. Repeat until the user gives up: Ask for an integer 0 ≤ i <1000 to insert into the tree.
After each insertion, print out the tree. The root goes on one line. The nodes at depth one go on the next, depth two
on the next, etc. You must make sure that any nodes in the subtree of x are printed to the left of x.
To do this, add two fields to your tree data structure. One is inOrderNum , which is the inorder traversal number of
any node. Calculate this each time after an insertion, prior to printing the tree. The other is the depth , which is the
depth of each node. This just tells you when to print newlines. Use a queue to perform the level-order traversal.

3. Variations include: a red-black tree with color information output, or a BinarySearchTreeWithRank in which the inorder number is deduced by
the size fields.
140

1. Basic Assignment
You are given a text file of words. The assignment is to read the file into a binary search tree and then answer
questions.

2. The Input
The files are in mixed case. For the purposes of this assignment, a word is any sequence of lower or upper case let-
ters; digits and other characters do not count. Convert words to lower case before you process them.

3. The Program
Ask the user for a file name. Read the file into a binary search tree. Then ask the user what to do next. The options
are
1. get help
2. quit
3. print the number of distinct words,
4. print the number of occurrences of a particular word,
5. print all words that appear more than a certain number of times, alphabetically,
6. print all words, with the frequency of occurrence, that occur alphabetically on or after some word1 and
on or before some word2, and
7. print out a list of line numbers on which some word occurs.
You should keep asking until the user quits.
All the tree routines must be placed in a generic class. All routines may accept only a tree and a word, where appro-
priate. You should also use a generic queue class, which must use a linked list implementation. Efficiently imple-
ment toString for the standard queue class (use a StringBuffer ).
141

1. Basic Assignment4, 5, 6
You are to solve the maze traversal problem. Prompt the user for the name of the file that contains the maze.
The format of the maze file is given below. For this assignment, assume that the starting point is row 0, column 0,
and the ending point is the last row, last column. If a path exists, print the length of the shortest path and the
sequence of steps taken to reach it. If not say so.

2. Maze File
The first line contains the number of rows and columns. Each subsequent line represents a square and possible walls:
N for northern wall, S for southern wall, E for eastern wall, W for western wall. An eastern wall for square (i, j)
implies a western wall for square (i, j+1) (if square (i, j+1) exists), whether or not square (i, j+1) explicitly says so,
and so on for other directions. Any square in row zero automatically has a northern wall; similarly for other squares
on the border, except for the starting and ending points. Each square may list several walls (or possibly no walls);
the directions can be in any order, and the squares can be in any order. Example of an input file with a couple of
redundant walls:

4 4
0 0 S
0 1 E
0 2 W
1 0 NS
1 2 ES
2 1 N
3 1 W
3 2 N
3 3 N

For this maze the shortest path is 13 squares, and the path is given by the directions ESENESSWWSEE. (In other
words, go east, then south, then east, etc. Once you have numbered the maze squares by the breadth-first search,
printing out the path is simple if you use recursion.
Needless to say, you should catch any ill-formatted lines in the input file and skip them after printing a warning mes-
sage. You should use the queue class provided in the online code.

4. Additional assignment: Generate a maze using the following algorithm: begin with walls between every pair of adjacent squares. Repeatedly select
a wall at random; if the squares separated by the wall are not already connected, then remove the wall. Repeat this until all squares form one connected
component. Use the union/find data structures to test connectivity.

5. If the students know how to use the AWT, then the assignment can require sketching the maze graphically.

6. Additional assignment: Allow the knocking down of walls, with a penalty p (which is part of the input). This problem is solved using Dijkstra’s
algorithm.

You might also like