We Iss Book
We Iss Book
and
Problem Solving
Using Java™
(Third Edition)
Instructor’s Resouce Manual
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 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 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 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 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
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 [email protected] . My home page http://www.cs.fiu.edu/~weiss
will maintain an updated error list and additional notes.
CHAPTER
1 Primitive Java
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.
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.
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
2 References
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.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
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:
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>( );
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?
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
• instanceof operator
• static class members
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.
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.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 {
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
CHAPTER
4 Inheritance
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.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.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.
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;
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.
4.24
class SortWithMin
{
public static int findmin( Comparable [] a, int start )
{
int minIndex = start;
}
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();
}
4.26
class Person implements Comparable
{
public int compareTo(Object rhs)
{
Person p2 = (Person) rhs;
return (name.compareTo(p2.name));
}
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;
}
}
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( ) );
}
/**
* 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( );
}
class TestSortedArrayList
{
public static String listToString( SortedArrayList list )
{
Iterator itr = list.iterator( );
StringBuffer sb = new StringBuffer( );
4.29
public interface Matchable
{
boolean matches(int a);
}
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;
}
}
}
4.30
class EqualsK implements Matchable
{
private int k;
public EqualsK()
{ this( 0 ); }
class P429
{
public static void main (String [] args)
{
int [] a = { 4, 0, 5, 0, 0};
System.out.println( P428.countMatches( a, new EqualsK(5) ) );
}
}
CHAPTER
5 Algorithm Analysis
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.
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.
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
6 Collections API
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;
}
CHAPTER
7 Recursion
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.
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
boolean aIsOdd = a % 2 == 1;
boolean bIsOdd = b % 2 == 1;
If A = Bk, then
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.
7.20 A driver routine calls binarySearch with 0 and n-1 as the last two parameters.
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 ).
7.25 The nonrecursive routine is straightforward. The recursive routine is shown below.
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.
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
CHAPTER
8 Sorting
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.
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 );
8.17 The number of recursive calls is logarithmic because the smaller partition will always be less or equal
to half the original size.
// 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.
CHAPTER
9 Randomization
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.
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
8. A
60
CHAPTER
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.).
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.
CHAPTER
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.
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
12 Utilities
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.
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.
CHAPTER
13 Simulation
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.
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.
CHAPTER
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
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:
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:
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.
CHAPTER
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.
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
In Practice
15.8
public class MyContainer
{
private Object [ ] items = new Object[ 5 ];
private int size = 0;
items[ size++ ] = x;
return true;
}
import java.util.*;
public class BetterIterator
{
Iterator itr;
public BetterIterator()
{
Iterator itr = new Iterator();
}
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
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
CHAPTER
In Practice
16.4
public static void main( String [ ] args )
{
Stack intStack = new StackAr( );
Stack doubleStack = new StackAr( );
CHAPTER
17 Linked Lists
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
currentPosition = header.next;
nextPosition = currentPosition.next;
previousPosition = null;
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.
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.
CHAPTER
18 Trees
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
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.
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;
}
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 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.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 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 ).
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.
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
CHAPTER
20 Hash Tables
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
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 :
CHAPTER
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 ];
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
12. B
13. B
119
CHAPTER
22 Splay Trees
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
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.
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
CHAPTER
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.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
CHAPTER
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
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.
a. 6
b. 26
c. 27
d. 28
129
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.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).
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;
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.