INSTRUCTOR’S GUIDE

Stephen P. Alberg

DATA ABSTRACTION AND PROBLEM SOLVING WITH C++
WALLS AND MIRRORS
Frank M. Carrano University of Rhode Island Paul Helman Robert Veroff University of New Mexico

2

Copyright © 1998 by Addison Wesley Longman, Inc. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, electronic, mechanical, photocopying, recording, or any other media or embodiments now known or hereafter to become known, without the prior written permission of the publisher. Published in the United States of America. The programs and applications presented have been included for their instructional value. They have been tested with care but are not guaranteed for any particular purpose. The publisher does not offer warranties or representations, nor does it accept any liabilities with respect to the programs or applications.

3

CONTENTS
Introduction 4 Three Possible Courses Based on Walls and Mirrors 5

Solutions to Exercises 8 PART I Problem-Solving Techniques 1 Principles of Programming and Software Engineering 8 2 Recursion: The Mirrors 13 3 Data Abstraction: The Walls 26 4 Linked Lists 31 5 Recursion as a Problem-Solving Technique 45 PART II Problem Solving with Abstract Data Types 6 Stacks 54 7 Queues 66 8 Class Relationships 72 9 Algorithm Efficiency and Sorting 88 10 Trees 95 11 Tables and Priority Queues 113 12 Advanced Implementations of Tables 122 13 Graphs 129 14 External Methods 138
Appendix A Review of C++ Fundamentals 147 Appendix D Mathematical Induction 152

.4 INTRODUCTION This instructor’s guide. Some discussion-type solutions are omitted. is organized as follows: Three Possible Courses Based on Walls and Mirrors. The book’s flexibility will allow you to use it in a variety of situations. The solutions to the end-of-chapter exercises and some programming projects are included in this section. and functions that appear in the book are available by anonymous ftp. header files. implementation files. We begin by offering suggestions for how to use Walls and Mirrors in your course. Reminder: All programs. Solutions to Exercises. which supplements Data Abstraction and Problem Solving with C++: Walls and Mirrors. Questions and problems suitable for examinations and quizzes are provided for each chapter in the text. Test Bank. Please consult the preface of the book for further details.

variations are possible. if the introductory course manages to cover only the basics of a programming language. you can devote less time to Chapter 3 than is indicated in the syllabus given here. you may need to devote some class time to the coverage of Appendix A. With this flexibility in mind. as time permits. The book’s preface provides details about the orders in which you can cover chapters. The course should cover the material through Chapter 10. you should devote a fair amount of class time to Chapters 1 and 2. A Second Course in Computer Science If your introductory course emphasizes structured programming and introduces recursion. You can cover many topics within these chapters in any order. If your students do not know C++ or C. On the other hand. at least.) 11 Efficiency and sorting 9 12 Efficiency and sorting 9 (Cont. which reviews C++.) 4 Linked lists 4 5 Linked lists 4 (Cont. You can select topics from Chap-ters 11 through 14.) Queues 7 9 Queues 7 (Cont.) 6 Recursion 5 7 Stacks 6 8 Stacks 6 (Cont. Possible Syllabus for a Second Course in Computer Science Week Topic Chapter 1 Review of programming principles 1 Review of recursion 2 2 Data abstraction 3 3 Data abstraction 3 (Cont.5 THREE POSSIBLE COURSES BASED ON WALLS AND MIRRORS By appropriately choosing which parts of the book to emphasize.14 .) Class relationships 8 10 Class relationships 8 (Cont.16 Selected advanced topics 12 . Although the suggested courses presented here cover material in the order in which it appears in the book. here are three possible courses based on this book.) 14 Tables and priority queues 11 15 . If your students have studied C++ classes. students can review most of the first two chapters on their own. you can easily tailor the material to fit your particular curriculum.) Trees 10 13 Trees 10 (Cont.

Possible Syllabus for a Lower-Division Data Structures Course Week Topic Chapter 1 Review of programming principles 1 Review of recursion 2 2 Data abstraction 3 3 Linked lists 4 4 Recursion 5 5 Stacks 6 6 Stacks 6 (Cont.) 12 Balanced search trees 12 13 Hashing 12 (Cont. You should assign projects.) Queues 7 7 Queues 7 (Cont.) External sorting 14 16 External sorting 14 (Cont.) Tables 11 11 Priority queues and heaps 11 (Cont.) . You can use this book in such a course by giving high priority to the material in Chapter 3 and Chapters 6 through 14.) Class relationships 8 8 Efficiency and sorting 9 9 Trees 10 10 Trees 10 (Cont. the primary undergraduate data structures course comes early in the curriculum.) Graphs 13 15 Graphs 13 (Cont.6 Lower-Division Data Structures Courses At many schools.) 14 Multiple organizations 12 (Cont. in which students implement 2-3 trees or hashing. such as the ones suggested in Chapter 12. and you may even find it necessary to omit some of the material on recursion. This means that you should spend as little time as possible on Chapter 1.

7 Second.) 5 Linked lists 4 6 Linked lists 4 (Cont.) Multiple organizations 8 Graphs 13 9 External sorting 14 10 External sorting 14 (Cont.) 6 Balanced trees 12 7 Hashing 12 (Cont. then Chapters 1 through 7 would constitute a reasonable second quarter. If the first quarter is an introductory programming course. Possible Syllabus for a Second-Quarter Course Week Topic Chapter 1 Review of programming principles 1 2 Review of recursion 2 3 Data abstraction 3 4 Data abstraction 3 (Cont.) Possible Syllabus for a Third-Quarter Course Week Topic Chapter 1 Class relationships 8 2 Efficiency and sorting 9 3 Trees 10 4 Trees 10 (Cont.) 7 Recursion 5 8 Stacks 6 9 Stacks 6 (Cont.) Tables 11 5 Priority queues and heaps 11 (Cont.) .) Queues 7 10 Queues 7 (Cont. This would leave the remainder of book for the third quarter.and Third-Quarter Courses This book should work very well for schools on the quarter system.

// Postcondition: The valid numeric values for the succeeding Month. int& C). } if( Year <= 0) { cout << "Bad input value for Year" << endl. // ---------------------------------------------------------------- 2a #include <bool. // ----------------------------------------------------------------------// Determines if the input year is a leap year. // ---------------------------------------------------------------// Computes the change remaining from purchasing an item costing // DollarCost dollars and CentsCost cents with D dollars and C cents. int CentsCost. 31. 30. except // when Month == 2. CentsCost. Day. int& Year) { // error check on input values if( Month <= 0 || Month > MONTHS_PER_YEAR) { cout << "Bad input value for Month" << endl. Year) by one day. // and Year are returned. // Precondition: 1 <= Month <= MONTHS_PER_YEAR. 31. // Preconditions: DollarCost. 30. the // proper negative values for the amount owed in D dollars and/or C // cents is returned. int& Year). 31. // 1 <= Day <= DAYS_PER_MONTH[Month . 31. Day. 30. void ComputeChange(int DollarCost. // ----------------------------------------------------------------------- 2b void IncrementDate( int& Month.h> const MONTHS_PER_YEAR = 12. 30. // Precondition: Year > 0. const DAYS_PER_MONTH[] = {31. int& Day. // ----------------------------------------------------------------------// Increments the input Date values (Month. Day == 29 and IsLeapYear(Year) is true. 31. return. return. int& D. void IncrementDate( int& Month.8 SOLUTIONS TO EXERCISES PART I: Problem-Solving Techniques 1 Principles of Programming and Software Engineering 1 const CENTS_PER_DOLLAR = 100.1]. // Postconditions: Returns true if Year is a leap year. D and C are all nonnegative // integers and CentsCost and C are both less than CENTS_PER_DOLLAR. // Postconditions: D and C contain the computed remainder values in // dollars and cents respectively. int& Day. 31}.1]) { // special case for February . false otherwise. } if( Day <= 0 || Day > DAYS_PER_MONTH[Month . // ----------------------------------------------------------------------bool IsLeapYear( int Year ). If input value D < DollarCost. 28.

1]) { // test for February 29 if( Month == 2 && IsLeapYear(Year) && Day == 29) return. a four-digit age entry is surely a typo and the user should be allowed to correct the error.g. Declare and use a constant for the maximum length of a name. } // end if } // end if } // end if } // end IncrementDate bool IsLeapYear( int Year ) { // Example: 2000 is a leap year but 1900 was not return ((Year % 400 == 0) || ((Year % 4 == 0) && (Year % 100 != 0))). e. Use more descriptive variable names.g. return. The user should also be given an option to exit the program.. Document the program. if( Day > DAYS_PER_MONTH[Month . requiring exactly eight characters per name is unreasonable. Check input for obvious errors. else { // increment to next month Day = 1.. } else if( !IsLeapYear(Year) || Day > 29) { cout << "Bad input value for Day" << endl. if( Month > MONTHS_PER_YEAR) { // increment to next year Month = 1. . } // end if } // end if // increment the date Day++. Year++. Month++. Make the program more robust. e. } // end IsLeapYear 3 We can make several improvements to user interaction and programming style: • • • • • • • Prompt the user for the input and indicate the expected form of the input. return. Give more descriptive output.9 if( Month != 2 ) { cout << "Bad input value for Day" << endl.

// -----------------------------------------------------------{ // compute the cosine once and store it double Denominator = cos(X). then // the function returns the sum of the first LIMIT positive // elements and Success is true. ≤ F = N! / Integer! 6 The invariants for the for loop are as follows: a b c ≤0 I≤ N . the result of this computation must be test divisor in order to avoid a "divide by zero error.1. Sum is equal to the sum of the first NumPos integers in A. } // check for division by zero if( Denominator == 0 ) { cerr << "Cannot divide by zero" << endl. } 5 The invariants for the for loop of the function Factorial() are as follows: a b ≤1 Integer N.10 4 There are two hazards with the function Computation(). int N. returns the value of the Computation. Se in the range of the function cos(X).1)*PI/2 where k is a // positive integer. Else. First. // check for negative argument to sqrt() function if( X < 0 ) { cerr << "Cannot take square root of negative number" << endl." The following implementatio safe checks. // Postcondition: returns -1 if preconditions are not met // else. // Precondition: A is an array of N elementTypes with at least LIMIT // of A positive and N >= LIMIT. ≤0 NumPos≤ LIMIT. the argument X cann number because Computation() calls the sqrt() function with X as the argument. elementType ComputeSum(const elementType A[]. return -1. // Preconditions: X >=0 and X != (2k . Sum is 0 and . const int LIMIT = 5. bool& Success) // -----------------------------------------------------------------------// Computes the sum of the first LIMIT positive elements in A. return -1. double Computation( double X ) // -----------------------------------------------------------// Performs a computation. } return sqrt(X)/Denominator. typedef int elementType. // Postcondition: If A contains at least LIMIT positive elements.

. the last value of Index (i. + A[Index]. return Sum. -----------------------------------------------------------------------int NumPos = LIMIT.1] and A[N]. ≤ 0 ≤ Prior to the while loop. ++I) if(A[I] > 0) { Sum += A[I]..: A[ <last value of Index> + 1 ]) is added to Sum. + A[N . Since the value of Sum prior to this last access was A[0] + .. } // end if // perform the computation on the array for(int I = 0. the value of Sum so far is A[0] + . I < N && NumPos > 0. A and N are unchanged. The value of the next element of the ar accessed element (i. elementType Sum = 0. } // end for // verify that LIMIT elements have been added if(NumPos > 0) { Success = False.11 // // { Success is false. b The while loop condition should be while(Temp1 <= X) Debugging involves printing the values of Temp1. return 0. return Sum. + A[Index]. c Supply user prompts and check the value of X to ensure that it is greater than 0. } // end if // pass on results Success = true. Within the while loop. // check for out of bounds errors if(NumPos > N || N < 0) { Success = false.. If In incremented (i. Index is set to 0 and Sum is initialized to A[Index]. the algorithm corroborates the invariant at Index = N. describes the value of Sum at this point. Temp2 and Ans at the top of the loop. . NumPos--..e.: Index = Index + 1).. 8 The bug is in the while loop: a Output is The floor of the square root of 64 is 7.e. } // end ComputeSum 7 Invariant: Index N and Sum = A[0] + . the last value of Index must have been N since the loop t increments before it is used to access the array A. After the while loop.

.h> enum errorCode {DZERO. // Preconditions: none. }.. // mnemonic error codes void SevereErrorMessage(errorCode Error) // ----------------------------------------------------------------// Displays an error code and terminates program execution. // terminate execution immediately } // end SevereErrorMessage . . break. . AOTRG. default: cerr << "Unknown fatal error." << endl. // ----------------------------------------------------------------{ switch(Error) { case DZERO: cerr << "Divide by zero error. }. // Postconditions: An error message corresponding to the input // errorCode is output to the standard error stream // and the program is terminated." << endl. . break.h> #include <stdlib. case AORTG: cerr << "Array index out of range. // end switch exit(0)." << endl.12 9 #include <iostream.

After N recursive calls.e. Each recursive call diminishes the size of the problem: The recursive call to NumberValue subtracts 1 from the current value for N. passing this as the parameter N in the next call. the base case is reached: N is an integer and is decremented by 1 with each recursive call. effectively reducing the size of the unsearched remainder of the array by 1. An instance of the problem serves as the base case: Here. This terminates the recursion. As the problem size diminishes.13 2 Recursion: The Mirrors 1 The problem is defined in terms of a smaller problem of the same type: Here. the parameter N in the Nth call will have the value 0 and the base case will be reached. the case where the size of the 0) ≤ array is 0 (i.: N results in the return of the value 0: an array of size 0 can have no instances of the DesiredValue. 2a The call Rabbit(5) produces the following box trace: N = 5 Rabbit(4) = ? Rabbit(3) = ? Return ? N = 5 Rabbit(4) = ? Rabbit(3) = ? Return ? Follow the Rabbit(4) call N = 4 Rabbit(3) = ? Rabbit(2) = ? Return ? Follow the Rabbit(3) call N = 5 Rabbit(4) = ? Rabbit(3) = ? Return ? N = 4 Rabbit(3) = ? Rabbit(2) = ? Return ? N = 3 Rabbit(2) = ? Rabbit(1) = ? Return ? Follow the Rabbit(2) call = 2 N = 5 Rabbit(4) = ? Rabbit(3) = ? Return ? N = 4 Rabbit(3) = ? Rabbit(2) = ? Return ? N = 3 Rabbit(2) = ? Rabbit(1) = ? Return ? N = 2 Base case: N Return 1 call N = 5 Rabbit(4) = ? Rabbit(3) = ? Return ? N = 4 Rabbit(3) = ? Rabbit(2) = ? Return ? N = 3 Rabbit(2) = ? Rabbit(1) = ? Return ? N = 2 The Rabbit(2) Return 1 completes . a single index of the array is evaluated and the result added to a call on the remaining part of the array.

14 N = 5 Rabbit(4) = ? Rabbit(3) = ? Return ? N = 4 Rabbit(3) = ? Rabbit(2) = ? Return ? N = 3 Rabbit(2) = 1 Rabbit(1) = ? Return ? Follow the Rabbit(1) call N = 5 Rabbit(4) = ? Rabbit(3) = ? Return ? N = 5 Rabbit(4) = ? Rabbit(3) = ? Return ? N = 4 Rabbit(3) = ? Rabbit(2) = ? Return ? N = 4 Rabbit(3) = ? Rabbit(2) = ? Return ? N = 3 Rabbit(2) = 1 Rabbit(1) = ? Return ? N = 3 Rabbit(2) = 1 Rabbit(1) = 1 Return 2 N = 1 Base case: N = 1 Return 1 N = 1 The Rabbit(1) Return 1 completes call call N = 5 Rabbit(4) = ? Rabbit(3) = ? Return ? N = 4 Rabbit(3) = 2 Rabbit(2) = ? Return ? N = 3 Rabbit(2) = 1 Rabbit(1) = 1 Return 2 N = 1 The Rabbit(3) Return 1 completes N = 5 Rabbit(4) = ? Rabbit(3) = ? Return ? N = 4 Rabbit(3) = 2 Rabbit(2) = ? Return ? Follow the Rabbit(2) call N = 5 Rabbit(4) = ? Rabbit(3) = ? Return ? N = 5 Rabbit(4) = ? Rabbit(3) = ? Return ? N = 5 Rabbit(4) = 3 Rabbit(3) = ? Return ? N = 4 Rabbit(3) = 2 Rabbit(2) = ? Return ? N = 4 Rabbit(3) = 2 Rabbit(2) = 1 Return 3 N = 4 Rabbit(3) = 2 Rabbit(2) = 1 Return 3 N = 2 Base case: Return 1 N = 2 The Rabbit(2) call completes Return 1 N = 2 The Rabbit(4) call completes Return 1 N = 2 N = 5 Rabbit(4) = 3 Rabbit(3) = ? Return ? N = 5 Rabbit(4) = 3 Rabbit(3) = ? Return ? Follow the Rabbit(3) call N = 3 Rabbit(2) = ? Rabbit(1) = ? Return ? Follow the Rabbit(2) call N = 5 Rabbit(4) = 3 Rabbit(3) = ? Return ? N = 5 Rabbit(4) = 3 Rabbit(3) = ? Return ? N = 3 Rabbit(2) = ? Rabbit(1) = ? Return ? N = 3 Rabbit(2) = 1 Rabbit(1) = ? Return ? N = 2 Base case: Return 1 N = 2 The Rabbit(2) call completes Return 1 N = 2 .

15 .

the call to CountDown(0) N = 5 N = 4 N = 3 N = 2 N = 1 N = 0 cout << “5 “. cout << “3 “. cout << “3 “. cout << “2 “. cout << “2 “. Follow the call to CountDown(3) N = 4 N = 3 N = 5 cout << “5 “. cout << “3 “. The end of line is printed and the CountDown(0)call completes. cout << “1 “. cout << “4 “. The value 1 is printed. cout << “4 “. The value 3 is printed. cout << “2 “. Follow the call to CountDown(2) N = 5 N = 4 N = 3 N = 2 cout << “5 “. return . Follow the call to CountDown(4) N = 5 N = 4 cout << “5 “. cout << “3 “. The value 2 is printed. cout << “4 “. The value 4 is printed. N = 0 cout << endl. cout << “3 “. cout << “2 “.16 N = 5 Rabbit(4) = 3 Rabbit(3) = ? Return ? N = 3 Rabbit(2) = 1 Rabbit(1) = ? Return ? N = 3 Rabbit(2) = 1 Rabbit(1) = ? Return ? N = 3 Rabbit(2) = 1 Rabbit(1) = 1 Return 2 Follow the Rabbit(1) call N = 5 Rabbit(4) = 3 Rabbit(3) = ? Return ? N = 5 Rabbit(4) = 3 Rabbit(3) = ? Return ? N = 1 Base case: Return 1 N = 1 The Rabbit(1) call completes Return 1 N = 1 N = 5 Rabbit(4) = 3 Rabbit(3) = 2 Return 5 N = 3 Rabbit(2) = 1 Rabbit(1) = 1 Return 2 N = 1 The Rabbit(3) call completes Return 1 N = 5 Rabbit(4) = 3 Rabbit(3) = 2 Return 5 N = 3 Rabbit(2) = 1 Rabbit(1) = 1 Return 2 N = 1 The Rabbit(5) call completes and the value 5 is returned to the calling function Return 1 2b The call CountDown(5) produces the following box trace: N = 5 cout << “5 “. cout << “4 “. cout << “4 “. Follow the call to CountDown(1) N = 5 N = 4 N = 3 N = 2 N = 1 cout << “5 “. cout << “4 “. cout << endl. The value 5 is printed. cout << “1 “. N = 5 N = 4 N = 3 N = 2 N = 1 cout << “5 “. cout << “1 Follow “.

cout << “3 “. cout << “4 “. // ----------------------------------------------------{ // base case if(Size == 1) cout << S[0]. typedef char stringType[MAX_LENGTH]. N = 2 N = 1 N = 0 cout << “2 “. N = 3 N = 2 N = 1 N = 0 cout << “3 “.1). else // reduce the problem size return A[N . return The CountDown(5) call completes and returns to the calling function. // -------------------------------------------------------{ // base case if(N <= 0) return 0.1]. N = 5 N = 4 N = 3 cout << “5 “. // Preconditions: 0 <= N <= size of A. cout << “4 “. cout << endl. // Precondition: The string S contains Size characters. N = 4 N = 3 N = 2 N = 1 N = 0 cout << “4 “. but remains // unchanged. } // end ComputeSum 4 const MAX_LENGTH = 30. N = 5 N = 4 cout << “5 “. cout << “1 “. cout << endl. // Postcondition: S is written backward. int Size) // ----------------------------------------------------// Writes a character string backward. cout << “1 “. return The CountDown(3) call completes. The contents of // A and the value of N are unchanged. // where Size >= 1. N . int N) // -------------------------------------------------------// Returns the sum of the first N integers in the array A. 3 int ComputeSum(const int A[]. cout << “3 “. cout << “2 “. void WriteBackward(stringType S. WriteBackward(S. // else. Size . N = 5 N = 4 N = 3 N = 2 cout << “5 “. // Postconditions: The Sum of the first N integers in the // array A are returned. return The CountDown(2) call completes. return The CountDown(4) call completes.17 The CountDown(1) call completes. cout << “2 “. cout << endl. write rest of string else if (Size > 1) { cout << S[Size . cout << endl.1). cout << “2 “.1] + ComputeSum(A. N = 5 cout << “5 “. cout << “3 “. cout << “1 “. cout << “4 “. N = 1 N = 0 cout << “1 “. } .

18 // Size <= 0 do nothing. } // end WriteBackward .

// This function does not output a // newline character at the end of // a string. // ----------------------------------------------{ // check for input bounds if(Number >= 0) { // base case if(Number < NUMBER_BASE) cout << Number. // end of string } // end if // N <= 0 do nothing. } // end PrintIntegers 6 const int NUMBER_BASE = 10. // Precondition: Number >= 0.19 5 void PrintIntegers(int N. // test for end of string if(N != Limit) cout << ". else cout << ". // Postcondition: The decimal digits of Number // are printed in reverse order. // Postcondition: The integers from 1 through N // are printed out followed by a // newline. void ReverseDigits(int Number) // ----------------------------------------------// Prints out the decimal digits of Number in // reverse order. else { // print out rightmost digit cout << Number % NUMBER_BASE. // Preconditions: N >= 0 and Limit == N. // --------------------------------------------{ if(N > 0) { // print out the rest of the integers PrintIntegers(N . Limit). } // end if } // end if } // end ReverseDigits . // now print out this integer cout << N. ". // pass remainder of digits to next call ReverseDigits(Number / NUMBER_BASE). int Limit) // --------------------------------------------// Prints out the integers from 1 through N as a // comma separated list followed by a newline.1." << endl.

// Precondition: N >= 0. M . int N) // --------------------------------------------------------// Outputs a line of N characters where Ch is the character. int M.20 7a void WriteLine(char Ch.1. } // end WriteBlock 8 Running the given program produces the following output: Enter: Enter: Leave: Leave: 2 A A A A = = = = 1 1 1 1 B B B B = = = = 7 3 3 7 9 Running the given program produces the following output: Enter: Enter: Enter: Enter: Leave: Leave: Leave: Leave: 5 First First First First First First First First = = = = = = = = 1 1 1 4 4 1 1 1 Last Last Last Last Last Last Last Last = = = = = = = = 30 14 6 6 6 6 14 30 . // Preconditions: M >= 0 and N >= 0. // write first line WriteBlock(Ch. int N) // -------------------------------------------------------// Outputs a block of M rows by N columns of character Ch. // Postcondition: A line of N characters Ch is output // followed by a newline. // --------------------------------------------------------{ // base case if(N <= 0) cout << endl. N). // -------------------------------------------------------{ if(M > 0) { WriteLine(Ch. N . // Postconditions: A block of M rows by N columns of // character Ch is printed. WriteLine(Ch. N). } // end if } // end WriteLine 7b void WriteBlock(char Ch. // write rest of line else { cout << Ch. // write rest of block } // base case: M <= 0 do nothing.1).

and the function w and F(-1). if N is odd. likewise.. After this call. The execution with N = 100: N = 100 DisplayOctal(12) N = 12 DisplayOctal(1) N = 1 cout << 1 cout << 4 cout << 4 Output: 144 11 Even though the precondition states that N is nonnegative. is pri The given function computes the number of times 80.: called again with N/8 as an argument.21 10 The algorithm first checks to see if N is a positive number: if not it immedia integer division of N by 8 is taken and if the result is greater than 0 (i. recursively and are printed out in the reverse of the order of computation. 81. This call processes that portion of the powers of 8. the else will execute. will divide N.e. F(N) two smaller odd integers. there is no actual c for N from being used as the argument in the function. calling F with an odd integer will cause an infinite sequence of practical level. . or an integer unde The following is the exact output of the program: Function entered with N Function entered with N Function entered with N Function entered with N Function entered with N Function entered with N Function entered with N Function entered with N Function entered with N The value of F(8) is 27 = = = = = = = = = 8 6 4 2 0 2 4 2 0 12 The following output is produced when X is a value argument: 6 7 8 8 7 6 2 1 0 0 1 2 . A call to function F() will produce a further call to F() with a negative argume is not within the subrange of 0 to 2. 82. N % 8. Thus any odd nonnegative integer will eventually cau Theoretically. the residue for the current power. the computer's run-time stack will overflow. Because the value for F(N) is based on the values for F(N-2) and F(N-4 addends will be the next two smaller even integers..

c Number of comparisons = = 6. --N. 101 lg 15a double Power1(double X. } return Result.22 Changing X to a reference argument produces: 6 7 8 8 8 8 2 1 0 0 1 2 13a The call BinSearch(5) produces the following box trace: Value = 5 First = 1 Last = 8 Middle = 4 Value < A[4] Value = 5 First = 1 Last = 3 Middle = 2 Value = A[2] return 2 13b The call BinSearch(13) produces the following box trace: Value = 13 First = 1 Last = 8 Middle = 4 Value > A[4] Value = 13 First = 5 Last = 8 Middle = 6 Value < A[6] Value = 13 First = 5 Last = 5 Middle = 5 Value < A[5] Value = 13 First = 5 Last = 4 First > Last return 0 13c The call BinSearch(16) produces the following box trace: Value = 16 First = 1 Last = 8 Middle = 4 Value > A[4] Value = 16 First = 5 Last = 8 Middle = 6 Value < A[6] Value = 16 First = 5 Last = 5 Middle = 5 Value > A[5] Value = 16 First = 6 Last = 5 First > Last return 0 14 a For a binary search to work the array must first be sorted in either ascendin b Index = (0 + 101)/2 = 50. // Preconditions: N >= 0 // Postcondition: The computed value is returned. // value of X^0 while(N > 0) { Result *= X. { double Result = 1. int N) // Returns the value of X raised to the Nth power. } // end Power1 // iterate until N == 0 .

multiply X by rest of computation else return X * Power2(X. } } // end Power3 15d The following table lists the number of multiplications performed by each of the the values on the top line: 332 32 32 7 319 19 19 8 Power1 Power2 Power3 15e The following table lists the number of recursive calls made by each of the algo perform the computation on the inputs given on the top line: 332 32 6 319 19 5 Power2 Power3 ... // Preconditions: N >= 0 // Postcondition: The computed value is returned. else return X * HalfPower * HalfPower. // else. int N) // ----------------------------------------------// Returns the value of X raised to the Nth power.. // Preconditions: N >= 0 // Postcondition: The computed value is returned.. // ----------------------------------------------{ if(N == 0) return 1. else { // do this computation only once!! double HalfPower = Power3(X. N-1). // if N is odd. // ----------------------------------------------{ // base case if(N == 0) return 1. // if N is even. int N) // ----------------------------------------------// Returns the value of X raised to the Nth power. N/2). } // end Power2 15c double Power3(double X. if(N % 2 == 0) return HalfPower * HalfPower.23 15b double Power2(double X.

int Tab) // --------------------------------------------------------------------------// Computes a term in the Fibonacci sequence. if (N<1) // base case Result = 0. ++I) cout << '\t'. F(12) is 95. I < Tab. I++) Result *= I. return Value. F(7) is 11. I<=N. // Postconditions: The progress of the recursive function call is displayed // as a sequence of increasingly nested blocks. // Preconditions: N is a positive integer and Tab = 0. ++I) cout << '\t'. else { Result = 1. Tab+1) + Rabbit(N-2. long Result. I < Tab. 18a A function to compute N! iteratively: long Fact(int N) { int I. so N-1 > 0 and N-2 > 0 // indent by one for next call Value = Rabbit(N-1. The function // returns the Nth Fibonacci number. Tab+1). } // end if return Result. // Display status of call cout << "Enter: N = " << N << endl. for (I=2. // Display status of call cout << "Leave: N = " << N << " Value = " << Value << endl. // Indent the proper distance for this block for(int I = 0. } // end Fact . else // N > 2. } 17 Since we only need the five most recently computed values. F(15) is 320. // Indent the proper distance for this block for(int I = 0. F(6) is 8. we will maintain a "c element array indexed modulus 5.24 16 Maintain a count of the recursive depth of each call by passing this count as an function call and print that many spaces or tabs in front of each message: int Rabbit(int N. // --------------------------------------------------------------------------{ int Value. if(N <= 2) Value = 1.

1. } else if (A[Mid] < Key) Low = Mid + 1.1. Result. its location index // in A is returned. else Result = Low. // Preconditions: A is sorted in ascending order. int Key. int Low. // search the lower half } // end while loop if (Low > High) Result = -1. return Result. High = Mid. Low // = 0 and High = the size of A . return -1 else if (A[Low] != Key) Result = -1. cout << endl. // if not found. // ------------------------------------------------------{ int Mid. } // end WriteBackward 18c A routine to do iterative binary search: int BinarySearch(int A[]. I>= 0. // search the upper half else High = Mid .h> void WriteBackward(char S[]) { for (int I = strlen(S)-1. } // end BinarySearch . if (A[Mid] == Key) { Low = Mid. // Postconditions: If Key is found. int High) // ------------------------------------------------------// Searches a sorted array and returns the index in the // array corresponding to the value Key if Key is in the // array.25 18b A simple iterative solution to writing a string backwards: #include <string. else -1 is returned. -1 otherwise. I--) cout << S[I]. while(Low < High) { Mid = (Low + High)/2.

Then. i. Then. } // end KSmall 19 The for loop invariant is: ≤ I≤ N 3 So the sum = Rabbit − 1 + (I ) I=3 ∑ N Rabbit − 2). b. (I 20a We must verify that the equation holds for both the base case and the recursive For the base case.rq'). A[J]). Thus. let GCD(a. b) = b.. J++) if (A[J] < A[I]) Swap(A[I]. Therefore.: a = g(s . g divides a and by hypothesis. a mod b) = d. The proof is symmetrical where GCD(b. (n/d) = kq + j. To show that d is the greatest common divisor of b and a mod b. int N) 22 . an integer. int KSmall(int K. a mod b) = d is taken for the hypothesis. i. J<Size. suppose for con integer g > d such that b = gr and (a mod b) = gs for integers r and s.a)/b = q. b) = d. a mod b) = b. I<K.26 18d A function to find the kth smallest element of an array. So gs . We implement with an selection sort up to k times.: a = dj and b = dk for integer integer n = a mod b such that (n . let GCD(a. GCD(b. 20b If b > a in the call to GCD. since 0/n = 0 fo For the recursive case. int A[].e. then a mod b = a and so the recursive call effecti 20c When a > b. Assume a standard integer Swap function. where q is an integer. int Size) { for ( int I=0. GCD(b.a = grq'. Then.e. I++) for ( int J=I+1. So mod b). 21a   0 if n = 1  C (n)=  1 if n = 2  n−1 C ) ∑ ( (n − i + 1) else  i=1 int Acker(int M. Then. the actual argument associated with the formal argument a decreases If b > a then the next recursive call will swap the actual arguments so that a will eventually equal the second and so eventually a mod b will be 0. return A[k-1]. ni.e. Hence. where (kq + j) is an integer. a mod b = 0 and. n = d(kq + j).

else if (N == 0) Result = Acker(M-1. } // end Acker . else Result = Acker(M-1.27 { int Result. Acker(M. 1). N-1)). if (M == 0) Result = N+1. return Result.

Else // Success is false.ListLength(). Second. NextItem.ListInsert(I. int J. ++I) { // Invariant: Sum is the total of the first // I elements in L. L. // Postconditions: If the contents of the list have been // reversed. Success = true. First. int I. Second. Second. boolean& Success) // -----------------------------------------------------// Reverses the order of the items in list L.28 3 Data Abstraction 1 int AddList(listClass& L) // ---------------------------------------------------------// Computes the sum of the integers in the list L. Success).ListRetrieve(I. } // end for } // end AddList 2 void Swap(listClass& L. boolean& Success) // ------------------------------------------------------// Swaps the Ith and the Jth items in the list L. I <= L. if(Success) L. // ------------------------------------------------------{ listItemType First. NextItem. // Postcondition: The sum of the integers in L is returned. if(Success) Sum += NextItem.ListInsert(J.ListRetrieve(I. L. then Success is true. Success = true. } // end Swap 3 void ReverseList(listClass& L.ListRetrieve(J. // Test each retrieval and insertion for Success.ListLength(). for(int I = 1. // initialize this for loop test . boolean Success. if(Success) L. Success). Success // returns true. // Precondition: L is a list of integers. Success). Success = false. Else. // -----------------------------------------------------{ int High = L. // Postconditions: If the swap is successful. Success). // ---------------------------------------------------------{ int Sum = 0. Success). // Precondition: L is a list of type listItemType. First. Success is false. if(Success) L. // Preconditions: none. // else.

the manner in which the list items are displayed can be adjusted or formatted or an additional test for replacement of a list item may be performed at the client's discretion. However. b Defining these operations within the ADT obviously alleviates the disadvantage cited in part a. High. For example. Success). Proper bounds checking and other testing is performed within the ADT. Low < High && Success. High--) // Invariant: The items with indices lower than Low // and higher than High have been swapped. Low. the client cannot control how these operations perform and if greater refinement of control is required he must write additional functionality making use of the suite of operations provided by the ADT. removing such concerns from the client.29 for(int Low = 1. The disadvantage of this implementation is that the client may fail to test such important procedures as indexing past the end of the list or performing the insertion into the list after the deletion fails. } // end ReverseList 4 a A clear advantage to defining such operations externally to the ADT is the control that the client has in customizing the functionality. Swap(L. 5 Some ADT set operations: CreateSet() // creates an empty set DestroySet() // destroys a set SetCopy(Set2) // copies Set2 to a set SetIsEmpty() // determines whether a set is empty SetSize() // returns the number of elements in a set (the cardinality) IsSubsetOf(Set2) // determines whether a set is a subset of a Set2 IsAnElement(Item) // determines if Item is an element of a set SetUnion(Set2) // inserts all items in Set2 into a set SetIntersection(Set2) // deletes all items from a set that are not in Set2 6 Some ADT string operations: . Low++.

in a string LocateStringInString(String2) // returns the starting position of a substring. Time)) AB.IsAppointment(Date.IsAppointment(Date. Date. Time.Coefficient(Power) + Q.Coefficient(P. Char.MakeAppointment(Date.30 CreateString() // creates a null terminated empty string DestroyString() // destroys a string StringCopy(String2) // copies String2 to a string StringLength() // returns the length of a string.CancelAppointment(Date. 3) for(Power = 0.Degree() || Power < Q. . as well as definitions for the constants BegOfDay. 8 a b c Display(P. Purpose. ++Power) // Invariant: R is the sum of polynomials P and Q to degree Power.Degree().Degree())) P. excluding the null character StringConcatenation(String2) // concatenates String2 to the end of a string StringCompare(String2) // returns the result of a comparison between String2 and a string LocateCharInString(Char) // returns the position of a character. Time)) AB. String2. Time. DisplayAppointment().Coefficient(3) + 8.ChangeCoefficient(P. Power) 9 In the following implementation.Coefficient(Power). Power < P. R. Time. Success) Time = BegOfDay while(Time < EndOfDay) if(AB. Success) 7b Display all the appointments for a given date: DisplayAllAppointments(AB. Time. Success) Time = Time + HalfHour This implementation requires the definition of a new operation.DisplayAppointment(Date. Success) AB.ChangeCoefficient(P. the 0th power corresponds to the first item in the member list of the ADT polynomial. Success) if(AB. Date. in a string 7a Change the purpose of an appointment: ChangeAppointmentPurpose(AB. NewPurpose. EndOfDay and HalfHour.

ListRetrieve(2) by definition of N = B by axiom 9 Show the fourth item. C: (N. NewCoefficient. C) as Show the second item.ListInsert(2. A) and N represent M.SortedListRetrieve(I.ListRetrieve(2) by definition of M = A by axiom 9 . Success) if(Success) L.ListRetrieve(2) by axiom 10 =(L.ListInsert(2. Coefficient. Success) if(Success) return Coefficient else // Power may be greater than Degree return 0 ChangeCoefficient(NewCoefficient.ListRetrieve(2) by axiom 10 =(M. C) It is sufficient to show the characters A. and fourth items in the list N. for(int I = 0.ListRetrieve(3) = M. B)). A)). Success).ListDelete(Power + 1. third.1 Coefficient(Power) // Returns the coefficient of the x^Power term. B) Now retrieve the second. A: (M. 11 The first sequence of insertions is written as follows: ((L. Success). ++I) // Invariant: L contains the first I elements of A in ascending order L. B. A[I]. 3.ListRetrieve(Power + 1. B)). B: (N.SortedListInsert(A[I].ListRetrieve(3) = N.ListInsert(2. L.ListInsert(2. I < N && Success. B)). C)). for(int I = 0. C)).31 Degree() // Returns the degree of a polynomial return L.ListLength() .ListInsert(2. and C are at positions 4.ListRetrieve(2) = C by axiom 9 Show the third item.ListInsert(2. I < N && Success. Power) // Replaces the coefficient of the x^Power term with NewCoefficient L.ListInsert(2.ListInsert(2. A)).ListInsert(2. Success) 10 boolean Success = true. ++I) // Invariant: A contains the first I elements of L in ascending order L.ListInsert(2.ListInsert(2.ListInsert(Power + 1. and Let M represent L.

Success) if(Success) C.32 13 int F( int N ) // Note: list items are accessed beginning at position 1 // rather than position 0. Success). 1. if (N>=0 && N<5) // base case Last5. Last5.ListDelete(I%5+1. Last5. Success). Success). Last5. temp2.SortedListRetrieve(Index. Last5. Success). Success).ListRetrieve((I-5)%5+1. } Last5. Success). // insert items 1 through 5 into list Last5.ListInsert(4. result. Success) if(Success) C. else if(N >= 5) { for (I=5. DataItem. Success) } . Success). temp1+3*temp2. temp2. 1. Last5. } // else result = 0 return result.SortedListInsert(DataItem.ListInsert(5. Last5. Success). Success). temp1. Success) B. result = 0. This introduces an additional // offset after taking the mod of N for the retrieval and // updating of the proper values. temp1.ListInsert(I%5+1. I++) { Last5. Last5. 1. Success).SortedListInsert(DataItem.ListRetrieve((I-1)%5+1. result. } // end F 14 for(Index = 1 through A.ListRetrieve((N-1)%5+1. 3. Success).ListInsert(1. I<N.ListInsert(2. DataItem.ListRetrieve(N+1. 5.ListInsert(3. { listClass Last5.ListLength()) { A.SortedListRetrieve(Index. int I. boolean Success.ListLength() or B.

P->Next = Cur. b To delete the last node (containing J) on the list resulting from the previo Prev->Next = NULL. 2a Insert and delete at the head of the linked list: void InsertNode (ptrType& Head. c ‘G’ gets inserted at the end of the list resulting from the above statements by executing Prev->Next = new node. Head->Next = NULL. } else { NewNode->Next = Head. Head = NewNode. and assuming the struct node. Prev->Next->Ch = 'G'. Prev->Next = P. }. we have the following: a ‘F’ gets inserted into the middle as follows: P = new node. Prev->Next->Next = NULL. ptrType Next.33 4 Linked Lists 1 Starting from the list that results from Self-Test Exercise 3. typedef node* ptrType. P. Prev. } } // end InsertNode . ptrType Head. ptrType NewNode) { if (Head == NULL) { Head = NewNode. P->Ch = 'F'. Cur. struct node { char Ch. delete Cur.

delete Cur. Cur = Head. if (Cur != NULL) { while (Cur->Next != NULL) { Prev = Cur. while (Cur != NULL) { Prev = Cur. } } // end DeleteEndNode . Cur = Cur->Next. Cur = Cur->Next. Cur = Head. else Prev->Next = NewNode.34 void DeleteNode (ptrType& Head) { ptrType Cur. if (Cur != NULL) { Head = Head->Next. Prev. Prev. } if (Prev == NULL) Head = NewNode. } // end InsertEndNode void DeleteEndNode (ptrType& Head) { ptrType Cur. } // Cur points to node to delete if (Prev == NULL) // only one node to delete Head = NULL. Cur = Head. } } // end DeleteNode 2b Insert and delete at the end of the linked list: void InsertEndNode (ptrType& Head. else Prev->Next = NULL. ptrType NewNode) { ptrType Cur. Prev = NULL. Prev = NULL. delete Cur. Cur->Next = NULL.

35 .

Temp = Head. delete Tail. ptrType& Tail. ptrType NewNode) { if (Head == NULL) { Head = NewNode. Tail = Temp. // Temp points to node previous to the node to delete Temp->Next = NULL. } } } // end DeleteEndNode 3a int ListCount(ptrType Head) // -------------------------------------------------// Returns the number of items in a linked list.36 2c Insert and delete at the end of the linked list with a tail pointer: void InsertEndNode (ptrType& Head. } else { Tail->Next = NewNode. // Precondition: Head points to a list of type node. } else { while (Temp->Next != Tail) Temp = Temp->Next. Cur = Cur->Next). Cur. ptrType& Tail) { ptrType Temp. if (Head != NULL) { if (Tail == Head) // only one node to delete { delete Head. for(ptrType Cur = Head. } } // end InsertEndNode void DeleteEndNode (ptrType& Head. return Count. } // end ListCount . Count++. // Postcondition: The number of items in the list // is returned. Tail = Tail->Next. Tail = Head. // -------------------------------------------------{ int Count = 0. Head = Tail = NULL.

delete HighCurr. Next. Prev = HighPrev = NULL. if(Curr != NULL) // if there is a list { if(Curr->Next == NULL) // if there is only one item { delete Head. else // first node is the highest Head = HighCurr->Next. void DeleteHighestItem(ptrType& Head) // ---------------------------------------------------------// Deletes the largest item from the list pointed to by Head. Curr. // keep track if(Curr->Data { HighCurr = HighPrev = } } // delete node containing highest item if(HighPrev) HighPrev->Next = HighCurr->Next. } } // end if of highest item > HighCurr->Data) Curr. HighCurr. . else // base case return 0. Curr = HighCurr = Head. // Postcondition: The largest item in the list pointed to by // Head is removed. typedef int itemType.37 3b int ListCount(ptrType Head) // -------------------------------------------------// Returns the number of items in a linked list. // Postcondition: The number of items in the list // is returned. HighPrev. } 4 typedef struct node* ptrType. Curr = Curr->Next. // ---------------------------------------------------------{ ptrType Prev. Prev. // Precondition: Head points to a list of type node. // Precondition: Head points to a list of type node. } else { while(Curr->Next != NULL) { Prev = Curr. Head = NULL. // -------------------------------------------------{ if(Head) return 1 + ListCount(Head->Next). struct node { itemType ptrType }. Data.

ptrType StringPtr. This version. void WriteBackward2(ptrType StringPtr) // --------------------------------------------------------// Writes a string backward.38 } // end DeleteHighestItem 5a The following is an iterative implementation of WriteString. // --------------------------------------------------------{ for(ptrType Curr = StringPtr. } 5b The following is an iterative implementation of WriteBackward2. ptrType Curr = StringPtr. // --------------------------------------------------------{ int Count = 0. The // linked list and StringPtr are unchanged. Curr.. typedef struct node* ptrType. + 1 or N*(N+1)/2 steps altogethe typedef struct node* ptrType. if(Curr != NULL) { Count++. Curr = Curr->Next) cout << Curr->Item. ptrType StringPtr. and so on taking N + (N-1) + . ptrType Next. lik proceeds in linear fashion from the beginning of the string to the end. struct node { char Item. // Postcondition: The string is displayed. ptrType Next. // string is at least one character long while(Curr->Next != NULL) { Count++. // Precondition: The string is represented as a linked list // to which the pointer StringPtr points. }. Unlike the recur iterative approach is not straightforward: while the recursive version takes N the iterative version must first take N steps to get to the last character. struct node { char Item. The linked list // and StringPtr are unchanged. void WriteString(ptrType StringPtr) // --------------------------------------------------------// Writes a string.. // Postcondition: The string is displayed backward. the to the last. // Precondition: The string is represented as a linked list // to which the pointer StringPtr points. . }.

Cur1 = Head1. Result points to no list. ptrType Head2. FirstNode = false. } cout << Curr->Item. ptrType& MergeHead) // -----------------------------------------------------------------// Merges two lists sorted in ascending order into a third list. Cur2 = Cur2->Next. Cur2. // decrement length of rest of string } // end while } // end if } // end WriteBackward2 6 void MergeSortedLists(ptrType Head1. // remember to terminate the list if(Cur1->Item < Cur2->Item) // copy item from list 1 { MergeCurr->Item = Cur1->Item. while(Count > 0) { int Temp = 1. // if neither list is empty merge them while(Cur1 != NULL && Cur2 != NULL) { if(FirstNode) { MergeHead = new node. } MergeCurr->Next = NULL. Cur2 = Head2. // print last character Count--. } cout << Curr->Item. Curr = StringPtr.39 Curr = Curr->Next. // Preconditions: The lists pointed to by Head1 and Head2 are sorted // in ascending order. Count--. ptrType Cur1. // -----------------------------------------------------------------{ boolean FirstNode = true. of string while(Temp < Count) { Temp++. Cur1 = Cur1->Next. MergeCurr = MergeCurr->Next. MergeCurr. // Postconditions: Result points to a sorted list containing the // contents of the lists pointed to by Head1 and Head2 in ascending // order. Curr = Curr->Next. // print last character // decrement length of rest of string // for whatever remains of the string // reset pointer to beg. MergeCurr = MergeHead. The original lists pointed to by Head1 and Head2 are // unchanged. } else { MergeCurr->Next = new node. } else // copy from list 2 { MergeCurr->Item = Cur2->Item. } } // end while if(Cur1 != NULL) // determine which list is not completely traversed .

40 Cur2 = Cur1 .

MergeCurr = MergeCurr->Next.41 while(Cur2) { if(FirstNode) // in case one of the lists was empty { MergeHead = new node. (Note that this would be true even if the list implemented a Tail pointer to be reassigned following the deletion. 9 Consider first the case where Position = 1. } else { MergeCurr->Next = new node. You may be inclined to think faster by a constant factor since only a cout is executed each time through the for (I=0. 8 Traversing an array of size N and traversing a linked list of N nodes both requ will differ by a constant factor for increasing N. the special case for deletion list is invoked. In the pointer-based implementation. However. then the contents of that list becom is reassigned. I = 0. constant number of steps versus the O(N) steps required by the array imp For the case where Position = N. FirstNode = false. It is important to note that these nodes are not deallocated by so the memory they occupy cannot. third position until. and then deleting what was the first node. assigning the address of the first node to a temporary pointer to the next node in the list. all of which t the pointer-based implementation. Cur2 = Cur2->Next. while (I < N) { cout << A[I]. since a for loop has implicit comparison and increment operations. I++. A more detailed analysis would require looking at the as produced by the compiler. MergeCurr->Next = NULL. then N-1 overwrites must occur decremented. In an array based implementation t is deleted by overwriting with the value of the second position and this value. I++) cout << A[I]. I<N. it w implement the array traversal as follows for comparison purposes. in an array-based implementation this becomes address of the item to be deleted can be found immediately by indexing into the at the end of the list. if the List is of size N. } This array traversal loop executes the same number of C++ statements as does th program fragment cited. } MergeCurr->Item = Cur2->Item. the address of the preceding n . } //end while } // end MergeSortedLists 7 If Head points to a non-empty linked list. be reallocated to another pointe a "memory leak". The e small. as a result. only the value for Size need be reduced. MergeCurr = MergeHead. by contrast. the entire list must be traverse is accessed.

Curr = Curr->Next. } while (List != Stop). Temp = NULL. both implementations fare similarly. then th implementation will result in an increase in efficiency. Head->Next = NULL. if (L. // copy constructor . void PrintCircular (ptrType List) { ptrType Stop. If no predictable pattern of deletions is known in adva of an array-based versus a pointer-based list will have less impact on the overa 10 Print the contents of one of our circular lists. delete Temp. Curr = Curr->Next. // advance curr pointer past Head // make Head the last node in list while(Curr != Head) // Curr cannot be NULL in a circular list { ptrType Temp = Curr. Head = Curr = NULL. For the average case where position = N/2. Stop = List.42 after N-1 pointer assignments can the last node be reassigned to a temporary poi preceding node reassigned to NULL. and the last node deleted.Head != NULL) { Stop = L.Head. } // end if } // end destructor // delete last node 11b listClass::listClass (listClass& L) { ptrType P. } // end while delete Head. if (List != NULL) do { cout << List->Data. } // end PrintCircular 11a listClass::~listClass() // destructor { if(Head) { ptrType Curr = Head. If for example more de half of the list than from the first. Thus both require somewhere in the vicinity of N/2 steps If it is known where deletions will be performed in the list in general. List = List->Next. By contrast. This forms the w based list. taking O(N) time. then an array-based implementation will in the pointer-based list. Stop. access the N/2th position with one assignment but must then perform another N/2 reduction of the value Size. the pointer-based list must do N/2-1 finds the node to delete but then performs a constant number of operations for d connecting the list.

Cur->Next = NULL.Head != Stop) { // Stop is one node ahead of P P->Next = new node. boolean Done. Done = boolean ((I < 1) || (List == NULL). int I) { ptrType Prev. Prev. // move the List pointer to the new "last" node Prev->Next = Cur->Next. J++. int J. We also assu "last" node in the list. } if (!Done) // Cur points to the node to be deleted { if (Cur == List) List = Prev. P = P->Next. } // end if } // end DeleteItem // check for a completely // traversed list 13 Scan the list with current and previous pointers and change current’s Next to p step. P = Head.Head->Data. Cur = List->Next. Cur. // copy rest of the list while (L. void DeleteItem (ptrType& List. P->Data = Stop->Data. Stop = Stop->Next. Stop = Stop->Next.43 // copy first node Head = new node. void Reverse (ptrType& List) { ptrType Cur.. Temp. Done = boolean (Cur == List->Next). .. } P->Next = Head.. while ((J < I) && (!Done)) { Prev = Cur. } // end function ListCopy 12 This routine must deal with the event that I is larger than the number of nodes the nodes on a list of N nodes are implicitly numbered 1. Cur = Cur->Next. delete Cur. . Prev = List. Head->Data = L. N. // complete the circle } else Head = NULL. J = 1.

Prev = Cur. Success = bool(NewPtr != NULL). Cur->Next = Prev. bool& Success) { int NewLength = ListLength() + 1. Cur->Next = Prev. Success = bool((NewPosition >= 1) && (NewPosition <= NewLength)). } else { ptrType Prev = PtrTo(NewPosition-1). NewPtr->Item = NewItem. Cur = List->Next. List = Cur. } // end if } // end if } // end if } // end ListInsert .44 if (List != NULL) { Prev = List. // insert new node after node // to which Prev points NewPtr->Next = Prev->Next. // attach new node to list if (NewPosition == 1) { // insert new node after the dummy head node NewPtr->Next = Head->Next. Cur = Temp. Head->Next = NewPtr. listItemType NewItem. do { Temp = Cur->Next. if (Success) { // create new node and place NewItem in it ptrType NewPtr = new listNode. if (Success) { Size = NewLength. } while (Cur != List). Prev->Next = NewPtr. } } // end Reverse // reset List to point to lowest integer 14 Implementing ListInsert() and ListDelete() using a dummy head node: void listClass::ListInsert(int NewPosition.

if (Position == 1) { // delete the node after the dummy head node Cur = Head->Next. Head->Next = Head->Next->Next. if (Success) { --Size. } else .45 void listClass::ListDelete(int Position. bool& Success) { ptrType Cur. Success = bool((Position >= 1) && (Position <= ListLength())).

46 while(!Fin. Temp->Next = Cur.close(). } else { cerr << "Could not open " << Filename << endl. NewName)) { ptrType Temp = Cur. strcpy(Temp->Data. ptrType Dummy = ListHead. // Assumes ListHead points to dummy head node. // advance until NewName is found or dummy node is reached while((strcmp(Cur->Data. NewName) < 0) && (Cur != Dummy)) Cur = Cur->Next. circular linked list. // compare names and position in circular list to find insertion point while((strcmp(Cur->Data. Temp = NULL. ptrType Cur = Dummy->Next. P = P->Next. // after insertion point is found.eof()) { P->Next = new listNode. Fin >> P->Data. } // end if } // end RestoreList 16 // insertion of a node into a doubly linked list with a dummy head node ptrType Dummy = ListHead. delete Temp. } // end while Fin. Cur->Precede->Next = Cur->Next. } // end if . Temp->Precede = Cur->Precede. Cur->Next->Precede = Cur->Precede. NewName) != 0) && (Cur != Dummy)) Cur = Cur->Next. // assumes ListHead points to dummy node ptrType Cur = Dummy->Next. P->Next = NULL. NewName). // if NewName has been found. Temp->Next = Temp->Precede = NULL. Cur->Precede->Next = Temp. reset pointers and delete node if(strcmp(Cur->Data. Cur->Precede = Temp. insert node and copy data ptrType Temp = new listNode. 17 // deletion of a node from a doubly linked.

if(Cur->Precede != NULL) Cur->Precede->Next = Cur->Next. NewName) < 0)) Cur = Cur->Next. delete Temp.. ListHead = Temp. NewName). NewName) != 0)) Cur = Cur->Next. Temp->Precede = Cur->Precede. Temp->Precede = Cur. } else { Temp->Next = Cur. } // end if . ptrType Temp = new listNode. Cur->Precede = Temp. Temp->Next = Temp->Precede = NULL. // compare names in list to find insertion point while((Cur != NULL) && (Cur->Next != NULL) && (strcmp(Cur->Data.. if(Cur->Next != NULL) Cur->Next->Precede = Cur->Precede. Temp->Precede = NULL. Cur->Next = Temp. } strcpy(Temp->Data. // deletion of a node from a doubly linked list. } // if insertion point is at end of list. // if NewName has been found. if(Cur == ListHead) { Temp->Next = ListHead. // if insertion point is at beginning of list... Cur->Precede->Next = Temp. ptrType Cur = ListHead. reset pointers and delete node if(Cur != NULL) { ptrType Temp = Cur. // advance until NewName is found or dummy node is reached while((Cur != NULL) && (strcmp(Cur->Data.47 18 // insertion of a node into a doubly linked list without a dummy head // node ptrType Cur = ListHead. else if(Cur->Next == NULL) { Temp->Next = NULL. else // at beginning of list ListHead = Cur->Next. Temp = NULL.

: Name). 20 // At this point. the assign automatically copy ALL of the internal data from one struct Person to another stru If Person is saved as a C++ class. strcpy(Cur->WaitTail->Who. Cur->WaitTail = Cur->WaitTail->Next. } Cur->WaitTail->Next = NULL. NewName). then the listItemType need only be redefined Person.e. The remaining implementation will then handle Person as a simple data ty though one of the fields in Person is a character string (i. then it should be designed as an Abstract Dat attributes of a Person must be supplied with individual. if(Cur->WaitHead == NULL) { Cur->WaitHead = new waitNode. Cur->WaitTail = Cur->WaitHead. } else { Cur->WaitTail->Next = new waitNode. Cur points to the desired stock item. . well-defined accessor fu retrieving the values of those attributes.48 19 If Person is saved as a C++ structure.

. The call IsPal(“bcdeb”) returns false: W = “bcdeb” W = “abcdeba” return false. the call IsPal(“bcdeb”) is made: W = “abcdeba” W = “bcdeb” At the else if stmt. W = “cde” The call IsPal(“abcdeba”) returns false and the initial call completes. return false. 1b The call IsAnBn(AABB) produces the following box trace: The initial call is made and the function begins execution: W = “AABB” The call to IsAnBn(“AB”) is made: W = “AABB” W = “AB” The call to IsAnBn(“”) is made: W = “AABB” W = “AB” W = “” . W = “bcdeb” return false. W = “cde” return false.. W = “abcdeba” return false. the call IsPal(“cde”) is made: W = “abcdeba” W = “bcdeb” W = “cde” Base case: IsPal(“cde”) returns false: W = “cde” W = “abcdeba” W = “bcdeb” return false.49 5 Recursion as a Problem-Solving Technique 1a The call IsPal(abcdeba) produces the following box trace: The initial call is made and the function begins execution: W = “abcdeba” At the else if stmt.

6) First = 2 Last = 6 FirstEnd = 3 return 4 First = 2 Last = 6 FirstEnd = 3 return 4 First = 4 Last = 6 First = 4 Last = 6 return 4 First = 0 Last = 6 X:EndPre(S.6) First = 2 Last = 6 FirstEnd = 3 First = 3 Last = 6 First = 3 Last = 6 return 3 First = 2 Last = 6 FirstEnd = 3 Y:EndPre(S. return true.1.6) First = 5 Last = 6 . First = 0 Last = 6 First = 0 Last = 6 X:EndPre(S. returns true: W = “” W = “AABB” W = “AB” return true. W = “AABB” return true.1.6) First = 1 Last = 6 X:EndPre(S.2.6) First = 1 Last = 6 FirstEnd = 4 Y:EndPre(S. W = “” The call IsAnBn(“AABB”) returns true and the initial call completes.6) First = 0 Last = 6 X:EndPre(S. W = “” return true.1.50 Base case: W = empty string.5.6) First = 1 Last = 6 X:EndPre(S.6) First = 0 Last = 6 X:EndPre(S.6) First = 0 Last = 6 X:EndPre(S.6) First = 0 Last = 6 X:EndPre(S.6) First = 0 Last = 6 X:EndPre(S.3.6) First = 1 Last = 6 X:EndPre(S.1. W = “AB” return true.2. The call IsAnBn(“AB”) returns true: W = “AB” W = “AABB” return true.1.6) First = 0 Last = 6 X:EndPre(S.6) First = 1 Last = 6 First = 1 Last = 6 X:EndPre(S.4.2.1. 1c The call EndPre(-*/ABCD) produces the following box trace: S = “-*/ABCD”.6) First = 1 Last = 6 X:EndPre(S.1.6) First = 1 Last = 6 FirstEnd = 4 First = 2 Last = 6 First = 2 Last = 6 X:EndPre(S.2.1.2.

6) First = 0 Last = 6 FirstEnd = 5 return 6 First = 0 Last = 6 FirstEnd = 5 return 6 First = 6 Last = 6 return 6 2 The following lists all of the strings of length 7 or less that may be construc $ $$ $$$ $$$$ $$$$$ $$$$$$ $$$$$$$ abb $abb $$abb $$$abb $$$$abb aabbbb $aabbbb 3 <S> = <U> | <U> <L> <U> = A | B | … | Z <L> = a | b | … | z | a <L> | b <L> | … | z <L> 4 <S> = . .51 First = 0 Last = 6 X:EndPre(S. . | .<C> 5a . .6) First = 0 Last = 6 FirstEnd = 5 First = 1 Last = 6 FirstEnd = 4 return 5 First = 1 Last = 6 FirstEnd = 4 return 5 First = 6 Last = 6 First = 5 Last = 6 return 5 First = 0 Last = 6 FirstEnd = 5 Y:EndPre(S.. <C> | . ..1.| . | . . . <C> <C> = .. ..<C> .6.

. typedef char stringType[MAX_LENGTH].. { int Size = strlen(S).1] == Dot) return IsIn( S minus last character) else return false 6 Let L = {S: S is of the form a <S> = ABB | A <S> BB b #include <string.. // Precondition: none. false otherwise. boolean IsInL(stringType S) // Determines it the string S is in the language L. return false. . 5c word0 = . . . else { stringType Substring. Word0 is legal because it is of the form <dash><word>. } } // end IsInL . .. We may recursively remo each subword thus discovered until we get to the terminal subword “. // trim off first and last two characters of S strncpy(Substring.is not in the language because it is not possible for a anywhere except at the beginning. for some n > 0} const int MAX_LENGTH = 80. false otherwise Size = length of string S if(Size == 1 and S[0] == Dot) return true else if (Size > 1 and S[0] == Dash) return IsIn( S minus first character ) else if (Size > 1 and S[Size . Size ... else if (strcmp(S.. return IsInL(Substring). .h> AnB2n.52 5b The string . &S[1]. . if (Size < 3) // no strings shorter than 3 are in L. // Postcondition: returns true if S is in L.” which is 5d IsIn(s) // Returns true if S is in the indicated language. "ABB") == 0) // base case return true..3). typedef int boolean.

We attem 9 1AB 1BA a 1AA 2AB 2BA 2AA . E5 = Ex and E14 = Ey and so S is a valid postfix expression. expressions by proceeding from the front of the string as follows: E1 = A E2 = B E 3 = E 1E 2/ E4 = C E 5 = E 3E 4* E6 = E E7 = F E8 = G E 9 = E 7E 8* E10 = H E11 = E9E10/ E12 = E6E11+ E13 = D E14 = E12E13E15 = E5E14+ Thus. Let S = +E1E2. To show this we firs simple identifier and so it must be an expression of the form ExEy where the terminal symbol "+" is preceded by two postfix expressions. The first case is clear by inspection. To show th component prefix substrings by the following.53 7 Let S = "+*A-B/C++DE-FG". Thus S i We continue recursively by deriving the expressions Ei: 8 The string S = "AB/C*EFG*H/+D-+" is a valid postfix string. expression. E1 = *E3E4 E3 = A E4 = -E5E6 E5 = B E6 = /E7E8 E7 = C E8 = +E9E10 E9 = +E11E12 E11 = D E12 = E E10 = -E13E14 E13 = F E14 = G This exhausts the string S before the prefix expression E2 is defined. S is not a prefix expression since it is defined neither by the form <identifie <operator><prefix><prefix>.

} // end IsInL 11 The recognition algorithm uses recursion to backtrack.54 1BB 2BB b One such string is the following: 12AAB We note that this is of the form <D><S><S> where <D> = 1. Size .1). int Last) { if (Last < First) return false. typedef int boolean. int First. typedef char stringType[MAX_LENGTH]. First+1. reading in the character calling the function recursively on the substring indexed by First+1 and Last-1 compares the character at index Last with that of First. &S[1]. cinclude <string. else if ((Size > 0) && ((S[0] == 'C') || (S[0] == 'D'))) { strType Substring. // base case if ((Size == 1) && ((S[0] == 'A') || (S[0] == 'B'))) return true. else if (First < Last) return (IsPal(S. // Postcondition: returns true if S is in L. A C++ implementation boolean IsPal(stringType S. boolean IsInL(stringType S) // Determines if S is in L // Preconditions: none. else . <S> = A and the other <S> als 10 a <S> = A | B | C <S> | D <S> b CAB is not in the language since both A and B are terminals and there is no exp sequence of terminals. { int Size = strlen(S). } else return false. Last-1) && (S[First] == S[Last])). else if ((First == Last) && // base case (S[First] == '$')) return true. // trim off initial character from S strncpy(Substring. <S> = 2AA and <S> = <S> is also of the form <D><S><S> where <D> = 2. return IsInL(Substring). false otherwise.h> # const int MAX_LENGTH = 80.

55

return false; } // end IsPal

12

a

0 X <3  m (X )=  m (X − 1)+ m (X − 3)+ 1 X ≥ 3
b Base case: By definition, m(X) returns 0 for the case X < 3. Let m(X) = m(X-1) ≥ 3. + m(X-3) + 1, X

Inductive hypothesis:

Consider m(X+1): this function returns the number of multiplications performed know X+1 > 3 since3 Xby inductive hypothesis. Therefore the function P() will m ≥ to itself with the arguments P((X+1)-1) and P((X+1)-3) and then perform an addit of the return values of these function calls. We also know from inductive hypot performs m(X) multiplications and P((X+1)-3) or P(X-2) performs m(X-2) multiplic number of multiplications for m(X+1) = m(X) + m(X-2) + 1, i.e.: m(X+1) = m((X+

13

a

 n = 0 (the null string is a 1  C (n)=  n =1 26 26⋅ C (n − 2) n > 1 
b Base case(s): The null string is the same backwards and forwards so it counts number of lowercase letters is 26, this is the number of possible palindromes o Inductive hypothesis: C(n) = 26*C(n-2) , n > 1

Consider C(n+1): Since the string is a palindrome we know that its starting an identical and that there are 26 possible letters for this repeated character. S character may be chosen independently from the characters in the the substring e count of all possible palindromes of length n+1 is then 26 times the count of a the length of this substring, (n+1)-2 or n-1. By hypothesis we know that the e i, 1 < i ≤ n. Thus, C(n+1) = 26*C((n+1)-2) or C(n+1) = 26*C(n-1).

14

If E is a prefix expression, then EY is not a prefix expression for any nonempty cannot be the initial substring of another prefix expression). We will give a p number of characters in E.

56

Basis |E| = 1: Definition of prefix implies E is a single letter and definition begin with an operator. Inductive hypothesis: For all E with 1 < |E| < n and for all nonempty Y, if E prefix. Inductive step: Say |E| = n, then E = op E 1 E2 for some E1 and E2, which implies expressions and that | E1 |, | E2 | < n. If EY is prefix for some Y, then EY = and W1 and W2 are prefix expressions. We claim that E1 = W1; if not, then one is a substring of the other, so both can hypothesis (|E1| < n). Summarizing what we have so far: E = op E1 E2 and E = op W1 W2 E = op E1 W2 E = op E 1 E2 Y But this implies that W2 = E2Y, which cannot be because E2 and W2 are both prefixe substring of W2.

15

Prove by induction on n that

C (n,k)=

n! (n − k)! ! k

for all n and k such that 0 ≤ k ≤ n. Basis: Show true for n = 0. That is, show

0 C ( ,k)=

0! 0 ( − k)! ! k

for all k such that 0 ≤ k ≤ 0. If n = 0, then k must equal 0 and we have C(0,0) = 1 by definition. And, 0!/(0!0!) = 1, so the basis is established. Inductive hypothesis: Assume true for n = m–1. That is, assume

C (m − 1,k)=

(m − 1)! (m − 1− k)! ! k

for all k such that 0 ≤ k ≤ m–1.

57

Inductive conclusion: We must show the claim to be true for n = m. That is, show

C (m,k)=

m! (m − k)!! k

for all k such that 0 ≤ k ≤ m. There are three cases to consider: Case 1: If k = 0, then we have C(m,0) = 1 by definition and m!/(( m–0)!0!) = 1. Case 2: If k = m, then we have C(m,m) = 1 by definition and m!/((m–m)!m!) = 1. Case 3: If 0 < k < m, then we have C(m,k) = C(m–1,k) + C(m–1,k–1) by definition, and so by the inductive hypothesis (note k m–1), we have

C (m − 1,k)=

(m − 1)! (m − 1)! m! = + (m − 1− k)! ! (( − 1)− (k − 1))!( 1)! (m − k)!! k m k− k

This establishes that the inductive hypothesis implies the inductive conclusion and completes the proof.

stackClass T.GetStackTop(StackItem. Success).StackIsEmpty()) { S. Success). { stackItemType StackItem. Success). S. // put items into reverse order and count the items while(!S.StackIsEmpty()) { S. Success). S.StackIsEmpty()) { T. T. cout << StackItem. stackClass T. } // end while // restore the original stack and display the items while(!T. Success). boolean Success. counter++.GetStackTop(StackItem. Success). } // end while } // end PrintReverse 1b int CountItems(stackClass& S) // Counts the number of items in the stack S. } // end while return counter.Push(StackItem. int counter = 0.Push(StackItem. T.Push(StackItem.Push(StackItem. T.Pop(Success). // put items into reverse order while(!S.58 PART II: Problem Solving with Abstract Data Types 6 Stacks 1a All parts of Exercise 1 assume the standard stack operations as defined in the c void PrintReverse(stackClass& S) // Displays the contents of the stack S in reverse order. } // end while // restore the original stack while(!T.GetStackTop(StackItem. S.GetStackTop(StackItem.Pop(Success). } // end CountItems . S. T.Pop(Success).Pop(Success). boolean Success. Success). { stackItemType StackItem.StackIsEmpty()) { T. Success).

Success). // Note: This function is independent of the stack's implementation. and the lower-middle se the upper-left and the upper-right stacks are pushed onto the lower-middle stack middle stack can be pushed onto either the upper-left or the upper-right stack.StackIsEmpty()) { Temp.GetStackTop(StackItem. cout << StackItem. Success).StackIsEmpty()) { S. S. stackItemType Item) // Removes all occurences of Item from the stack S. stackClass Temp. { stackItemType StackItem.Pop(Success).59 1c void RemoveItem(stackClass& S. stackClass T.GetStackTop(StackItem. cars can be constructed. } // end while } // end RemoveItem 2 There are three stacks: the upper-left. } // end while // restore the original order of the remaining stack items while(!T. // The original stack remains unchanged. // put items into reverse order removing all occurrences of Item while(!S. Success). Success). Push(StackItem. Success). if (StackItem != Item) T. Success). } // end while // restore the original stack while(!Temp. Temp.Push(StackItem.Push(StackItem. // Preconditions: None // Postconditions: The stack items are displayed. T.StackIsEmpty()) { T. boolean Success. S. Success). { stackItemType StackItem. if (Success) { // put items into reversed order and display the stack items while(!StackIsEmpty()) { GetStackTop(StackItem. } // end while } // end if } // end Display .GetStackTop(StackItem. Success).Pop(Success).Push(StackItem. Success = boolean(!StackIsEmpty()).Pop(Success). 3a void stackClass::Display(boolean& Success) // Displays the contents of a stack. Temp. the upper-right. Pop(Success).

StackIsEmpty()) . 5a Contents of the stack during input: e d b a g f b a h f b a a b a c b a b a d b a d b a b a f b a f b a Contents of the stack during output: h f b a f b a b a a The final output is hfba. Success = boolean(TopPtr != NULL).GetStackTop()) S. Cur = Cur->Next. 5b // Assume stack S contains the items to be displayed T.Pop() } // Now shift them back to S. } // end while } // end if } // end Display 4 In the array-based implementation of the ADT list. then the stack can take advantage of the best case for a list insertion o the list at position Top and incrementing Top (employing.Push(S.CreateStack() // shift contents of S to temporary stack while(!S. // Note: This function assumes a pointer based stack implementation. If. the proper end of the array). if (Success) { // display the items Cur = TopPtr. of course. This operation can be done in O(1) time which is comparable operations when a pointer-based implementation is used. while(Cur != NULL) { cout << Cur->Item. // Preconditions: None // Postconditions: The stack items are displayed.StackIsEmpty()) { T. { ptrType Cur. the worst case for a list in insertion occurs at the beginning of the list since each insertion here forces t list one index down. // The original stack remains unchanged. If the ADT stack operations maintain the Top of the stack then each Push() and Pop() call will take O(n) time.60 3b void stackClass::Display(boolean& Success) // Displays the contents of a stack. displaying each item while(!T. the ADT stack maintains a member variable Top which contains the in list. however.

{.CreateStack() Continue = TRUE I = 0 while (I < strlen(Str) and Continue) { // ignore all characters but (.StackIsEmpty()) { S. T. } // terminate the string Str[I] = '\0'. { int Size = strlen(Str). // else.StackIsEmpty(). "ignore the '<-'" } // end for // now.Pop(). !T.GetStackTop() Write (Item) S. it is a backspace: pop a character and // increment the counter past the '-' of the backspace else if(!S.Push(S. stackClass S.h> #include <string.Push(Item) T. reconstruct the string while(!S. } for(int I = 0.Push(Str[I]).GetStackTop(). // Precondition: Backspaces are represented by the paired // sequence of characters '<' and '-'. } // else. // Postcondition: The string Str is corrected. ++I) { // if not a backspace. S. } and ) .h> const int MAX_LENGTH = 80. ].StackIsEmpty()) { T. T. typedef char stringType[MAX_LENGTH].GetStackTop()). add character to stack if(Str[I] != '<') S. I++.Pop(). void ReadAndCorrect(stringType Str) // Reads in a string of characters and uses the backspaces // in the string to correct the final contents of the string. for (int I = 0. typedef char stackItemType. [.Pop().61 { Item = T. ++I) { Str[I] = T. } // end ReadAndCorrect 6 Modified pseudocode solution for parenthesis matching to include three types ( S.Pop() } 5c #include <iostream. I < Size.

StackIsEmpty() ) report the string has matching parenthesis else report the string does not have matching parenthesis the same type or or ) the same type 7a Stack contents are x y x String rejected: unmatched characters b Stack contents are x y x String rejected: unmatched characters 7c Stack contents are y .62 if ( (Str[I] != ')') and (Str[I] != '(') and (Str[I] != '}') and (Str[I] != '{') and (Str[I] != ']') and (Str[I] != '[') ) ++I // process open parenthesis else if( (Str[I] == '(') or (Str[I] == '{') or (Str[I] == '[') ) { S.StackIsEmpty()) { S.GetStackTop(StackItem.Pop(Success) ++I } else // open and close parenthesis are not of Continue = FALSE } else // unmatched open and close parenthesis Continue = FALSE } // end while // Note that if Continue is FALSE.Push(Str[I]. // so no test of Continue is necessary if ( (I >= strlen(Str)) and S. Success) // try to match open and close parenthesis of if ( ((Str[I] == ')') and (StackItem == '(')) ((Str[I] == '}') and (StackItem == '{')) ((Str[I] == ']') and (StackItem == '[')) { S. Success) ++I } // process close parenthesis else if(!S. I < strlen(Str).

CreateStack() I = 0 Size = length of S Item = S[0] // determine which item is first in string while (I < Size) { if(S[I] == Item) S. remove item from stack // if the numbers match. // Returns true if S consists of a number of A's followed by the // same number of B's.Push(S[I]) I++ // should only be B's in rest of string while(I < Size and !S.StackIsEmpty and S[I] == 'B') . S. put in stack // else. // Returns true if the number of A's match the number of B's S. the stack should be empty return S.Push(Item) else S.StackIsEmpty() 8b IsInL(S) // Determines if the string S is in the language L. push onto stack while(I < Size and S[I] == 'A') S.63 String rejected: stack empty with input still to be scanned d Stack contents are x x x x String recognized e Stack contents are x y x x String rejected: stack not empty but all input scanned 8a IsInL(S) // Determines if the string S is in the language L.Pop() } // if match.CreateStack() I = 0 Size = length of S // while there are A's in the string.

E = 1. stack should be empty return boolean(S.op2 (-38) push result -38 // **DONE** 9c AB+C-DE*+ Character read Action Stack ---------------------------------------------------------------- . C = 12. Character read Action Stack ---------------------------------------------------------------A push A 7 B push B 7 3 C push C 7 3 12 + op2 = top of stack (12) 7 3 12 pop 7 3 op1 = top of stack (3) 7 3 pop 7 result = op1 + op2 (15) 7 push result 7 15 op2 = top of stack (15) 7 15 pop 7 op1 = top of stack (7) 7 pop result = op1 . D = -5.64 S. B = 3.op2 (-9) 7 push result 7 -9 D push D 7 -9 -5 * op2 = top of stack (-5) 7 -9 -5 pop 7 -9 op1 = top of stack (-9) 7 -9 pop 7 result = op1 * op2 (45) 7 push result 7 45 op2 = top of stack (45) 7 45 pop 7 op1 = top of stack (7) 7 pop result = op1 .StackIsEmpty() and I == Size) 9a For the following: ABC+- A = 7.op2 (-8) push result -8 // **DONE** 9b ABC-D*Character read Action Stack ---------------------------------------------------------------A push A 7 B push B 7 3 C push C 7 3 12 op2 = top of stack (12) 7 3 12 pop 7 3 op1 = top of stack (3) 7 3 pop 7 result = op1 .Pop() I++ // if A's and B's match.

( .( * empty A A A AB AB ABC ABC/ ABC/D ABC/D* ABC/D*- .( * .( / .op2 (-2) 10 10 12 10 12 10 10 stack (1) stack (-5) * op2 (-5) stack (-5) stack (-2) + op2 (-7) -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -5 -5 1 -5 1 -5 -5 -5 -5 -7 // **DONE** 10 a A–B+C ==> AB–C+ Stack Output Stack empty A A AB + AB+ AB-C empty AB-C+ Output b A/( B*C) ==> ABC*/ empty / / ( / ( / ( * / ( * / empty A A A AB AB ABC ABC* ABC*/ d A–( B+C) ==> ABC+– c ( A+B)*C ==> AB+C* Stack Output ( nothing ( A ( + A ( + AB ( AB+ * AB+ * AB+C empty AB+C* Stack Output empty .( / .65 A B + C - D E * + push A push B op2 = top of pop op1 = top of pop result = op1 push result push C op2 = top of pop op1 = top of pop result = op1 push result push D push E op2 = top of pop op1 = top of pop result = op1 push result op2 = top of pop op1 = top of pop result = op1 push result stack (3) stack (7) + op2 (10) 7 7 3 7 3 7 7 stack (12) stack (10) .( .

/ empty empty A A A AB AB ABC ABC ABCD ABCD* ABCD*+ ABCD*+ ABCD*+E ABCD*+E/ ABCD*+E/AB/C/DE+F*- 11a Fly from A to F: Action Reason Stack -----------------------------------------------------Push A Initialize A Push B Next adjacent city A B Push D Next adjacent city A B D Push E Next adjacent city A B D E Push I Next adjacent city A B D E I Push C Next adjacent city A B D E I C Pop C No unvisited neighbor A B D E I Pop I No unvisited neighbor A B D E Pop E No unvisited neighbor A B D Push F Next adjacent city A B D F **DONE** .( + .( .( .( + .( ABC/ .( / ABC .( AB .( / AB .( * ABC/ .( + .( + .* .( + .( * ABC/D ABC/D* empty ABC/D*Stack f A/B/C–(D+E)*F ==> AB/C/DE +F *– Output empty / / / / .( + .* A A AB AB/ AB/C AB/C/ AB/C/ AB/C/D AB/C/D AB/C/DE AB/C/DE+ AB/C/DE+ AB/C/DE+F g A*(B/C/D)+E ==> ABC/D/* E+ Stack Output empty A * A * ( A * ( AB * ( / AB * ( / ABC * ( ABC/ * ( / ABC/ * ( / ABC/D * ABC/D/ + ABC/D/* + ABC/D/*E empty ABC/D/*E+ Stack h A-( B+C*D)/ E ==> ABCD*+ E/– Output empty .( + * .66 e A–(B/C*D) ==> ABC/D*Stack Output empty A A ./ .( A .( .( + * .

67 b Fly from D to A: Action Reason Stack -----------------------------------------------------Push D Initialize D Push E Next adjacent city D E Push I Next adjacent city D E I Push C Next adjacent city D E I C Pop C No unvisited neighbor D E I Pop I No unvisited neighbor D E Pop E No unvisited neighbor D Push F Next adjacent city D F Push G Next adjacent city D F G Pop G No unvisited neighbor D F Pop F No unvisited neighbor D Push H Next adjacent city D H Pop H No unvisited neighbor D Pop D No unvisited neighbor **FAIL** c Fly from A to G: Action Reason Stack -----------------------------------------------------Push A Initialize A Push B Next adjacent city A B Push D Next adjacent city A B D Push E Next adjacent city A B D E Push I Next adjacent city A B D E I Push C Next adjacent city A B D E I C Pop C No unvisited neighbor A B D E I Pop I No unvisited neighbor A B D E Pop E No unvisited neighbor A B D Push F Next adjacent city A B D F Push G Next adjacent city A B D F G **DONE** d Fly from I to G: Action Reason Stack -----------------------------------------------------Push I Initialize I Push C Next adjacent city I C Push B Next adjacent city I C B Push D Next adjacent city I C B D Push E Next adjacent city I C B D E Pop E No unvisited neighbor I C B D Push F Next adjacent city I C B D F Push G Next adjacent city I C B D F G **DONE** e Fly from F to H: Action Reason Stack -----------------------------------------------------Push F Initialize F Push G Next adjacent city F G Push C Next adjacent city F G C Push B Next adjacent city F G C B Push D Next adjacent city F G C B D Push E Next adjacent city F G C B D E Push I Next adjacent city F G C B D E I Pop I No unvisited neighbor F G C B D E Pop E No unvisited neighbor F G C B D Push H Next adjacent city F G C B D H **DONE** .

Push(Z).Push(Item)).Pop() = S we know that each Push immediately followed by a Pop restores the stack to the s Furthermore.Push(X). and O = N. N = M.Push(Y). Then: .68 12a By the axiom (S. let M = S.

} Cur = TopPtr = NULL. } // end destructor . Cur = TopPtr. delete Cur.69 13 stackClass::~stackClass() // destructor { stackNodePtr Cur = TopPtr. while(Cur != NULL) { TopPtr = TopPtr->Next.

: half the contents of the stack and queue.QueueAdd(InputString[Index]. QSuccess) increment Index } // Index points to '$' or its value is greater than length of InputString while (Index < the length of InputString) // save the second half of the string { ++Index S.GetQueueFront(QItem.Pop(SSuccess) } while one of the structures is not empty if (Q. SSuccess).StackIsEmpty())) reject the string Q. since the back half stack has already been compared with the front half of the string as read from t 2 The following pseudocode performs the string recognition: queueClass Q.70 7 Queues 1 The while loop needs only to compare the contents of the stack and the queue up the string.QueueIsEmpty() && !S. i. SSuccess) } do // match the first half of the string with the second half of the string { if ((Q. S. if (the length of InputString == 0) reject the string // empty string not in L while (InputString[Index] != '$') // save the first half of the string { Q. int Index = 1.StackIsEmpty()) || (!Q.StackIsEmpty()) string is in L else reject the string .e.QueueIsEmpty()) Q.QueueIsEmpty() && S. if (QItem != SItem) reject the string if (!Q. boolean Qsuccess. QSuccess).Push(InputString[Index].QueueRemove(QSuccess) if (!S.StackIsEmpty()) S. stackClass S.GetStackTop(SItem.QueueIsEmpty() && S. SSuccess.

} // end switch // add to PE the operators remaining on the stack while the stack S is not empty { Add the top of stack S to the queue PE Pop the top item off of stack S } // end while 4 queueClass::queueClass(const queueClass& Q) { if (!Q. while (OrigCur != Q.BackPtr) { OrigCur = OrigCur->Next.BackPtr->Next. } // end while } // end if } // end copy constructor . NewCur->Next = NewHead. case operator: { while ( (stack S is not empty) && (the Top of stack S != '(') && (Precedence (StackTop()) >= Precedence (Ch))) { Add the top of stack S to the queue PE Pop the top item off of stack S } // end while Push Ch on to the stack S } break. NewCur->Next = new queueNode. case ')': { // pop down to the matching open parenthesis while (the Top of stack S != '(') { Add the top of stack S to the queue PE Pop the top item off of stack S } // end while Pop the top item off of stack S to remove open parenthesis } break. // pt to first node ptrType NewHead = new queueNode. NewCur->Item = OrigCur->Item.71 3 queueClass PE stackClass S // queue will hold the Postfix Expression for each character Ch in the infix expression switch (Ch) { case operand: Add to queue PE.QueueIsEmpty()) { // copy first node ptrType OrigCur = Q. // copy rest of list ptrType NewCur = NewHead. case '(': Push to stack S. NewHead->Item = OrigCur->Item. break. break. BackPtr = NewCur. NewCur = NewCur->Next. NewHead->Next = NewHead.

queueClass::Display(boolean& Success) { queueItemType Item. Cur = BackPtr = NULL. 7a The implementation of Display below uses a temporary queue to store the contents as they are removed and displayed. // output items on single line Temp. 6b Again. a ListDelete operation is called which removes the beginning of the array. is not fai equal value can be in the same queue. delete Cur. The QueueInsert operation. on the other hand. // get contents of original queue and display them while(!QueueIsEmpty()) { QueueDelete(Item. takes O(1) time since it simply resets the head of the list to the next node do node. that of saving item for comparison and terminating a loop on finding the same item. This "inefficient" procedure is necessary since there is no by the ADT queue that would inform a looping construct when to stop removing an and reinserting it in the same queue.72 5 queueClass::~queueClass() // destructor { if(BackPtr != NULL) { ptrType Cur = BackPtr->Next. In this case.QueueInsert(Item. forcing each remaining element of the array to be copied takes N-1 steps or O(N) time in comparison with the O(1) time required when the manipulated. in turn. Temp = NULL. Cur = Temp. the use of an ADT list with a pointer-based implementation is different of a linked list. } // end if } // end destructor // delete last node // reset last pointer to NULL 6a It is important to note that the use of an ADT list with an array-based impleme not the same as the use of an array in the ADT queue. the QueueDelete operation which calls. Success). while(Cur != BackPtr) { ptrType Temp = Cur->Next. Another possible solution. calls a ListInsert operation the entire list before it finds the last node at which point the new node can b time compared with the O(1) time for inserting into the circular linked list. Success). Since this is an O(1 optimal and comparable to that of the ADT queue which directly manipulates a da QueueDelete operation. Then. however. } // end while delete Cur. if(Success) { cout << Item << " ". The QueueInsert operation operation which adds a new item to the end of the array. // start with front end of queue BackPtr->Next = NULL. } } // end while // reconstruct original queue . queueClass Temp. the contents of the temporary queue ar into the original.

Success). } // end while cout << endl.e. } // end Display // terminate output with an end of line 7b queueClass::Display(boolean& Success) { if(BackPtr != NULL) // if there is a queue. // the character before the backspace as well as the backspace is removed // from the string.QueueDelete(Item.DequeRemoveFront() String[Index] = Item Index++ } String[Index] = NULL // terminate the string .DequeRemoveRear() // remove previous character from deque Index++ // increment Index past rest of backspace } else ignore backspace character } Index = 0 // now reset the string from the deque while(!Q.QueueIsEmpty()) { Temp.DequeAddRear(String[I]) else if(Index > 0 and String[Index] == '<') { Q. // display items separated by spaces Cur = Cur->Next. Q. "<-") is found.CreateDeque() Length = length of String for(Index = 0 through Length) { if(String[Index] != '<') // if this is not the backspace character Q. if a backspace (i. } // end while cout << Cur->Item << endl.DequeIsEmpty) { Q. { ptrType Cur = BackPtr->Next. Success).73 while(!Temp... // start at the front of the queue while(Cur!= BackPtr) { cout << Cur->Item << " ". if(Success) QueueInsert(Item.GetDequeFront(Item) Q. } // end if } // end Display // terminate output with last element 8 ReadAndCorrect(String) // Reads a string of characters and.

74 9 E: A|5|9 Q: Simulation begins E: A|7|5 D|14 Processing arrival at time 5 Q: 5|9 E: A|14|5 D|14 Q: 5|9 7|5 E: D|14 A|30|5 Q: 5|9 7|5 14|5 E: D|19 A|30|5 Q: 7|5 14|5 E: D|24 A|30|5 Q: 14|5 E: A|30|5 Q: empty E: A|32|5 D|35 Q: 30|5 E: A|34|5 D|35 Q: 30|5 32|5 E: D|35 Q: 30|5 32|5 34|5 E: D|40 Q: 32|5 34|5 E: D|45 Q: 34|5 E: empty Q: empty Processing arrival at time 14 Processing arrival at time 7 if there is a tie. this arriva into place. overruling the insertion policy used by a queue.5 departure event time for sorting purposes.g. departure events for the same time may not be enforced b special precautions are taken (e. remembering of course to remove this .: introducing a "perturbation factor" of 0. The event list could be an ADT list although all of the functionality for sorti be externally defined. The event list could also be an ADT sorted list except ordering of arrival vs. arrivals are processed first Processing departure at time 14 Processing departure at time 19 Processing departure at time 24 Processing arrival at time 30 Processing arrival at time 32 Processing arrival at time 34 Processing departure at time 35 Processing departure at time 40 Processing departure at time 45 10 The event list cannot be an ADT queue since the ProcessArrival operation places event list before it reads the input file for the next arrival event and places time may precede the departure time of an event already in the list.

CreateQueue() if (Q.CreateQueue()).QueueRemove() = Q.QueueInsert(Item)).CreateQueue()).QueueIsEmpty() == FALSE) then (Q. the "outermost" representation.QueueRemove()). we have Reason initialize next unvisited adjacent city next unvisited adjacent city no unvisited adjacent city next unvisited adjacent city no unvisited adjacent city next unvisited adjacent city next unvisited adjacent city no unvisited adjacent city no unvisited adjacent city next unvisited adjacent city no unvisited adjacent city next unvisited adjacent city no unvisited adjacent city no unvisited adjacent city no unvisited adjacent city Queue Contents (front to rear) P PR PRW RW RWX WX WXS WXSY XSY SY SYT YT YTZ TZ Z empty Add P Add R Add W Remove P Add X Remove R Add S Add Y Remove W Remove X Add T Remove S Add Z Remove Y Remove T Remove Z 12a To simplify notation. QueueFront is treated as a valued function that returns the the argument Success is omitted. QueueFront returns the first item that was added to a queue. Without it.QueueInsert(Item)).QueueIsEmpty() == FALSE) then (Q.e.QueueInsert(Item)).e Insert of the representation.CreateQueue()). i..QueueRemove() = (Q.CreateQueue()). 12b Any queue can be represented with only Insert operations. QueueFront could be shown to be equal to error for any queu StackTop returns the last item that was added to a stack.75 11 Action Starting at node P.QueueFront() = Q. i.QueueFront() = error ((Q.QueueFront() = Item Recursive step: if (Q. QueueFront must be defined to "skip over" app finds the first (innermost) one.QueueRemove() = error ((Q. Thus.QueueInsert(Item)). shown by these axioms (Q. Base cases: (Q.QueueFront() The QueueIsEmpty test is needed to prevent the recursive axiom from being applied one item.QueueInsert(Item) .

The ballClass DisplayStatistics() function can invoke the inherited sphereClass Area() function by writing sphereClass::Area(). float NewInkAmount). // end class .0. penClass(colorType NewColor. boolean IsClickedOn(). boolean& Success). since Ball is of type ballClass which sphereClass.h // This file contains the interface for an object of type penClass. boolean ClickedOn. ==> calls the sphereClass version SPtr = &Ball. penClass(const penClass& P). 2 The completed penClass which has-a member ballClass. // pen operations boolean ClickPen(). private: ballClass Ball. #include <iostream. GREEN}. void Write(int LineLength. RED. 1b SPtr->DisplayStatistics(). BLUE. enum colorType {BLACK. }. class penClass { public: // constructors penClass(). boolean IsEmpty().h> #include "ball. float NewInkAmount. and DisplayStatistics was not declared as a virtual member function in SPtr accesses the functionality inherited from sphereClass rather than the redefine ballClass.76 8 Class Relationships 1a Each DisplayStatistics() function invokes the Area() function in its own class. ==> calls the ballClass version SPtr is defined as a pointer to sphereClass. float GetInkAmount(). float InkAmount. boolean& Success). float ComputeInkPerLineLength(int LineLength). SPtr->DisplayStatistics(). ==> still calls the sphereClass version BPtr->DisplayStatistics(). void ReplaceInk(colorType NewColor. void FillPen(colorType NewColor. colorType GetColor(). virtual ~penClass(). // File: pen. float NewInkAmount). colorType Color.h" const float INK_CAPACITY = 10.

ComputeInkPerLineLength(LineLength).cpp const int BALL_RADIUS = 3. InkAmount = NewInkAmount. } // end constructor penClass::penClass(colorType NewColor. . InkAmount = P. Success = (Amount >= 0. InkAmount = INK_CAPACITY. ClickedOn = false. if(Success) InkAmount = Amount. // call copy constructor for member class Color = P.Ball).Color.0). boolean& Success) { float Amount = InkAmount .ClickedOn. penClass::penClass() { Ball(BALL_RADIUS. ClickedOn = P. // call constructor for member class Color = NewColor. return ClickedOn. } // end constructor penClass::penClass(const penClass& P) { Ball(P. } // end Write void penClass::FillPen(colorType NewColor. } // end IsEmpty void penClass::Write(int LineLength. float NewInkAmount) { Ball(BALL_RADIUS.0). "").77 // Implementation of pen. ""). boolean& Success) { // prevent ink from mixing in pen Success = ((NewColor == Color) || (NewColor != Color && IsEmpty())). // call constructor for member class Color = BLACK. } // end copy constructor boolean penClass::ClickPen() { ClickedOn = !ClickedOn. } // end ClickPen boolean penClass::IsClickedOn() { return ClickedOn. float NewInkAmount.InkAmount. } // end IsClickedOn boolean penClass::IsEmpty() { return (InkAmount <= 0. ClickedOn = false.

78 if(Success) { Color = NewColor. if(InkAmount > INK_CAPACITY) InkAmount = INK_CAPACITY. if(InkAmount > INK_CAPACITY) InkAmount = INK_CAPACITY. } // end if } // end FillPen void penClass::ReplaceInk(colorType NewColor. InkAmount += NewInkAmount. . } // end ReplaceInk colorType penClass::GetColor() { return Color. InkAmount = NewInkAmount. float NewInkAmount) { Color = NewColor.

0 * PI * R * R * R)/3. } // end Volume void sphereClass::DisplayStatistics() { cout << "\nRadius = " << Radius() << "\nDiameter = " << Diameter() << "\nCircumference = " << Circumference() << "\nArea = " << Area() << "\nVolume = " << Volume() << endl. } // end Circumference double sphereClass::Area() { return 4.0 * Radius().79 Implementation of the modified sphere class member functions is as follows: sphereClass::sphereClass(double R): equidistantClass(R) { } // end constructor sphereClass::sphereClass(): equidistantClass(1. return (4.0) { } // end default constructor sphereClass::sphereClass(const sphereClass& S): equidistantClass(S.Radius()) { } // end copy constructor sphereClass::~sphereClass() { } // end destructor double sphereClass::Diameter() { return 2. } // end DisplayStatistics .0. } // end Diameter double sphereClass::Circumference() { return PI * Diameter(). } // end Area double sphereClass::Volume() { double R = Radius().0 * PI * Radius() * Radius().

circleClass(const circleClass& S).0 * Radius(). } // end Diameter double circleClass::Circumference() { return PI * Diameter(). virtual ~circleClass(). }.80 3b A circle class is similar to the sphere class without a volume member.0) { } // end default constructor circleClass::circleClass(const circleClass& S): equidistantClass(S. virtual double Circumference(). virtual double Area().h> #include "equidist.Radius()) { } // end copy constructor circleClass::~circleClass() { } // end destructor double circleClass::Diameter() { return 2. virtual double Diameter(). } // end Circumference double circleClass::Area() { return PI * Radius() * Radius(). class circleClass: public equidistantClass { public: circleClass(double R).h" const double PI = 3. // end class Implementation of the circle class member functions is as follows: circleClass::circleClass(double R): equidistantClass(R) { } // end constructor circleClass::circleClass(): equidistantClass(1. circleClass(). virtual void DisplayStatistics(). } // end DisplayStatistics . } // end Area void circleClass::DisplayStatistics() { cout << "\nRadius = " << Radius() << "\nDiameter = " << Diameter() << "\nCircumference = " << Circumference() << "\nArea = " << Area() << endl.14159. #include <iostream.

TheRadius. none of the fun implemented.0 * TheRadius. virtual double Diameter(). virtual ~sphereClass(). #include <iostream. }. } // end copy constructor sphereClass::~sphereClass() { } // end destructor double sphereClass::Diameter() { return 2. sphereClass(). // end class 3d The following is a revised version of sphereClass where the data member TheRadiu member of the abstract base class equidistantClass. sphereClass(const sphereClass& S). virtual double Radius() const = 0. virtual double Circumference().81 3c When the data member TheRadius is made a protected variable. // end class Implementation of the modified sphere class member functions is as follows: sphereClass::sphereClass(double R) { TheRadius = R.. virtual double Area().h" const double PI = 3. virtual void DisplayStatistics() const = 0.h> #include "equidist. class equidistantClass // abstract base class { public: virtual void SetRadius(double NewRadius) = 0. } // end Diameter double sphereClass::Circumference() . then any derived cla sphereClass will have access to this data member. } // end constructor sphereClass::sphereClass() { TheRadius = 1.e. class sphereClass: public equidistantClass { public: sphereClass(double R).14159. virtual void DisplayStatistics(). }.0. Thus it is possible for all of equidistantClass to be redeclared as pure virtual functions. virtual double Volume(). } // end default constructor sphereClass::sphereClass(const sphereClass& S) { TheRadius = S. i. protected: double TheRadius.

82

{ return PI * Diameter(); } // end Circumference double sphereClass::Area() { return 4.0 * PI * TheRadius * TheRadius; } // end Area double sphereClass::Volume() { double R = TheRadius; return (4.0 * PI * R * R * R)/3.0; } // end Volume void sphereClass::DisplayStatistics() { cout << "\nRadius = " << TheRadius << "\nDiameter = " << Diameter() << "\nCircumference = " << Circumference() << "\nArea = " << Area() << "\nVolume = " << Volume() << endl; } // end DisplayStatistics

The following is a revised version of circleClass where the data member TheRadiu of the abstract base class equidistantClass.
#include <iostream.h> #include "equidist.h" const double PI = 3.14159; class circleClass: public equidistantClass { public: circleClass(double R); circleClass(); circleClass(const circleClass& S); virtual ~circleClass(); virtual double Diameter(); virtual double Circumference(); virtual double Area(); virtual void DisplayStatistics(); }; // end class

Implementation of the circle class member functions is as follows:
circleClass::circleClass(double R) { TheRadius = R; } // end constructor circleClass::circleClass() { TheRadius = 1.0; } // end default constructor

circleClass::circleClass(const circleClass& S)

83

{ TheRadius = S.TheRadius; } // end copy constructor circleClass::~circleClass() { } // end destructor double circleClass::Diameter() { return 2.0 * TheRadius; } // end Diameter double circleClass::Circumference() { return PI * Diameter(); } // end Circumference double circleClass::Area() { return PI * TheRadius * TheRadius; } // end Area void circleClass::DisplayStatistics() { cout << "\nRadius = " << TheRadius << "\nDiameter = " << Diameter() << "\nCircumference = " << Circumference() << "\nArea = " << Area() << endl; } // end DisplayStatistics

4

a

IsBlank should be protected. Its primary use is as a utility operation used algExprClass. At the same time, it is unnecessary for a client using an instance access to the IsBlank method.

b The client declaring InfixExpr cannot invoke EndExpression() since it is a prote and is inaccessible to the client. c Add the keyword virtual before the prototype for IsExpression() in the algExprCl

5

Assume the following objects have been declared in a main function:
stringClass S; algExprClass AE; infixClass InE;

a Length() is a public member function declared within stringClass that, although redefined in any of the descendents of stringClass. Thus, all three objects may correctly. b AE calls the function declared within the algExprClass definition correctly. declared within the infixClass definition correctly. c Only InE can invoke the member function ValueOf().
I

d
// function declaration

84

void F(algExprClass& E); . . . void main() { infixClass InE; // declare object of derived type . . F(InE); // legal call }

6a

The only methods that change are ListInsert, ListDelete, and ListRetrieve.
#include <iostream.h> #include "boolean.h" typedef int listItemType; // type of list item class baseFrontListClass // abstract base class { public: virtual ~baseFrontListClass(); // destructor virtual int ListLength() = 0; virtual boolean ListIsEmpty() = 0; virtual void DisplayList() = 0; }; // end class struct listNode; typedef listNode* ptrType; class frontListClass : public baseFrontListClass { public: // constructors and destructor: frontListClass(); frontListClass(const frontListClass& L); virtual ~frontListClass(); virtual virtual virtual virtual virtual virtual virtual int ListLength() const; boolean ListIsEmpty() const; void ListInsert(listItemType NewItem, boolean& Success); void ListDelete(boolean& Success); void ListRetrieve(listItemType& DataItem, boolean& Success); void DisplayList() const; void operator=(const frontListClass& L);

protected: void SetSize(int NewSize); // sets Size ptrType ListHead() const; // returns Head void SetHead(ptrType NewHead); // sets Head listItemType ListItem(ptrType P) const; // returns a list item ptrType ListNext(ptrType P) const; // returns list node's Next ptr private: ptrType PtrTo(int Position) const; int Size; // number of items on the list ptrType Head; // pointer to the linked list }; // end class

Implementation of the front list class member functions are as follows:

// insert new node at beginning of list NewPtr->Next = Head.Head->Next.85 struct listNode { listItemType Item. ptrType Next. }.Head == NULL) Head = NULL.Head->Item. } // end ListLength void frontListClass::ListInsert(listItemType NewItem. for ( int Position = 1. . NewPtr->Item = NewItem. // original list is empty else { // copy first node Head = new listNode. // create new node and place NewItem in it ptrType NewPtr = new listNode.Size) { if (L. Success). Head->Item = L. NewPrev = NewPrev->Next. Position <= ListLength(). // new list pointer for (ptrType OrigCur = L. Head(NULL) {} // end constructor frontListClass::frontListClass(const frontListClass& L): Size(L. Success = true. OrigCur = OrigCur->Next) { NewPrev->Next = new listNode. } // end ListIsEmpty int frontListClass::ListLength() const { return Size. OrigCur != NULL. ++Position) ListDelete(1. } // end if } // end copy constructor frontListClass::~frontListClass() { boolean Success. NewPrev->Item = OrigCur->Item. } // end for NewPrev->Next = NULL. Head = NewPtr. } // end destructor boolean frontListClass::ListIsEmpty() const { return boolean(Size == 0). // copy rest of list ptrType NewPrev = Head. // end struct baseFrontListClass::~baseFrontListClass() {} // end destructor frontListClass::frontListClass(): Size(0). boolean& Success) { ++Size.

} // end ListRetrieve void frontListClass::DisplayList() const { for (ptrType Cur = Head. } // end ListDelete void frontListClass::ListRetrieve(listItemType& DataItem. Cur != NULL. Cur = NULL. // delete the first node from the list and return it to the system Cur = Head. if(Head != NULL) Head = Head->Next. boolean& Success) { DataItem = Head->Item. Success = true. delete Cur. } // end ListItem ptrType frontListClass::ListNext(ptrType P) const { return P->Next. } // end ListHead void frontListClass::SetHead(ptrType NewHead) { Head = NewHead. Cur = Cur->Next) cout << Cur->Item << " ".86 } // end ListInsert void frontListClass::ListDelete(int Position. } // end DisplayList void frontListClass::SetSize(int NewSize) { Size = NewSize. boolean& Success) { ptrType Cur. --Size. } // end ListNext . Cur->Next = NULL. cout << endl. // get data in front node Success = true. } // end SetHead listItemType frontListClass::ListItem(ptrType P) const { return P->Item. } // end SetSize ptrType frontListClass::ListHead() const { return Head.

h" typedef listItemType stackItemType.h for the ADT stack. { if ( (Position < 1) || (Position > ListLength()) ) return NULL. . returns NULL.87 ptrType frontListClass::PtrTo(int Position) const // Locates a specified node on a list. Head->Item = L. for (int Skip = 1. If // Position < 1 or Position > the number of nodes on the list.Head->Next. OrigCur != NULL. } // end if } // end PtrTo void frontListClass::operator=(const frontListClass& L) { Size = L. // new list pointer for (ptrType OrigCur = L. if (L. // original list is empty else { // copy first node Head = new listNode. Skip < Position. // Postcondition: Returns a pointer to the node at position Position. } // end if } // end operator= 6b // ************************************************************* // Header file StackFL. // Derived from ADT frontList // ************************************************************* #include "frontList. // copy rest of list ptrType NewPrev = Head. NewPrev->Item = OrigCur->Item. ++Skip) Trav = Trav->Next. NewPrev = NewPrev->Next. // above could be combined into one for return Trav.Head->Item.Size. } // end for NewPrev->Next = NULL.Head == NULL) Head = NULL. // Precondition: Position is the number of the desired node. else // count from the beginning of the list { ptrType Trav = Head. OrigCur = OrigCur->Next) { NewPrev->Next = new listNode.

// default constructor stackClass(const stackClass& S). boolean& Success) const.stackClass inherits what it // needs from frontListClass }. void Pop(stackItemType& StackTop. void GetStackTop(stackItemType& StackTop. // ************************************************************* #include "StackFL. // copy constructor ~stackClass(). // Derives from the ADT frontList. // destructor // stack operations: boolean StackIsEmpty() const. boolean& Success) { ListInsert(NewItem. boolean& Success).cpp for the ADT stack. } // end Pop . private: // no private members -. } // end StackIsEmpty void stackClass::Push(stackItemType NewItem. void Push(stackItemType NewItem. // end class // end header file // ************************************************************* // Implementation file StackFL. } // end Push void stackClass::Pop(boolean& Success) { ListDelete(Success).88 class stackClass: public frontListClass { public: // constructors and destructor: stackClass(). boolean& Success). Success).h" // header file stackClass::stackClass() { } // end default constructor stackClass::stackClass(const stackClass& S): frontListClass(S) { } // end copy constructor stackClass::~stackClass() { } // end destructor boolean stackClass::StackIsEmpty() const { return ListIsEmpty(). void Pop(boolean& Success).

void SetStudentID(stringType ID). class personClass { public: // constructors and destructor: personClass(). void SetName(stringType N). gender. int Age() const. if(Success) ListDelete(Success). virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual private: stringType StudentID() const. but adds year of grad list of classes. virtual ~studentClass(). personClass(const personClass& P). char Gender() const. } // end Pop void stackClass::GetStackTop(stackItemType& StackTop. void AddClass(listType C). } // end GetStackTop // end of implementation file 7 The person class has basic information like name. boolean& Success) const { ListRetrieve(StackTop. int YearOfGraduation() const. void DisplayClasses(). void DisplayPerson(). // end class The student class has the basic person class information. virtual virtual virtual virtual virtual virtual virtual stringType Name(). studentClass(const studentClass& P). void RemoveClass(listType C). void SetGender(char G). }. char TheGender. age. void SetMajor(stringType M). void SetAge(int A). Success). boolean& Success) { ListRetrieve(StackTop. void UpdateClass(listType OldClass. listType NewClass). int TheAge. etc.89 void stackClass::Pop(stackItemType& StackTop. void YearOfGraduation(int YOG). stringType Major() const. . Success). private: stringType TheName. virtual ~personClass(). float GradePointAverage() const. class studentClass: public personClass { public: // constructors and destructor: studentClass().

private: thesisType ThesisOption.TopPtr == NULL) TopPtr = NULL. // end class 8 The overloaded assignment operator for the stack template is as follows: stackClass& stackClass::operator=(const stackClass<TYPE>& S). ptrType NewPrev. // point to next node on list NewPrev = TopPtr. enum thesisType {MASTERS_NON_THESIS. TopPtr->Item = S. TheMajor. // end class The graduate student class has the basic student class information.TopPtr->Next. NewPrev = NewPrev -> Next. listClass TheClassList. return (*this). virtual ~gradStudentClass(). // copy rest of list OrigCur = S. }. gradStudentClass(const gradStudentClass& G). // overloaded assingment operator template<class TYPE> stackClass<TYPE>& stackClass<TYPE>::operator=( const stackClass<TYPE>& S) // overloaded assignment operator { ptrType OrigCur. } // end while NewPrev->Next = NULL. else { // copy first node TopPtr = new stackNodeType. OrigCur = OrigCur->Next. MASTERS_THESIS. // new list pointer while (OrigCur != NULL) { NewPrev->Next = new (stackNodeType). virtual thesisType ThesisOption() const. class gradStudentClass: public studentClass { public: // constructors and destructor gradStudentClass(). virtual void SetThesisOption(thesisType T).TopPtr->Item. NewPrev->Item = OrigCur->Item. but adds an the thesis option. if (S. DOCTORATE}. }. } // end if } // end stackClass::operator= . int TheYearOfGraduation.90 stringType TheStudentID.

// overloaded assingment operator template<class TYPE> queueClass<TYPE>& queueClass<TYPE>::operator=( const queueClass<TYPE>& Q) // overloaded assignment operator { if (!Q. NewCur->Next = new queueNode.QueueIsEmpty()) { // copy first node ptrType OrigCur = Q. NewCur = NewCur->Next. // pt to first node ptrType NewHead = new queueNode. BackPtr = NewCur. NewHead->Item = OrigCur->Item.BackPtr->Next. NewCur->Item = OrigCur->Item. NewHead->Next = NewHead.91 The overloaded assignment operator for the queue template is as follows: queueClass& queueClass::operator=(const queueClass<TYPE>& S). NewCur->Next = NewHead. while (OrigCur != Q. return (*this). } // end queueClass::operator= .BackPtr) { OrigCur = OrigCur->Next. // copy rest of list ptrType NewCur = NewHead. } // end while } // end if else BackPtr = NULL.

+ [2*(N-1) + 1] + 1 the last being the comparison that bumps us out of the outer for loop. Since x is independent of N by hypothesis. + c[0]. N-1]. the stack is implemented with an array. the overall total of comparisons is: [2*(0) + 1] + [2*(1) + 1] + . . Then.e. this confers a 2:1 advantage for selection sort.92 9 Algorithm Efficiency and Sorting 1 a b c d e f g h i j O(N) O(N) O(N) O(N) O(1) O(N) Worst case: O(log N) O(N log N) O(N) Worst case: O(N) Worst case: the entire list must be traversed first. The overall computation then requires c passes for each of the N pa loop and the middle loop requires N passes in turn for each of the N passes of the overall time complexity is c*N*N or O(N2). The first N of them precede another pass the for loop and the last one fails the test and forces the loop to stop. i. Then..: J = [0. Wi comparison is made by the if statement so 2*J + 1 total comparisons are made by Since J increases from 0 to N-1... the innermost loop must 10 * f(x) steps to compute.3 steps for the worst case while insertion For large N. the queue is implemented with an ADT list. 5 Let c = c[n] + c[n-1] + . Suppose the computation in the innermost for loop take some function f(x). . 2 The algorithm is O(N2).. This is due to the co term in the count of comparisons and data moves each algorithm performs.. f(x) <= c*x n and is thus O(xn). For each of the N passes through the loop. where x is independent of N. . As de selection sort takes N2/2 + 5*N/2 . N −1 i0 = Thus th ∑ (2i+ 1)+ 1 = 2∑ i+ N + 1 = 2N (N − 1)/2 + N + 1 = N (N − 1)+ N + 1 = N 2 − N + N +1 = O (N 2 ) i=0 N −1 4 Selection sort is moderately faster than Insertion sort. 3 The outer loop makes N+1 comparisons. J+1 comparisons are made by the test being the value of the index for the for loop. the val constant c..

80 40 60 insert 25 40 80 copy 60 and shift 80 40 80 insert 60 40 60 copy 40 and shift 60.93 6 Let a > 1 and b > 1. Then. bubble sort and insertion sort to sorted and inverse sort Inverse sorted array: i selection sort action initial array first swap second swap no swap array 8 2 2 2 6 6 4 4 4 4 6 6 2 8 8 8 8 6 4 2 . final array 80 20 20 20 20 20 20 20 20 20 20 80 80 80 40 40 25 25 25 25 25 25 25 25 25 80 80 80 60 60 40 9a Apply selection sort. The proof is symmetrical for f(N) is O(logb N) => f(N) is O(loga N). f(N) is O(loga N) = c * O(logb N) = O(logb N). Define c = 1/(logb a). 7 Any operations outside of loops contribute a constant amount of time and would n behavior of the selection sort for a large N. 80 80 60 insert 40. 8 Apply insertion sort to: array 40 40 80 80 40 40 40 40 40 40 20 80 40 25 60 40 action 60 initial array 40 60 copy 20 on top of itself 40 60 copy 40 and shift 80 40 60 insert 40 40 60 copy 25 and shift 40. To show: f(N) is O(loga N) => f(N) is O(logb N).

insert 4 copy 2 and shift 4. 2 4 6 2 2 2 2 2 8 8 action initial array copy 6 and shift 8 insert 6 copy 4 and shift 6. bubble sort action initial array pass 1 array 8 6 6 6 4 4 4 2 2 2 6 8 4 4 6 2 2 4 4 4 4 4 8 2 2 6 6 6 6 6 2 2 2 8 8 8 8 8 8 8 pass 2 pass 3 iii. action initial array 2 copy 2 on itself 2 copy 4 on itself 2 copy 6 on itself 2 1000 5 10 20 30 40 50 60 array 4 4 4 4 6 6 6 6 8 8 8 8 10 .94 ii. insertion sort array 8 6 4 8 8 4 6 8 4 8 6 8 6 4 6 8 4 48 6 6. bubble sort action initial array pass 1 array 2 2 2 2 4 4 4 4 6 6 6 6 8 8 8 8 ii insertion sort i. insert 2 9b Sorted array: i 2 4 6 8 selection sort action initial array no swap no swap no swap array 2 2 2 2 4 4 4 4 6 6 6 6 8 8 8 8 ii.

X = Y. int N) { int L. int Size) { int Max = 0. Cur<Size. } // end IndexOfLargest 12 The following recursive version of SelectionSort uses the IndexOfLargest and Swa previous solution. InA[Last]). } // end for loop return Max. for (int Last=N-1. Cur++) { if(A[Cur]. Swap(InA[L]. } // end for loop return Max. for (int Cur=1. const int MAX_SIZE = 8. int Age. Y = Temp. int IndexOfLargest(dataType A. int Size) { int Max = 0. } // end IndexOfLargest void Swap(dataType& X. Last). Cur<Size. for (int Cur=1.Key > A[Max].95 11 Revision of SelectionSort using the data member Key as a sort key: const int MAX_NAME_LEN = 20. // sorting key }. } // end Swap void SelectionSort(dataType InA[]. dataType& Y) // standard Swap function { dataType Temp = X. } } // end SelectionSort Revision of IndexOfLargest where dataType is a class with member function SortKe int IndexOfLargest(dataType A. Cur++) { if(A[Cur]. .SortKey() > A[Max]. typedef struct node dataType. Last>0.Key) Max = Cur. Last--) { L = IndexOfLargest(InA. struct node { char Name[MAX_NAME_LEN].SortKey()) Max = Cur. int Key. float GPA.

Loc--. int Index. InsertionSort(A. } // end if } // end Bubble void BubbleSort(dataType A[]. int N) { if(N > 1) { boolean Sorted = true. N = size of A. } // end if } // end InsertionSort 13 Trace of mergesort on the array 20 80 40 25 60 30: 20 80 40 25 60 30 . int Unsorted. { if(Unsorted < N) { dataType NextItem = A[Unsorted]. Sorted = false. NextItem. Index+1. InA[Last]). Sorted). NextItem.96 void SelectionSort(dataType InA[]. } } // end SelectionSort A recursive version of BubbleSort: void Bubble(dataType A[]. A[Loc] = NextItem. N. Sorted). N. boolean& Sorted) // helping function { if(N > Index+1) { int NextIndex = Index + 1. Last). int& Loc) { if((Loc > 0) && (A[Loc-1] > NextItem)) { A[Loc] = A[Loc-1]. } Bubble(A. Swap (InA[Current]. if(!Sorted) BubbleSort(A. if(A[Index] > A[NextIndex]) { Swap(A[Index]. Loc). } // end if } // end Shift void InsertionSort(dataType A[]. int N) // Preconditions: Unsorted = 1. int N. SelectionSort (InA. Bubble(A. A[NextIndex]). Shift(A. Unsorted+1. Shift(A. if (Last > 0) { Current = IndexOfLargest(InA. 0. } // end if } // end BubbleSort A recursive version of InsertionSort. Last-1). // Postcondition: A is sorted in ascending order. Here the sorted region is at the end rather than at the beginning of the array. void Shift(dataType A[]. int Loc = Unsorted. dataType NextItem. N). N-1). Loc). int Last) { int Current.

Only the dimension of the array. maintain the lo yet unmerged items in the first and second sub-arrays respectively. the selected index is incremente made until one index or the other exceeds the length of the sub-array. whereupon the other sub-array are copied over. first for loop of Merge where two index values. 14b The items in the array are only swapped during the call to Merge when the two so from the previous two calls to Mergesort are merged together. First1 and First2. The lesser by these indices is merged into the new array. More specifically. Approximating the .97 20 80 40 25 60 30 RECURSIVE STEPS 20 80 40 25 60 30 20 80 40 25 60 30 20 80 40 25 60 30 MERGE STEPS 20 40 80 25 30 60 20 25 30 40 60 80 14a The recursive calls to Mergesort simply subdivide the array A until it can be di when the size of the subdivided array is one element) at which point the simpler a larger array. The values wit impact on this mechanism. the array will not be sor 17 If the actual median of the elements is selected as the pivot at each step. 15 Trace of quicksort on the array 20 80 40 25 60 10 15: pivot | 20 80 20 15 20 15 S1 15 10 | 20 | 25 60 40 40 10 25 25 25 60 60 60 S2 10 10 40 15 80 80 40 80 Item 20 is now in its correct position and quicksort is now applied recursively 16 All of the actual sorting is done in the merge steps. the relative values of its fir whether another pair of recursive calls to Mergesort takes place. the recursive calls will be made to sort the original N elements.

18 We need only add a single comparison to guard the Swap statement. D S2. ST) (CJ) (DQ. The third pass would form sorted sections of size 4 by merg 2. DQ. HQ. The second pass would form sorted sections of size 2 by sections of size 1. DQ. there will be N / 2k sorted pass. CJ. HQ) (DK) by rank sorted S2. D6. S4. C9. ST. . DK) sorted by suit S2. D6. S4. D6. CJ. HT. DQ. DK combined (sorted) b All 10’s would become sorted before all 2’s in rank because of the 0 in t would get sorted before or after all of the suits. H. depending on the precedence a with respect to S. ST) (HT. HT. HQ) (C9. HQ.98 values can still lead to a worst case of N recursive calls. HQ. C9. HT. size 1 (already sorted). 21 Insertionsort and Mergesort can be implemented to be stable. In the first pass. ST. Heapsort and Quicksort a 22 a Trace the radix sort for the following cards: S2. ST. 20 An iterative version of Mergesort would work from the bottom up by dividing the a of sorted sections and merging the runs to form larger runs. DK original cards (S2) (S4) (D6) (C9) (HT. C. and so on. D6. S4. CJ) (D6. DQ. CJ. S4. S4. HT. An invariant: given N items to sort. and D. DK combined (S2. C9. CJ. C9.

// Postconditions: the binary tree has been destroyed. // Postconditions: an empty binary tree is prepared to accept new // insertions. RootData() // Returns the data item in the root of a nonempty binary tree. BinaryTreeIsEmpty() // Determines whether a binary tree is empty. // Postconditions: the left child of the root of the tree contains // NewItem and Success is true. Else. DestroyBinaryTree() // Destroys a binary tree. as its left and right subtrees. . CreateBinaryTree(RootItem. // Preconditions: a binary tree has been created. // Preconditions: a nonempty binary tree has been created. // Preconditions: a binary tree has been created. // Postconditions: the tree whose root contains RootItem and whose left // child is LeftTree and whose right child is RightTree has been created. RightTree) // Creates a binary tree whose root contains RootItem and has LeftTree // and RightTree. // Preconditions: RootItem is of a type recognizable by ADT binary tree // operations. // Postconditions: the tree contains a root node containing RootItem. respectively. if the tree is empty. LeftTree and RightTree are both ADT binary trees. Success is false. However. false otherwise. AttachLeft(NewItem. // Postconditions: returns true if the tree is empty. // Preconditions: none. // Preconditions: RootItem is of a type recognizable by ADT binary tree // operations. if // the tree is not empty. // Preconditions: a nonempty binary tree has been created. // Postconditions: the contents of the root node are returned. // Preconditions: a binary tree has been created. creates a root // node whose data item is NewItem and inserts the new node into the // tree. SetRootData(NewItem) // Replaces the data item in the root of the binary tree with NewItem.99 10 Trees 1 The following lists the operations for the ADT binary tree: CreateBinaryTree() // Creates an empty binary tree. // Postconditions: the root node of the binary tree contains NewItem. Success) // Attaches a left child containing NewItem to the root of a binary tree. CreateBinaryTree(RootItem) // Creates a one-node binary tree whose root contains RootItem. // Success indicates whether the operation was successful. LeftTree.

// Postconditions: the right child of the root of the tree contains // NewItem and Success is true. Else. AttachRightSubtree(RightTree. AttachLeftSubtree(LeftTree. RightTree is empty and Success // is false. Success) // Attaches a right child containing NewItem to the root of a // binary tree.100 AttachRight(NewItem. LeftTree is empty and Success is // false. Success indicates whether the operation was successful. Success indicates whether the operation was successful. InorderTraverse(Visit) // Traverses a binary tree in inorder and calls the function Visit once . PreorderTraverse(Visit) // Traverses a binary tree in preorder and calls the function Visit once // for each node. // Preconditions: a nonempty binary tree has been created and LeftTree // is an ADT binary tree. Else. Success is false. // Preconditions: a nonempty binary tree has been created. // Preconditions: a nonempty binary tree has been created. // Postconditions: the right child of the root is removed and stored in // RightTree and Success is true. // Preconditions: a nonempty binary tree has been created. Else. Success indicates whether the operation was successful. // Preconditions: a nonempty binary tree has been created and RightTree // is an ADT binary tree. // Preconditions: a binary tree has been created. // Postconditions: LeftTree is the left child of the root and Success // is true. // Postconditions: RightTree is the right child of the root and Success // is true. Success) // Detaches the left subtree of a binary tree’s root and retains it in // LeftTree. RightSubtree() // Returns a copy of the right subtree of a binary tree’s root without // detaching the subtree. // Preconditions: a nonempty binary tree has been created and RightTree // is an ADT binary tree. DetachLeftSubtree(LeftTree. // Postconditions: the tree has been traversed in preorder and the // function Visit has been performed on each node. Success indicates whether the operation was successful. // Postconditions: a copy of the right subtree of the root has been // returned. // Postconditions: a copy of the left subtree of the root has been // returned. Else. Success) // Attaches a copy of LeftTree as the left subtree of the root of a // binary tree. // Postconditions: the left child of the root is removed and stored in // LeftTree and Success is true. LeftSubtree() // Returns a copy of the left subtree of a binary tree’s root without // detaching the subtree. Success is false. Visit is a function // which performs operations which are non-destructive to the structure // of the tree. Success indicates whether the operation was successful. DetachRightSubtree(RightTree. // Preconditions: a nonempty binary tree has been created and LeftTree // is an ADT binary tree. Else. Success) // Detaches the right subtree of a binary tree’s root and retains it in // RightTree. Success) // Attaches a copy of RightTree as the right subtree of the root of a // binary tree. Success is false.

2. b 4. 4 T N J E B A W W T N A B E J A B W J E N T a) insert order WTNJEBA b) insert order WTNABEJ c) insert order ABWJNTE . // Preconditions: a binary tree has been created. 1. 7. 5. Postconditions: the tree has been traversed in inorder and the function Visit has been performed on each node. 6. Visit is a function // which performs operations which are non-destructive to the structure // of the tree. 3. // Postconditions: the tree has been traversed in postorder and the // function Visit has been performed on each node. 2 preorder a for Figure 10-42GDAHKLTRVUW M b for Figure 10-6bBDFGEC A c for Figure 10-6c BCDEFG A inorder ADHGKLMRUVTW GFDBEAC ACEGFDB postorder AHDLKGUVRWTM GFDEBCA GFEDCBA 3 The a node (6) must contain the inorder successor of the root because it is the l child. 9. PostorderTraverse(Visit) // Traverses a binary tree in postorder and calls the function Visit once // for each node. Visit is a function which performs operations which are non-destructive to the structure of the tree.101 // // // // // // for each node. Preconditions: a binary tree has been created. 8.

102 5 A C E F L V Maximum height insert order ACEFLVZ Z A E C F V L Z Minimum height insert order FCVAELZ 6 60 20 10 30 75 25 35 45 60 25 10 80 30 35 45 75 40 70 65 40 50 65 70 80 a) insert order 80 65 75 45 35 25 b) delete items 50 and 20 .

Referring to the tree from Exercise 5b. 4 9 10 2 5 3 6 8 7 2 1 3 9a bool IsLeaf().. . Deleting and inserting an item from a node with one or two children will alter the shape of a binary search tree.7 Deleting an item from a leaf of a binary search tree and then inserting it back into the tree does not alter the shape of the tree. return false. 60 25 10 20 30 35 40 45 50 65 70 80 75 reinserting items 50 and 20 8 The following tree is produced after all the statements are executed. { // empty tree is not a leaf if(Root == NULL) return false. // else. } // end IsLeaf c The client could implement a version of IsLeaf by performing the operations LeftSu RightSubtree and testing both subtrees returned to see if they are empty. b bool binTreeClass::IsLeaf() // Returns true if the binary tree is a leaf node.. inserting the items 20 and 50 back into the binary search tree results in the following tree structure. // Returns true if the binary tree is a leaf node. which is obviously different in shape. if(Root->LChildPtr == NULL && Root->RChildPtr == NULL) return true.

104

10a

SearchTreeReplace(ReplacementItem, Success) // Locates, if possible, the item in a binary search tree with the same // search key as ReplacementItem. If found, the item is replaced with // ReplacementItem and Success is true. Else, Success is false. if(search key of RootItem equals search key of ReplacementItem) RootItem = ReplacementItem else if(search key of ReplacementItem < search key of RootItem and Root->LChildPtr != NULL) Root->LChildPtr->SearchTreeReplace(ReplacementItem, Success) else if(Root->RChildPtr != NULL) Root->RChildPtr->SearchTreeReplace(ReplacementItem, Success) else Success = false

b

SearchTreeReplace(Root, ReplacementItem, Success) // Locates, if possible, the item in the binary search tree Root with the // same search key as ReplacementItem. If found, the item is replaced // with ReplacementItem and Success is true. Else, Success is false. if(search key of RootData() equals search key of ReplacementItem) SetRootData(ReplacementItem) else if(search key of ReplacementItem < search key of RootItem and Root.LeftSubtree() != NULL) SearchTreeReplace(Root.LeftSubtree(), ReplacementItem, Success) else if(Root.RightSubtree() != NULL) SearchTreeReplace(Root.RightSubtree(), ReplacementItem, Success) else Success = false

Since no insertions or deletions are performed by this algorithm, only resettin node, the shape of the binary tree will remain the same.

11

The tree should be traversed using a preorder traversal and the contents of the 60 20 10 40 30 50 70

12a

Array-based implementation of tree in Figure 10-18a of the text.
Root contains address 1 and Free contains address 8.

address name LChild RChil d

1 Tom 2 3

2 Jane 4 5

3 Wendy 0 0

4 Bob 6 7

5 Nancy 0 0

6 Alan 0 0

7 Ellen 0 0

8

9

9

0

105

b

Operations on the above implementation.
T.SearchTreeInsert (“Doug”, Success); Root contains address 1 and Free contains address 9.

address name LChild RChil d

1 Tom 2 3

2 Jane 4 5

3 Wendy 0 0

4 Bob 6 7

5 Nancy 0 0

6 Alan 0 0

7 Ellen 0 0

8 Doug 0 0

9

0

T.SearchTreeDelete (“Nancy”, Success); Root contains address 1 and Free contains address 5.

address name LChild RChil d

1 Tom 2 3

2 Jane 4 0

3 Wendy 0 0

4 Bob 6 7

5

9

6 Alan 0 0

7 Ellen 0 0

8 Doug 0 0

9

0

T.SearchTreeDelete (“Bob”, Success); Root contains address 1 and Free contains address 6.

address name LChild RChil d

1 Tom 2 3

2 Jane 4 0

3 Wendy 0 0

4 Alan 0 7

5

6

9

5

7 Ellen 0 0

8 Doug 0 0

9

0

T.SearchTreeInsert (“Sarah”, Success); Root contains address 1 and Free contains address 5.

address name LChild RChil d

1 Tom 2 3

2 Jane 4 6

3 Wendy 0 0

4 Alan 0 7

5

9

6 Sarah 0 0

7 Ellen 0 0

8 Doug 0 0

9

0

12c

Array-based implementation of tree in Figure 10-18b of the text.
Root contains address 2 and Free contains address 8.

address name LChild RChil d

1 Tom 0 3

2 Jane 4 5

3 Wendy 0 0

4 Bob 6 7

5 Nancy 0 1

6 Alan 0 0

7 Ellen 0 0

8

9

9

0

Operations on the above implementation.
T.SearchTreeInsert (“Doug”, Success); Root contains address 2 and Free contains address 9.

address name LChild RChil d

1 Tom 0 3

2 Jane 4 5

3 Wendy 0 0

4 Bob 6 7

5 Nancy 0 1

6 Alan 0 0

7 Ellen 8 0

8 Doug 0 0

9

0

106

T.SearchTreeDelete (“Nancy”, Success); Root contains address 2 and Free contains address 5.

address name LChild RChil d

1 Tom 0 3

2 Jane 4 1

3 Wendy 0 0

4 Bob 6 7

5

9

6 Alan 0 0

7 Ellen 8 0

8 Doug 0 0

9

0

// Array-based implementation if(Tree[Root].RChild != -1) { OldRoot = Root Root = Tree[Root]. Success).b Without a convention for placing duplicates in the left or right subtree. Success).RChild InOrderTraversal(Visit) Root = OldRoot } 13a.LChild != -1) { OldRoot = Root Root = Tree[Root].107 T.SearchTreeDelete (“Bob”.LChild InOrderTraversal(Visit) Root = OldRoot } Visit(Tree[Root]) if(Tree[Root]. Root contains address 2 and Free contains address 6. Root contains address 2 and Free contains address 5.SearchTreeInsert (“Sarah”. address name LChild RChil d 1 Tom 0 3 2 Jane 4 1 3 Wendy 0 0 4 Alan 0 7 5 6 9 5 7 Ellen 8 0 8 Doug 0 0 9 0 T. address name LChild RChil d 1 Tom 6 3 2 Jane 4 1 3 Wendy 0 0 4 Alan 0 7 5 9 6 Sarah 0 0 7 Ellen 8 0 8 Doug 0 0 9 0 d InOrderTraversal(Visit) // Traverses the binary tree inorder and calls the function Visit once on // each node. searching for duplicates .

// Iterative version.Push(Cur. } else Done = true.GetStackTop(Cur. { // Initialize stackClass S. } // end else backtrack } // end while } // end InorderTraverse { .Pop(Success). ptrType Cur = Root. Success = true. Success). // traverse the right subtree // of the node just visited Cur = Cur->RChildPtr. // if the stack is empty.108 NULL >50 >40 >20 Visit 50>60 >50 >40 >20 >60 >40 >20 >60 NULL >20 >60 >60 >70 Visit 60>60 >70 >60 >70 >60 Visit 70 NULL >70 >60 >70 >60 >60 15 void binTreeClass::InorderTraverse(functionType Visit) // Traverses the binary tree inorder and calls the function Visit once on // each node. } else // backtrack from the empty subtree and visit // the node at the top of the stack. however. // traverse the left subtree Cur = Cur->LChildPtr.StackIsEmpty()) { S. Success). while(!Done) { if(Cur != NULL) { // place pointer to node on stack before // traversing node’s left subtree S. bool Done = false. S. Visit(Cur->Item). you are done if(!S.

if (Temp > T. } // end Sum e Recursive computation of the average of all elements of a binary tree.RightSubtree()).BinaryTreeIsEmpty()) return 0. else return Sum(T.) int Count (binTreeClass T) { if (T.RootData().LeftSubtree(). else { Temp = MaxElement(T.BinaryTreeIsEmpty()) return 0.RightSubtree()) + T.T. Recursive computation of the number of nodes in the tree.LeftSubtree()) + Count(T.BinaryTreeIsEmpty()) return 0.RightSubtree()) + 1.BinaryTreeIsEmpty()) return 0. else return Max(Height(T. } // end Height c Recursive computation of the maximum element of a binary tree: int MaxElement (binTreeClass T) { int Temp. if (T.LeftSubtree()) + Sum(T. } // end if } // end MaxElement d Recursive computation of the sum of all elements of a binary tree: int Sum (binTreeClass T) { if (T.LeftSubtree()) + NumNodes(T. (The Count routine is used to count the number of nodes in the tree. } // end Count float Average (binTreeClass T) . int NumNodes(binTreeClass T) { if(T. } // end NumNodes a b Recursive computation of the height of a binary tree: int Height (binTreeClass T) { if (T.109 16 The following solutions use the functions Max and Min which return the maximum and minimum between two integers.MaxElement(T.RightSubtree()))+1. else return Count(T.RootData().MaxElement(T.BinaryTreeIsEmpty()) return -maxint. else return Max(T.RightSubtree())). else return 1 + NumNodes(T.RightSubtree())).LeftSubtree()).RootData()) return Max(Temp.

} else return (Ancestor(T.110 { int N. return(Temp1. return Temp.RightSubtree.RightSubtree(). Temp2 = Find(T.RootData == Item1) { Temp1 = Find(T. else return Temp. FindItem). if (Temp.RightSubtree(). else if (T. else { Temp = Find (T. Item2) || Ancestor (T. if (N == 0) return 0.LeftSubtree(). } // end Average f Recursive item search in a (unordered) binary tree. N = Count (T). Temp2.BinaryTreeIsEmpty() || Temp2.BinaryTreeIsEmpty()) return (Find (T. Item2)). Item2). int Item1. else return Min(FullLevel(T. } else if (T. int Item2) { binTreeClass Temp1.BinaryTreeIsEmpty()) return 0. Item1. int FindItem) { binTreeClass Temp.LeftSuntree. .BinaryTreeIsEmpty()) { Temp = T. Item1.BinaryTreeIsEmpty()) return false. if (T.0. } // end Ancestor h Recursive computation of highest full level in a binary tree: int FullLevel (binTreeClass T) { if (T. if (T.RootData() == FindItem) return T. FindItem)).RightSubtree()))+1. } // end FullLevel 17a The value of the whole tree is 23.LeftSubtree()).LeftSubtree().BinaryTreeIsEmpty()). Item2). FullLevel(T. else return Sum(T)/(float)N. returns a tree rooted at the node where the item is found: binTreeClass Find (binTreeClass T. } // end if } // end Find g Recursive ancestor relationship computation in a binary tree: boolean Ancestor(binTreeClass T.

} // end Minimax 18 There is no binary search tree (BST) that gives a preorder traversal of 2 3 1. Let T(N) denote the number of trees with N nodes.RightSubtree())). The inductive step of Axiom D-2 is If P(0).111 b int Minimax(binTreeClass T) { if(T. P(k) are true for any k ≥0. High.RootData) and (T.RootIsMaxNode()) return Max(Minimax(T. if(T.IsLeaf()) return T.LeftSubtree()). High. A preorder traversal uniquely specifies a BST. keyType Low. High: void RangeQuery(bstClass T.BinaryTreeIsEmpty()) return 0.RootData). else // root is min node return Min(Minimax(T. then we have T(0)= 0 T (N )= ∑ ( ()× T (N − i− 1)) T i i0 = N −1 20 Visit all items in a binary search tree that have a key value in the range Low .RootData(). functionType Visit) { if (!T. .BinaryTreeIsEmpty()) { if (Low < T. then P( k + 1) is true.. P(1). as stated in Appendix D.RightSubtree. if(T.RootData) RangeQuery (T. .. else if (High > T. . you can also write this step as .RootData <= High)) Visit (T.LeftSubtree()). but with a slight change in notation.RootData) RangeQuery (T. keyType High. 19 There is a 1-1 mapping between BSTs and binary trees. Visit). Minimax(T. Low. . else if ((Low <= T. Minimax(T.LeftSubtree(). Low. However. Visit) } } // end RangeQuery 21 The proofs of Theorem 10-1 in Chapter 10 and Theorems 10-2 and 10-3 given here use Axiom D-2.RightSubtree())).

The proof of Theorem 10-2 uses this last form of the inductive step. The proof of Theorem 10-3 is virtually identical to the proof of Theorem 10-2. then P( h) is true. Thus. r TL TR where TL and TR are full binary trees of height h – 1. P(h –1) are true for any h ≥ 0. THEOREM 10-2. P(1). then. . that a full binary tree of height k has 2k-1nodes. P(1). . and it contains 0 = 2 0 –1 nodes. The number of nodes in T is thus 1 (for the root) + (number of nodes in TL) + (number of nodes in TR) By the inductive hypothesis. . That is. . (End of proof. The maximum number of nodes that a binary tree of height h can have is 2h-1. then P(k) is true or as If P(0). . Inductive hypothesis. the full binary tree is empty. If T is a full binary tree of height h > 0. 0 ≤ k < h. Assume that the theorem is true for all k. . A full binary tree of height h (h ≥ 0) has 2 PROOF: The proof is by induction on h. from definition of a full tree. The arithmetic is identical to that used in the proof of Theorem 10-2. then P( h) is true. The previous form is equivalent to If P( k) is true for all k such the 0 ≤ k < h.) THEOREM 10-3. Inductive conclusion. You must show that the theorem is true for h (h > k ≥ 0). By the inductive hypothesis. Basis. That is. the number of nodes in T is 1 + (2h-1–1) + (2h-1– 1) = 1 + 2 * (2h-1–1) = 2h-1 which is what you needed to show.112 If P(0). When h = 0. TL and TR each have 2h-1– 1 nodes because each is a full binary tree of height h –1. . assume for all k. P(k –1) are true for any k ≥0. . 0 ≤ k < h. . its form is: h –1 nodes. you must show that a full binary tree of height h has 2h-1nodes. the left and right subtrees can have at most 2h-1– 1 nodes. You argue that the maximum number of nodes in T equals the maximum number of nodes in T’s left subtree plus the maximum number in its right subtree plus one for the root.

So we add two nodes. the tree (with N nodes) will have N–1+2 = N+1 empty subtrees. Bob. but the net effect is to add one leaf (we change one leaf from a leaf to an interior node by adding two children).2. there is one node (the root). This closed form can be proven by induction. Bob. Wendy. By the inductive hypothesis. Alan. Tom. Wendy b Use a stack for algorithm 1 and a queue for algorithm 2. Inductive conclusion: The maximum number of nodes at level N is 2N . 25a Traversing the tree of Figure 10-17 in the text: i. Therefore. 21 . Inductive hypothesis: For all levels k. Alan ii. the maximum number of nodes at level N is 2 * 2N . Nancy. Ellen. have 2N -1 nodes b The closed form of the formula given in 2h .1. If we add a node to this tree. 23 A proof that a binary tree with N nodes has exactly N+1 empty subtrees: Base: A binary tree with one node has two empty subtrees. Each node on level N–1 can have at most two children.2 = 2N . Ellen.1. . a Part 1 of the formal definition of a complete binary tree can be rewritten: (1) all levels N. We cannot add one node and preserve the strict binary tree property. which is the number of nodes in a full binary tree (a binary tree is full if all levels have the maximum number of nodes). 1 ≤ N < h. but we will add two empty subtrees (the subtrees of the added node).1 = 2 0 = 1 -1 . The data item for the stack or queue should be a pointer to the node being added. Induction: Assume that a strictly binary tree with N–1 leaves has 2(N–1) – 1 = 2N – 3 nodes.1. we have N leaves and 2N –3 + 2 = 2N –1 nodes. Therefore. There is no need to store the entire tree node since the traversal does not change the tree (the stored pointer will indicate the correct tree node when the pointer is retrieved from the stack or queue). Therefore. Tom.113 22 The maximum number of nodes that can exist at level N of a binary tree is 2N Proof by induction on the level number: Base: At level 1. Algorithm 2: Jane. Base: A strictly binary tree with one leaf has one node. Algorithm 1: Jane. the maximum number of nodes on a level is 2k -1 . 24 A proof that a strictly binary tree with N leaves has 2N –1 nodes. the maximum number of nodes on level N–1 is 2N . Nancy. which is what we want to show. we will replace one of the N empty subtrees with a node. Induction: Assume that a binary tree with N–1 nodes has N empty subtrees. 1 ≤ k < N.

28 An iterative BST insert routine: void bstClass::InsertItem(ptrType& TreePtr. etc. while(Cur != NULL) // loop until a leaf is hit { Parent = Cur. NULL).114 26 Perform a preorder traversal on the binary tree. subtree deletion. ADT binary search tree operations could include postorder and preorder traversals. else Cur = Cur->RChildPtr. treeItemType NewItem. ReplacementItem). DelPtr := DelPtr->RChild. delete ReplacementItem. } Cur = new treeNode(NewItem. 27 Consider the case when the node to be deleted has two children: // two-child case of delete ProcessLeftmost(DelPtr.Key) Cur = Cur->LChildPtr. 30 . if (Cur != NULL) Success = true. else { Success = false. Cur = TreePtr. boolean& Success) { ptrType Parent = NULL. ReplacementItem->LChildPtr := DelPtr->LChildPtr. saving the data to a line in the file for each node visited. tree balancing.Key < Cur->Item. NULL. return. a new node is inserted for each line encountered. When the file is read. else Parent->RchilePtr = Cur. This operation is linear in the number of nodes of the tree for both the binary tree and the binary search tree. ReplacementItem = DelPtr. if (NewItem. // else just inserted into an empty tree } // end function InsertItem 29 In addition to the ADT table operations. recapitulating the order of the original tree. } // Parent points to the node that will be the parent of NewItem if (Parent != NULL) Parent->LChildPtr = Cur.

// 2. } // end if two children } // end if NodePtr != NULL } // end DeleteRootItem . NodePtr = NodePtr->RChildPtr. } // else search for the insertion position else if (NewItem. TreePtr. // 4. ptrType& Parent. insert after leaf { // create a new node TreePtr = new treeNode(NewItem. delete DelPtr. treeItemType NewItem. The root has no left child. The root has two children. Success). NodePtr->Parent = DelPtr->Parent. void bstClass::InsertItem(ptrType& TreePtr. ReplacementItem). delete DelPtr. } // end if no left child // test for no right child else if (NodePtr->RChildPtr == NULL) { DelPtr = NodePtr. Parent). NodePtr->Parent = DelPtr->Parent. } // end InsertItem void bstClass::DeleteRootItem(ptrType& NodePtr) // Algorithm note: There are four cases to consider: // 1. { ptrType DelPtr. // 3. // added DelPtr->LChildPtr = NULL. NewItem. NodePtr = NodePtr->LChildPtr. else // search the right subtree InsertItem(TreePtr->RChildPtr.Key < TreePtr->Item. boolean& Success) { if (TreePtr == NULL) // position of insertion found. } // end if no right child // there are two children: // delete and retrieve the inorder successor else { ProcessLeftmost(NodePtr->RChildPtr. NULL. NULL. The root is a leaf. treeItemType ReplacementItem. if (NodePtr != NULL) { // test for a leaf if ( (NodePtr->LChildPtr == NULL) && (NodePtr->RChildPtr == NULL) ) { delete NodePtr. The only operations that need to be modified are included.115 31 BST with parent pointers. NodePtr->Item = ReplacementItem. The root has no right child. NodePtr = NULL. TreePtr. // added DelPtr->RChildPtr = NULL. // was allocation successful? Success = boolean(TreePtr != NULL). } // end if leaf // test for no left child else if (NodePtr->LChildPtr == NULL) { DelPtr = NodePtr. Success).Key) // search the left subtree InsertItem(TreePtr->LChildPtr. // Calls: ProcessLeftmost. NewItem.

{ if (T != NULL) { Visit (T->Item). Visit). void bstClass::Inorder(ptrType TreePtr. b An implementation of a tree using oldest child/next sibling representation: void bstClass::Traverse (ptrType T). if (DeleteIt) Inorder(TreePtr->RChildPtr. If the children are kept in order. insertions and deletions may require shifting pointers in the array. } // end if TreePtr != NULL } // end Inorder 34 . DeleteIt). Visit). Traverse (T->Child). for (int I=0. the implementation described in the chapter is a better representation. If the maximum number of children possible is higher than the average number of children. Visit(TreePtr->Item. direct access to each child is available. as its height is smaller. We assume that DeleteItem has been modified in the two-child case to replace the node to be deleted with its inorder predecessor. } } // end Traverse Advantages: Access to the children is easy.I++) Traverse (T->Children[I]). binary search tree algorithms are easier since one can go directly from the parent to the right subtree. For example. Disadvantages: Limits the number of children to a fixed maximum.ChildNum. the second (the same tree in oldest child/next sibling representation) has a height of 3. I<T. 33 Inorder traversal that allows Visit to delete the current node.116 32a A recursive preorder traversal: void bstClass::Traverse (ptrType T). functionType Visit) { boolean DeleteIt = false. The representations of the two types of trees would be the same for a left linear tree (no right children). Inorder traversals are easier. then memory is wasted. consider the tree A B C B A C The first has a height of 2. if (TreePtr != NULL) { Inorder(TreePtr->LChildPtr. Traverse (T->Sib) } } // end Traverse c If a tree is known to be binary. // pre-order { if (T != NULL) { Visit (T->Item).

117 35 .

++Index) Items[Index] = T.cpp for the ADT table. // Sorted Array-based implementation. { // locate the position where SearchKey exists/belongs int Spot = Position(SearchKey). { // is there room for insertion? Success = boolean(Size < MAX_TABLE). // shift up to make room for the new item for ( int Index = Size-1. // set count field to 0 } // end constructor tableClass::tableClass(const tableClass& T) : Size(T.118 11 Tables and Priority Queues 1 // ********************************************************* // Implementation file Table. the table already contains MAX_TABLE items. Index >= Spot.Key). } // end TableLength void tableClass::TableInsert(tableItemType NewItem. } // end copy constructor tableClass::~tableClass() { } // end destructor boolean tableClass::TableIsEmpty() { return boolean(Size == 0).h" // header file tableClass::tableClass() { Size = 0. // is Searchkey present in the table? . } // end if } // end TableInsert void tableClass::TableDelete(keyType SearchKey. if (Success) { // there is room to insert.Size) { for ( int Index = 0. boolean& Success) // Calls: Position.Items[Index]. Index < T. that // is. // ********************************************************* #include "Table. // make the insertion Items[Spot] = NewItem. --Index) Items[Index+1] = Items[Index]. // Calls: Position. } // end TableIsEmpty int tableClass::TableLength() { return Size.Size. ++Size. boolean& Success) // Note: Insertion is unsuccessful if the table is full. // locate the position where NewItem belongs int Spot = Position(NewItem.

// delete the item // shift down to fill the gap for ( int Index = Spot. if (Success) { // Search Key in Table --Size. } // end if } // end TableDelete void tableClass::TableRetrieve(keyType SearchKey. tableItemType& TableItem. .119 Success = boolean((Spot <= Size) && (Items[Spot]. ++Index) Items[Index] = Items[Index+1].Key == SearchKey)). Index < Size.

It is more likely. b TableReplace preserves the existing structure of a binary-tree-based implementatio node is a leaf node.120 2a For the unsorted array-based implementation. unless the function is re-written to replace all of the fie changing the values of any of the pointer variables in the node.h" // header file template <class T> tableClass::tableClass() { Size = 0. 5 The pointer-based binary search tree implementation would be the best suited for the task. SearchKey).Size) { . // Value found at A[First] else return KeyIndex(First+1. however. the searching technique changes from a binary search to a sequential search. 3 If the word can be evaluated to generate an index in constant time. } // end else } // end KeyIndex The sorted and unsorted versions of TableReplace are the same for pointer-based and array-based tables (the differences are in the KeyIndex routines). Because the order of items is not known. and insertion is done without moving data in an array. // ********************************************************* #include "TableT. keyType SearchKey) { if (First > (Size-1)) return First. that a bi comparing the value of the word with the various search keys generated by the s n time for either the sorted-array based or the binary-search-tree based impleme 4 The binary search tree offers the most efficient implementation for the spell che insertions in O(log n) time. the sorted-a the the table may be the most efficient. A[First] <= Value <= A[Last] if (SearchKey == Items[First]. return its proper // position else { // Invariant: If Value is in A. int tableClass::KeyIndex(int First. // Sorted Array-based implementation. If KeyIndex succeeds. the only function that needs to change is KeyIndex. // set count field to 0 } // end constructor template <class T> tableClass::tableClass(const tableClass<T>& Table) : Size(Table. A binary search is used for retrieval and insert point location. 6 // put the header file here // ********************************************************* // Implementation file TableT. // Value not in original array.Key) return First.cpp for the ADT table. then replace the item by copying over it.

// Calls: Position.Size. } // end copy constructor template <class T> tableClass::~tableClass() { } // end destructor template <class T> boolean tableClass::TableIsEmpty() { return boolean(Size == 0). } // end TableIsEmpty template <class T> int tableClass::TableLength() { return Size.121 for ( int Index = 0. ++Size. . Index < Size. T& TableItem. } // end TableLength template <class T> void tableClass::TableInsert(T NewItem. // delete the item // shift down to fill the gap for ( int Index = Spot. // shift up to make room for the new item for ( int Index = Size-1.Key). boolean& Success) // Calls: Position. ++Index) Items[Index] = Table. that // is.Key == SearchKey)). Index >= Spot. boolean& Success) // Note: Insertion is unsuccessful if the table is full. // is Searchkey present in the table? Success = boolean((Spot <= Size) && (Items[Spot]. // make the insertion Items[Spot] = NewItem.Items[Index]. // locate the position where NewItem belongs int Spot = Position(NewItem. { // locate the position where SearchKey exists/belongs int Spot = Position(SearchKey). Index < Table. } // end if } // end TableInsert template <class T> void tableClass::TableDelete(keyType SearchKey. if (Success) { // there is room to insert. { // is there room for insertion? Success = boolean(Size < MAX_TABLE). ++Index) Items[Index] = Items[Index+1]. } // end if } // end TableDelete template <class T> void tableClass::TableRetrieve(keyType SearchKey. --Index) Items[Index+1] = Items[Index]. the table already contains MAX_TABLE items. if (Success) { // Search Key in Table --Size.

122 boolean& Success) // Calls: Position. // Value found at A[Mid] else if (SearchKey < Items[Mid]. retrieve it } // end TableRetrieve template <class T> void tableClass::TraverseTable(functionType Visit) { for ( int Index = 0. A[First] <= Value <= A[Last] int Mid = (First + Last)/2. Index < Size. } // end else } // end KeyIndex template <class T> int tableClass::Position(keyType SearchKey) // Calls: KeyIndex. return its proper // position else { // Invariant: If Value is in A. int Last. } // end SetItem template <class T> int tableClass::KeyIndex(int First. . SearchKey). Mid-1. int Index) { Items[Index] = NewItem. Last. { // locate the position where SearchKey exists/belongs int Spot = Position(SearchKey). { if (First > Last) return First. } // end SetSize template <class T> void tableClass::SetItem(T NewItem. SearchKey).Key) return KeyIndex(First.Key) return Mid. Size . else return KeyIndex(Mid+1. keyType SearchKey) // Note: Compare this function with BinSearch in Chapter 2.1. // is Searchkey present in table? Success = boolean((Spot <= Size) && (Items[Spot]. { return KeyIndex(0. SearchKey). // item present. // Value not in original array. if (SearchKey == Items[Mid]. ++Index) Visit(Items[Index]).Key == SearchKey)). } // end TraverseTable template <class T> void tableClass::SetSize(int NewSize) { Size = NewSize. if (Success) TableItem = Items[Spot].

the assertion is true for a heap of size n. once the target record is fo the Number field is used as the search key to delete from the other table. the children of the root of H have the largest value in their subtrees by the inductive hypothesis. if (DeleteIt) TableDelete(Items[Index]. Thus. the most sane method for implementing TableRetrieve and TableDelete when duplicates are allowed is to retrieve and delete all items with the given key. must then similar keys and delete only the node whose secondary values duplicate the value TableRetrieve must also traverse these lists of similar keys. and simplest. for ( int Index = 0. the root of H is larger than either of its children. since deletions will occur from both tables on the sa of the deletion depends on the longer operation on the linked list. in finding the target key. suppose that the assertion is true for all heaps with n–1 or fewer items. the record which is stored in the binary search tree will be found more qu the linked list. For each deletion on TableDeleteN. DeleteIt = true. b If duplicate keys are to be inserted. 10 Here.Key. . a dynamic structure would have to be returned that could hold a variable number of items. Index < Size. and each of its children are roots of a heap of size n–1. would involve two tables. Success). Thus a TableInsert. Do the reverse when performing TableDeleteS.123 8a In the absence of secondary keys. one indexed by Name Security Number. H. thus deleted from both tables. } // end for } // end TraverseTable 12 If the heap contains only a single item. again deciding on t secondary information. Similarly. Since the root of H is larger than either of its children. By definition. in addition to its search f check if there is a node with the same key and. TableDelete. then each node will have to possess an ad list of similarly tagged nodes. 9 The most efficient. DeleteIt). and consider a heap. insert the new node into node in the table. if so. then the assertion is obviously true. For an inductive step. containing n items. For TableRetrieve. However. ++Index) { Visit(Items[Index]. which takes 11 void tableClass::TraverseTable(functionType Visit) { boolean Success.

To remove duplicate priorities in first in. therefore the call to RebuildHeap does nothing and the for loop could be shortened. In this manner.g. RebuildHeap and HeapInsert reassign pointers rather than swap items. 18 If the priority value increases. 19 // templates 20 HeapSort calls RebuildHeap (A. During a delete. RebuildHeap does nothing if 2 * Root > Size. the order in which the items were inserted must be maintained as a secondary key. 17 Maintaining an index to the minimum item in the heap can be achieved by comparing the value of a newly inserted item with the current minimum (the current minimum is maxint initially). trickle it down (as in adjust). the item should trickle up if it is smaller than its parent. check to see if the location of the current minimum changes during the adjust process (e. . 2 * i will always be larger than N. When i is between N and N/2. then as the new item is inserted check to see whether the location of the current minimum changes as the result of a swap. If the value is not smaller than the current minimum. with i being passed to Root and N being passed to Size in RebuildHeap. In HeapAdd. If the value is smaller than the current minimum.124 13 The order of item insertion does not affect the shape of the resultant binary tree. If the priority value decreases. 16 Changing to a minheap only affects HeapAdd and RebuildHeap. it does affect the distribution of values in the individual nodes. then update the minimum value and store its position after the trickle up process has terminated. 15 The order in which two items with the same priority value are inserted in a heap does not necessarily affect the order in which they are removed.. the item should trickle down if it is larger than one of its children. Only comparison operators need be changed. i. and in RebuildHeap. first out (FIFO) order. The following diagram illustrates this point. trickle the item up (as in insert). 10 10 9 6 9 5 5 2 3 3 6 2 The tree resulting from the insert order: 10 9 3 5 2 6 The tree resulting from the insert order: 6 3 5 10 9 2 14 Implement the heap as an actual binary tree. when the current minimum is the last element in the heap). N).

const int MAX_SIZE = 10.h> // for rand() #include <iostream. if any if ( Child < N ) { // root is not a leaf. { // if the root is not a leaf and the root's value is less // than the larger of the values in the root's children int Child = 2 * Root + 1. int Root. // index of root's left child. nothing needs to be done. // Postcondition: H is an array representation of a heap // rooted at index Root and containing N items. // Precondition: H is an array representation of a semiheap // rooted at index Root and containing N items. // Method: Recursively trickles the item at index Root // down to its proper position by swapping it with its larger // child. . // index of right child. if any // if root has a right child. so it has a left child at Child int RightChild = Child + 1. int N) // Converts a semiheap into a heap. void RebuildHeap(arrayType H. if the child is larger than the item.h> typedef int dataType. // Child is the index of larger child of root // if the root's value is smaller than the // value in the larger child. find larger child if ( (RightChild < N) && (H[RightChild] > H[Child]) ) Child = RightChild. typedef dataType arrayType[MAX_SIZE]. swap values if (H[Root] < H[Child]) { // swap dataType Temp = H[Root].125 21 The original order After the heap is created After swap 1 and RebuildHeap After swap 2 and RebuildHeap After swap 3 and RebuildHeap After swap 4 and RebuildHeap After swap 5 and RebuildHeap After swap 6 and RebuildHeap After swap 7 and RebuildHeap After swap 8 and RebuildHeap After swap 9 and RebuildHeap The array is sorted 5 1 2 8 6 10 3 9 4 7 10 9 8 6 7 2 3 1 4 5 | 9 7 8 6 5 2 3 1 4 | 10 8 7 4 6 5 2 3 1 | 9 10 7 6 4 1 5 2 3 | 8 9 10 6 5 4 1 3 2 | 7 8 9 10 5 3 4 1 2 | 6 7 8 9 10 4 3 2 1 | 5 6 7 8 9 10 3 1 2 | 4 5 6 7 8 9 10 2 1 | 3 4 5 6 7 8 9 10 1 | 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 22 // Heapsort #include <stdlib. If the item // is at a leaf.

// transform semiheap into a heap RebuildHeap(A. --Index) // invariant: tree rooted at Index is a semiheap RebuildHeap(A. // transform tree into a heap // sort for (int Last = N-1. A[Last+1. the sorted array is built from right to le resulting in the array sorted in descending order. A[Last] = Temp.Last] is a heap.126 H[Root] = H[Child]. .. Index >= 0. N). N)... { // build the initial heap for ( int Index = N/2. Last). // Precondition: A is an array of N elements.N-1] is // sorted and contains the largest elements of A // swap A[0] and A[Last] dataType Temp = A[0]. Last >= 1. --Last) { // invariant: A[0. // Last is now 1 greater than index of last item in semiheap // Semiheap rooted at 0 contains Last items. int N) // Sorts the elements of an array into ascending order. H[Child] = Temp.Last-1] } // end for } // end HeapSort 23 Simply reset the comparison in RebuildHeap so that the root contains the smalles repeated calls to HeapDelete are made. Index. A[0] = A[Last]. // transform the new subtree into a heap RebuildHeap(H. Child. 0. N is unchanged. // assertion: A[0] is the largest element in A[0. do nothing } // end RebuildHeap void HeapSort(arrayType A. } // end if } // end if // else if root is a leaf. // Postcondition: A is sorted into ascending order.

127 12 Advanced Implementations of Tables 1a Binary search tree (reading across. then down): insert 10 10 insert 100 10 100 100 30 insert 30 10 100 100 30 80 50 delete 10 100 30 80 50 80 50 60 100 30 80 50 60 70 insert 90 100 30 50 40 60 70 90 20 30 50 40 60 70 90 20 insert 20 100 40 50 20 60 70 90 delete 30 100 40 50 60 90 40 insert 60 100 100 30 80 50 60 70 delete 70 100 insert 70 100 30 50 40 60 70 30 insert 40 delete 80 30 80 insert 80 10 insert 50 10 b 2-3 tree: insert 10 10 insert 100 10|100 10 insert 50 30|80 10 50 100 80 30|50 100 delete 10 50|80 30 60 insert 30 30 100 10 insert 80 30 80|100 insert 60 50|80 100 insert 70 30 60|70 100 .

128 .

129 insert 40 50|80 30|40 60|70 100 50|70 delete 80 50|70 60 100 insert 90 50 30|40 60 90|100 30 20 insert 20 30|40 70 40 60 90|100 delete 30 50|70 20|40 60 90|100 50|90 delete 70 20|40 60 100 c 2-3-4 tree: insert 10 10 insert 100 10|100 insert 30 10|30|100 10 insert 50 30 10 50|80|100 30 delete 10 50 80|100 delete 80 50|70 30|40 60 100 delete 70 50|90 20|40 60 100 50|70 30 insert 60 50 60|80|100 30 insert 90 50|70 30|40 60 90|100 20|30|40 60 90|100 60|70 100 insert 20 insert 80 30 80|100 insert 70 50|80 insert 40 50|80 30|40 60|70 100 delete 30 50|70 20|40 60 90|100 d A red-black tree : insert 10 10 insert 100 10 100 10 insert 30 30 100 10 80 insert 50 30 10 50 80 100 30 delete 10 50 80 100 30 60 insert 60 50 80 100 30 60 insert 70 50 80 100 70 insert 80 30 100 .

a 2-3 tree is a more efficient ADT table . In a tree with N items.130 insert 40 50 30 40 60 80 100 70 delete 30 50 40 20 60 70 100 90 20 delete 80 50 30 40 60 70 100 insert 90 50 30 40 60 70 100 90 20 insert 20 50 30 40 60 70 100 90 delete 70 50 40 60 90 100 e AVL tree : insert 10 10 insert 100 10 100 10 insert 30 30 100 10 80 insert 50 30 10 50 80 100 30 50 delete 80 60 80 70 100 30 40 50 70 100 30 50 40 70 delete 10 80 100 30 50 60 insert 90 60 90 100 30 50 20 delete 30 60 40 20 50 70 90 100 20 40 50 delete 70 60 90 100 40 70 100 insert 60 80 100 30 50 70 insert 70 60 80 100 insert 80 30 100 insert 40 60 40 30 50 insert 20 60 90 2 The height of a binary search tree depends on the order in which the items are inserted. A 2-3 tree is always balanced and thus has height proportional to log N. Since most table operations take time proportional to the height of the tree. the height can range between N (equivalent to a linked list) and log2 ( N+1) (a fully balanced tree).

functionType Visit) { if (!T.131 3 implementation than a binary search tree. and 32 into the 2-3-4 tree of Figure 12-5b in the text. High: void RangeQuery(bstClass T. BLACK}. 35.RootData <= High)) Visit (T. struct rbTreeNode . High.RootData) RangeQuery (T. typedef rbTreeNode* rbPtrType. 37.RootData) RangeQuery (T. 38. 37|50 30|35 10|20 32|33|34 36 38 39 40 60 70|90 80 100 5 6 Another red-black that represents Figure 12-20 in the text. Low.BinaryTreeIsEmpty()) { if (Low < T. else if ((Low <= T.. 36. keyType High..RootData) and (T. 34. else if (High > T. Visit all items in a binary search tree that have a key value in the range Low . keyType Low.RightSubtree. 50 37 35 30 20 10 33 32 34 36 38 39 40 60 80 100 70 90 7 30|35 10|20 32|33|34 36 37 | 50 39 38 40|45 60|65 70|85|90 80 87|89 100 8 9 enum color {RED. Visit) } } // end RangeQuery 4 Insert 39. Visit). Maintaining a balanced binary search tree may become very expensive in the face of frequent inserts and deletions as the entire tree must be rebuilt in the worst case. Low. High. 33.LeftSubtree().RootData).

Success). return NULL.Item = T. return NULL. if(NewNode == NULL) { Success = false. if (Success) { NewNode.LChildPtr = Convert234toRBtree(T.LColor = RED. rbPtrType NewNode = new rbTreeNode. } // else.LChildPtr = NewChildNode. Success). return NewNode.LColor = NewNode. Success).LMidChildPtr.SmallItem. rbPtrType LChildPtr. } else return NULL. if (NewChildNode == NULL) { Success = false. this node has 4 children rbPtrType NewLChildNode = new rbTreeNode. NewNode. return NULL.SmallItem. }. bool& Success) { if (T == NULL) { Success = true.RMidChildPtr. NewNode.LColor = NewChildNode.RChildPtr = Convert234toRBtree(T. NewChildNode. Success). if (Success) NewChildNode. return NewNode.RColor = BLACK.Item = T.RColor = BLACK. } if (T.Item = T. if (Success) { NewNode.LChildPtr.RMidChildPtr == NULL) // leaf node. Rcolor. NewNode. if (NewLChildNode == NULL) . NewChildNode. } NewNode. NewNode. if (Success) NewNode.RColor = BLACK. rbPtrType Convert234toRBtree(PtrType T. } NewChildNode. return NULL. color Lcolor.LChildPtr.132 { treeItemType Item.LChildPtr = Convert234toRBtree(T. RChildPtr.RChildPtr = Convert234toRBtree(T.RChildPtr == NULL) // 3 children { rbPtrType NewChildNode = new rbTreeNode.MiddleItem. } else return NULL. } if (T.LMidChildPtr. if (!Success) return NULL. } NewNode.RChildPtr = Convert234toRBtree(T. if (NewNode == NULL) { Success = false. Success). 1 child or 2 children { rbPtrType NewNode = new rbTreeNode.

Success). Success). Success) I = HashIndex(SearchKey) .RChildPtr = Convert234toRBtree(T. NewRChildNode. if (!Success) return NULL. NewRChildNode. rbPtrType NewNode = new rbTreeNode.LColor = NewRChildNode. } // end Convert234toRBtree 10 The following pseudocode implements operations on a table which uses hashing an resolve collisions: TableInsert(NewItem.LChildPtr = Convert234toRBtree(T.RColor = BLACK. if (Success) NewLChildNode. if (!Success) return NULL.LChildPtr = NewLChildNode. } NewLChildNode.RChildPtr = NewRChildNode. rbPtrType NewRChildNode = new rbTreeNode. return NewNode. return NULL. if (NewNode == NULL) { Success = false. if (Success) NewRChildNode. return NULL. Success). } NewRChildNode. } NewNode. Success) SearchKey = the search key of NewItem I = HashIndex(SearchKey) if (T[I] is not empty) { do I = (I + 1)mod TableSize while(T[I] is not empty and I != HashIndex(SearchKey)) } if (T[I] is empty) { T[I] = NewItem Success = true } else Success = false TableDelete(SearchKey.MiddleItem.LChildPtr.Lcolor = NewNode.RChildPtr. return NULL.RColor = BLACK.RColor = RED.LMidChildPtr.Item = T.LColor = NewLChildNode. NewNode. if (NewRChildNode == NULL) { Success = false.Item = T.SmallItem. NewNode.LargeItem. NewNode.RChildPtr = Convert234toRBtree(T.RMidChildPtr.LChildPtr = Convert234toRBtree(T. NewLChildNode.133 { Success = false. NewLChildNode. Success).Item = T.

Success) I = HashIndex(SearchKey) if (T[I].Key == SearchKey) { delete T[I] Success = true } else Success = false TableRetrieve(SearchKey.Key != SearchKey and I != HashIndex(SearchKey)) } if (T[I].Item Success = true } else Success = false 11 Once the bucket is found using the hash function.134 if (T[I]. call the .Key != SearchKey) { do I = (I + 1)mod TableSize while (T[I].Key != SearchKey and I != HashIndex(SearchKey)) } if (T[I]. TableItem.Key == SearchKey) { TableItem = T[I].Key != SearchKey) { do I = (I + 1)mod TableSize while (T[I].

The size of the adjacency matrix is then (9 * 9)m = 81m bytes. Thus the total is 9m + (10 * 2)m = 29m < 81m. . c d. 0 0 9 1 9 0 8 6 0 1 2 3 4 5 ∞ ∞ 2 8 0 5 2 ∞ ∞ ∞ 1 ∞ ∞ ∞ ∞ 5 0 3 ∞ ∞ ∞ ∞ 0 7 4 1 6 ∞ ∞ ∞ 7 0 2 5 b The adjacency matrix for the directed graph in Figure 13-34 (where a 1 indicates the existence of an edge and a 0 indicates the absence of an edge): a 0 0 0 0 0 0 0 0 0 b 1 0 0 0 0 0 0 0 0 c 1 0 0 0 0 0 1 0 1 d 0 1 1 0 0 0 0 0 0 e 0 0 1 0 0 0 0 0 0 f 0 0 0 0 0 0 0 0 0 g 0 0 0 0 1 1 0 1 0 h 0 1 0 1 0 0 0 0 0 i 0 0 0 0 0 1 0 0 0 a b c d e f g h i The adjacency list: node a b c d e f g h i edges to: b.135 13 Graphs 1a The adjacency matrix for the weighted graph in Figure 13-33. The adjacency list requires an array of 9 addresses of size m plus ten nodes each composed of an integer for storing the vertex name and another integer for the address of the next node (if any). e h g g. h d. i c g c 2 Let each integer require m bytes and assume that addresses are stored as integers.

2. i. 4 Within the while loop. 3. c. 1. c. h. 4. breadth-first search: 0. where the possible vertices at each step are visited in sorted order (increasing): Graph in Figure 13-33. d. 1. Graph in Figure 13-31. 3. 4. 5. insert the following just before the first if statement: if (there is a visited vertex adjacent to the vertex on top of the stack) report a cycle 5 a Depth-first search topological sort of the graphs in Figure 13-36: Action push a push b push c pop c pop b push d push e pop e pop d pop a Stack(bottom -> top) Ordered List a empty a b empty a b c empty a b c a b c a d b c a d e b c a d e b c a d e b c empty a d e b c b Action push a push b push c pop c pop b push d pop d pop a Stack (bottom>top) a ab abc ab a ad a empty Ordered List empty empty empty c bc bc dbc adbc . d. depth-first search: a. g. f. e.136 3 Depth-first and breadth-first searches. 5. b. b. f. g. depth-first search: 0. e. h. breadth-first search: a. i. 2.

L) // Arranges the vertices of G into a // topological order and places them in list L.ListInsert(Step.137 c Action push a push b push c pop c pop b push d pop d push e pop e pop a Stack (bottom -> top) a ab abc ab a ad a ae a empty Ordered List empty empty empty c bc bc dbc dbc edbc aedbc 6 TopSort1(G. N = number of vertices in G for(Step = 1 through N) { Select a vertex v that has no predecessors L. v. Success) Delete from G vertex v and its edges } // end for // end TopSort1 a a b e c Remove a from Add it to L d b e c a Remove b from Add it to L d c a b Remove d from Add it to L d e c a b d Remove e from Add it to L e c a b d e Remove c from Add it to L a b d e c Done .

138 b a b d Remove a from Add it to L c b a d Remove b from Add it to L c d a b Remove c from Add it to L a b c c d Remove d from Add it to L a b c d Done c a b e c Remove a from Add it to L d b a e c Remove b from Add it to L d e c a b Remove d from Add it to L d .

Success) Mark v as visited // loop invariant: there is a path from vertex v at the // bottom of the stack S to the vertex at the top of S while (!S.StackIsEmpty()) { if (no unvisited vertices are adjacent to the vertex at the top of the stack) S.Push(v.139 a b d e c Remove e from Add it to L a b d e c Remove c from Add it to L a b d e c Done 7 The trace of the DFS algorithm is in Figure 13-12. and so on. b c A DFS spanning tree A BFS spanning tree minimum spanning tree A rooted at vertex a. undirected graph // beginning at vertex v by using depth-first search: // Iterative version.Pop(Success) // backtrack else { Select an unvisited vertex u adjacent to the vertex at the top of the stack Mark the edge from u to the vertex at the .CreateStack() // push v onto stack and mark it S. For example. a generates edges to to c and e. Here the most recently dequ edge with the next cluster of nodes visited. 8 Refer to Figure 13-35 in the text. 9 DFSTree(v) // Forms a spanning tree for a connected. the tree in Figure 13-20 result The trace of the BFS algorithm is in Figure 13-13. S. Thus the tree in Figure 13-21 is the result. Because each new node visit this node and the node at the top of the stack.

labeled 0. S.QueueAdd(0. // mark all vertices as being unvisited for (V=0. W<N.GetQueueFront(V. visited[MaxVertices]. We assume the standard graph operations are available: BFS spanning tree.140 the top of the stack. int V. Q. while(!Q.Push(u. int N) // G2 will contain a spanning tree for the graph G1 upon return. W++) . // start search at vertex 0 Q. V++) visited[V] = false. queueClass Q. W. Assumes that the standard integer queue operations are available.QueueIsEmpty()) { // get next vertex to explore Q. visited[0] = true. V<N. Success). N is the // number of vertices in graph G1. Step 1 2 3 4 5 6 7 v 1 2 4 3 6 5 S [0] [01] [012] [0124] [01243] [012436] [0124365] W[0] W[1] W[2] W[3] W[4] W[5] W[6] 0 2 4 6 0 2 4 6 5 0 2 4 6 5 0 2 4 6 5 10 6 0 2 4 6 5 9 6 1 2 4 6 5 8 6 1 2 4 6 5 8 6 ∞ ∞ ∞ ∞ ∞ ∞ ∞ 12 The following functions produce a spanning tree for a given (undirected) connected graph using BFS and DFS.QueueRemove(Success). for (W=0. Success).. graphClass BFS (graphClass& G1.N-1 { graphClass G2. boolean Success. Success) Mark u as visited } // end if } // end while 10 Refer to Figure 13-22 in the text: a i e d g f a) minimal spanning tree rooted at g f b) minimal spanning tree rooted a g b a c h i e d b c h 11 A trace of the shortest-path algorithm for the graph given in Figure 13-37 of the text.

StackIsEmpty()) { // get next vertex to explore S. Success). N is the // number of vertices in graph G1.InsertEdge (V.Push(0.QueueAdd(W.Pop(Success). Q.. S. V<N. stackClass S.IsEdge(V. Success). while(!S. W. visited[W] = true. } // end function DFS 13 One such circuit is: a b f c d g e a We know the graph has an Euler circuit s and d is 2 and the degree of the remaining nodes is 4. visited[0] = true. // start search at vertex 0 S. boolean Success. W<N. } } return G2.Push(W. Success) && !visited[W]) { // add a new branch to the spanning tree G2. boolean visited[MaxVertices]. W. visited[W] = true. // mark all vertices as being unvisited for (V=0. int V.IsEdge(V. Success). W. Success) && !visited[W]) { // add a new branch to the spanning tree G2. labeled 0. for (W=0. Success). Assumes the standard integer stack operations.GetStackTop(V. W++) if (G1.InsertEdge (V. Since all nodes have ev many in-edges as out-edges and so a circuit is possible. Success). . W. } // end function BFS DFS spanning tree. W. V++) visited[V] = false. S. } } return G2. graphClass DFS (graphClass& G1. int N) // G2 will contain a spanning tree for the graph G1 upon return. Success).141 if (G1.N-1 { graphClass G2.

the first part of the invariant). W[v] is the smallest weight of all paths from 1 to v (including paths outside S) and the shortest path from 1 to v lies entirely in S.QueueIsEmpty()) { Q. and adding edge (i . (i. Hence. Success) BFS(w) } // end while 17 Proof of the loop invariant of Dijkstra’s shortest-path algorithm: The loop invariant: For v not in S. To get from u to w. Since the graph is undirected there is also a path from u to v. and the weights for these paths are in the array W. a traversal does visit every other vertex in the graph. not containing loops or multiple edges. 16b BFS(v) // Traverses a graph beginning at vertex v by using a // breadth-first search: Recursive version. DFS uses a stack and so can naturally be implemented recursively. If G has N vertices and N–1 edges.QueueDelete(w. regardless of where the traversal starts. when starting at vertex v. Q. there are k vertices in S. Suppose that the traversal starts at vertex v and does not visit vertex w. to G. and so a path from u to w proceeds from u to v and then from v to w. . Let v' be the last vertex on the path P that is in S. undirected graph. Now add any additional edge. j ). Success) // loop invariant: there is a path from vertex v // to every vertex in the queue Q while(!Q. It must be a queue to ensure that vertices close to the current vertex are visited before those further away. 15 A traversal visits every vertex in the graph if and only if the (undirected) graph is connected. suppose that. For v in S. By the inductive hypothesis (specifically. Since G is a tree. G. note that a) there is a path from v to u.142 14 Consider any connected. Find the smallest W[ v] such that v is not in S. Then there is no path from v to w and so the graph is not connected. The initialization step sets S to 1 and W to the first row of the adjacency matrix.CreateQueue() Mark v as visited for(each unvisited vertex u adjacent to v) Q. Proof by induction on Step: Basis: Step = 1. Inductive hypothesis: Assume the invariant is true during Steps 1 through k<N. and let P designate the path from 1 to v. The paths that the invariant describes when S contains only 1 are simply single edges from 1 to any vertex. the graph is connected. then G must form a tree. W[ v] is the smallest weight of all paths from 1 to v that pass through only vertices in S before reaching v. Then we assert that there is a path between every pair of vertices u and w. 16a The recursive BFS algorithm is not simple because the underlying data structure is a queue. and b) there is a path from v to w. W[v] is the smallest weight of all paths from 1 to v that pass through only vertices in S before reaching v. On the other hand. Inductive conclusion: At the beginning of Step k + 1. there is a path from i to j in G. j ) must then form a cycle. The invariant is true.QueueInsert(u.

143

By the inductive hypothesis (the second part of the invariant), W[v' ] is the smallest weight of all paths from 1 to v', including paths outside S, and the shortest path from 1 to v' — call it P' — lies entirely in S. Path P is thus obtained by adding to P' the edge from v' to v. Thus, W[ v] is obtained by adding to W[v'] the weight of the edge from v' to v. Therefore, when the algorithm adds v to S and adjusts W such that W[ u] ≤ W[v] + A[v, u] for each vertex u not in S, W[ u] must be the smallest of all paths from 1 to u that pass through only vertices in S before reaching u. We must now show that W[v] is the smallest weight of all paths from 1 to v (including paths outside S). If this statement were untrue, there would be a path from 1 to v whose weight was less than W[v]. If this shorter path passed only through vertices in S, then its existence would contradict the choice of v: v was chosen earlier so that W[v] would be a minimum of all paths that passed through vertices in S before reaching v. On the other hand, if the shorter path leaves S before reaching v, it must pass through a vertex v' outside of S. Clearly, the path to v' is shorter than the path to v, so W[v' ]<W[ v]. This result also contradicts the earlier choice of v. Therefore, the statement that began this paragraph must be true. Finally, we must show that the shortest path from 1 to v lies entirely in S. If the path left S before reaching v, it would pass through a vertex v' outside of S. As you just saw, this eventually leads to a contradiction. (End of proof.)

144

14 External Methods
1
void ShiftData (F, I, B) // Make a gap at record I in block B of sorted file F { if (B == LastBlock-1) { // special case ReadBlock (F, B, B); for (J=LastRecord-1;J>I; J--) // assuming LastRecord + 1 < RecordsPerBlock B[J] = B[J-1] } else { // do block B first ReadBlock (F, B, B); // save the last record Temp1 = B [RecordsPerBlock]; // make a gap at location I for (J=RecordsPerBlock-1; J>I; J--) B[J] = B[J-1]; WriteBlock (F, B, B); // do all blocks except LastBlock for (K=B + 1; K<LastBlock; K++) { ReadBlock (F, K, B); // save the last record Temp2 = B [RecordsPerBlock-1]; // shift all records down one, leaving a gap at location 1} for (J=RecordsPerBlock-1; J>0; J--) B[J] = B[J-1]; // insert record saved from last location in previous block} B[0] = Temp1; // get ready for next block } Temp1 := Temp2; WriteBlock (F, K, B) } // do LastBlock ReadBlock (F, LastBlock, B); for (J=RecordsPerBlock-1; J>0; J--) B[J] = B[J-1]; B[0] = Temp1; WriteBlock (F, LastBlock, B) } } // end ShiftData

2

We can use a doubly linked list of blocks. When a record is deleted from a block that was previously full or when a new block is allocated, add that block to the front of the free block list. When a slot is needed, use the block at the beginning of the list; if it becomes full then remove it from the list. When a record is deleted from a block and that block becomes completely empty, then return the block to the system and delete it from the free block list. By allocating some space in the block for “pointers” (block numbers), the blocks themselves can be the nodes on the free block list.
void GetSlot (F, BlockNum, RecNum)

145

{

if (Freelist != 0) { // Free list is not empty; there is room in existing blocks BlockNum = FreeList; // Read first block on free list into internal array B ReadBlock (F, BlockNum, B); RecNum = EmptySlot (B); // Find an empty slot MarkUsed (B, RecNum); // Has the block become full? if (NumFreeSlots (B) == 0) Freelist := NextBlock (B) WriteBlock (F, BlockNum, B) } else { // Free list is empty; get a block from the system AllocateBlock (F, BlockNum); ReadBlock (F, BlockNum, B); RecNum = 1; MarkUsed (B, RecNum); // Start a free list NextBlockUpdate (B, NULL); PrevBlockUpdate(B, NULL); Freelist = BlockNum; WriteBlock (F, BlockNum, B) } } // end GetSlot void FreeSlot (F, BlockNum, RecNum) { ReadBlock (F, BlockNum, B); if (BlockFull (B)) { // add to Freelist MarkUnused (B, RecNum); NextBlockUpdate (B, Freelist); PrevBlockUpdate (B, NULL); WriteBlock (F, BlockNum, B); ReadBlock (F, Freelist, B); PrevBlockUpdate (B, BlockNum); WriteBlock (F, Freelist, B); Freelist = BlockNum; } else { MarkUnused(B, RecNum); if (BlockEmpty(B)) { // detach from FreeList and return block to system } } } // end FreeSlot

3

Insert (TIndex, TData, NewItem, Success) // Insert into TData the NewItem and update the index. Find an empty slot // in TData; if no empty slot, get a new block. At this point, we would // use GetSlot to find an empty record: see Exercises 2 and 8. { P = NumberOfBlockWithEmptySlot; ReadBlock (TData,P,D); D[EmptySlotNumber] = NewItem; WriteBlock (TData,P,D); // apply hash function to NewItem's key I = hash (NewItem.Key); // get first block on chain of index blocks Q = T[I]; if (Q == 0) { // no items hashed there so far AllocateBlock (TIndex,Q);

X. Q. Q. find_data_record_D[K]_with_search_key_X.P>.P>.Q. B[1] = <NewItem.Key. See Exercises 2 and 8. // attach Q to beginning of chain B[ptr] = T[I]. } // end else Q != 0 } // end else Q == 0 } // end Insert Delete (TIndex. // attach new block to hash table WriteBlock (TIndex. WriteBlock (TIndex.146 ReadBlock (TIndex. while ((Q!=0) && (there_is_no_EmptySlot_in_Q)) { Q = block_number_of_NextBlock_on_chain. B) } else { ReadBlock (TIndex.B). B).B). WriteBlock(TIndex.Q). Q. Success = true. BlockNum. Q. B) } if (Q != 0) { // block with empty slot B[J] = <NewItem. ReadBlock(TIndex. } else { // allocate a new block AllocateBlock (TIndex.D). B). // P is TData block that NewItem is in T[I] = Q. // Find the item as described in the Retrieve pseudocode in the text. // At this point. D). mark_B[J]_deleted_and_do_whatever_memory_mgmt_is_appropriate.Key. Success) // Delete item with key X from TData file. // read in the appropriate block from the data file ReadBlock(TData. WriteBlock (TIndex. B). P>. TData. B[1] = <NewItem.Key. mark_D[K]_deleted_and_do_appropriate_memory_mgmt. // the block must contain an indicator of the next block T[I] = Q.B). Q. } else Success = false } // end Delete 4 B-tree of order 5 (starting with the empty tree): TableInsert (10) 10 .BlockNum.Q. { if (P != 0) { // the item is present BlockNum := block_number_of_data_file_pointed_to_by_B[J]. we would call FreeSlot.P. if (P != 0) ReadBlock (TIndex. and update TIndex file. WriteBlock(TData.

147 TableInsert (100) TableInsert (30) TableInsert (80) TableInsert (50) 10 | 100 10 | 30 | 100 10 | 30 | 80 | 100 50 10 | 30 TableDelete (10) TableInsert (60) 30 | 50 | 80 | 100 60 80 | 100 30 | 50 TableInsert (70) 60 80 | 100 30 | 50 TableInsert (40) 60 70 | 80 | 100 30 | 40 | 50 TableDelete (80) 60 70 | 80 | 100 30 | 40 | 50 TableInsert (90) 60 70 | 100 30 | 40 | 50 TableInsert (20) 60 70 | 90 | 100 20 | 30 | 40 | 50 TableDelete (30) 60 70 | 80 | 90 | 100 20 | 40 | 50 TableDelete (70) 60 70 | 80 | 90 | 100 20 | 40 | 50 80 | 90 | 100 .

{ K = the_key_position_of_X. SuccBlock). SuccBlockNum = the_block_number_of_the_root_of_the_0th_subtree. SuccBlock contains the block containing Succ. L.. // 1. in order to swap X // with its inorder successor. } else // there is no right subtree { SuccBlockNum = 0. SuccBlockNum = the_block_number_of_the_root_of_the_0th_subtree. Succ. { X = search_key_of_NewItem. // Insert NewItem into table stored in TData and update index stored in // TIndex. However. while (SuccBlockNum != 0) { ReadBlock (TIndex. // update index locate_the_leaf_block_L_in_TIndex. } // When this loop exits. it is useful to have both blocks in // memory. Succ = the_first_key_in_SuccBlock. The leftmost key value // will be the inorder successor of X. which contains m items. SuccBlock). B. } // end Insert Split (N) // Split node in block N. SuccBlockNum) // Return in Succ the key value of the inorder successor of X. D[J] = NewItem. // use memory management routines // At this point. if (L_now_has_m_items) Split(L). The deletion algorithm would similarly parallel the 2-3 tree deletion algorithm presented in the text. find_a_free_slot_in_TData. Insert (TIndex. conceptually_add_NewItem_to_L. // and SuccBlockNum contains the block number of SuccBlock. // J is the free slot in block B WriteBlock (TData. // see Retrieve in text ReadBlock (TData. if (X_is_not_the_m-1th_key) Succ = Key_immediately_to_right_of_X. D). B. call GetSlot (see Exercises 2 and 8) // B is the block number of the block in TData with a free slot ReadBlock (TData. B). we can // pass in rootnum instead of B instead of C. SuccBlock. TData. SuccBlockNum. D).148 5 InorderSuccessor (TIndex. B. If we do not // want to assume the block containing X has already been read in. // else there is no inorder successor } // end InorderSuccessor 6 Pseudocode implementation of Insert for a table implemented with an index file organized as a B-tree of order m. If N is an . X. if (SuccBlockNum != 0) // if there is a subtree { ReadBlock(TIndex. An actual implementation would be complicated by memory considerations such as how many blocks can be in memory at one time. SuccBlockNum.m-1 for an order m tree SuccBlockNum = block_number_containing_the_root_of_the_subtree _immediately_following_K. NewItem). we're at the leftmost leaf of the // subtree immediately to the right of X. B contains // the block containing X.

if (Key1 >= Low) && (Key1 <= High) Visit (Key1). The locations for the memory management calls are indicated. . // Rootnum is the block number in TIndex of the root of the B-tree. depending on how many blocks // can be in memory at one time. High. // if N is the root. Low. Visit).last subtree in root.High.149 // internal node. N2). } if (High >> last_Key_in_root) RangeQuery(TIndex. subtree I. Visit). subtree 0. } } // end RangeQuery 8 See Exercises 3 and 6. { ReadBlock (TIndex. if (P_now_has_m_items) Split(P). RootNum.Low. Low.I<number_of_keys_in_root-1. I++) { if (High >= Key[I]) || (Low <= Key[I+1]) // if any values in the I-th subtree are in the range RangeQuery (TIndex.Visit). } else // internal node { if (Low < Key1_of_the_root) RangeQuery (TIndex. B). // Visit (external function) the key values between Low and High // contained in TIndex. { P := parent_of_N. } // end Split 7 RangeQuery (TIndex. then get a new block for P replace_N_by_two_blocks (N1. RootNum. High. // give N1 the m div 2 items in N with smallest search key values // give N2 the m div 2 items in N with largest search key values if N (contains_an_internal_node) { // N1 becomes the parent of N's (m+1) div 2 leftmost children // N2 becomes the parent of N's (m+1) div 2 rightmost children } send_to_P(key_in_N_with_the_middle_search_key_value). High. for (I=1. then it has m+1 children. Low. Visit). if (RootNum_is_a_leaf) { for (each_Key_value_in_leaf) if ((Key >=Low) and (Key <= High)) Visit (Key). if (Key[I+1] >= Low) && (Key[I+1] <= High) Visit (Key[I+1]). which is organized as a B-tree of order m. // Note: we may have to be careful here.

ReadBlock (TData.I++) // may want special case for last block which may not be full { ReadBlock(TData. } } // end Traverse RangeQuery (TIndex. { // read the root into internal global array B if (BlockNum != 0) { ReadBlock (TIndex. High.TData. Traverse (BlockNum). B). Visit (data record). RootNum. J++) Visit(D[J]). TData. it would be part of the // recursive stack. Low.D). PDta. // Traverse the index file of a B-tree in sorted order. for (J=0.RootNum. one block access per subtree rooted at that node. BlockNum is the // block number of the root of the B-tree in the index file. one does not need to use the index file to traverse the records in sorted order except possibly to get the block number of the first block in the data file. // traverse the data file PData = pointer_in_the_first_index_record_of_B. J<RecordsPerBlock. Traverse (TIndex. extract_from_D_the_data_record_with_appropriate_search_Key. print(Key_Ki_of_B). RootNum) // find the smallest Key in the index { ReadBlock (TIndex. D). I<m. we need to get the block number of the first block containing keys in the range. // where Visit is defined elsewhere b If the data file records are sorted. I++) { if (P != 0) ReadBlock(TIndex. Traverse(P). } } } // end Traverse 10a Replace each Visit(Key) in Exercise 7 with PData = pointer_in_Key_index_record_of_B.B).I<=LastBlock. BlockNum. RootNum) { . while (P != 0) ReadBlock (TIndex. and in this implementation. // traverse the children P = block_number_of_the_0th_child_of_B. for (I=PData.I. Traverse (P). BlockNum.150 9 This algorithm performs one block access per leaf plus for each internal node. B). // we must read in the block again only if // there is a subtree at the I-1st position for (I=1. For RangeQuery. P = block_number_of_the_ith_child_of_B. we're assuming that there // is not enough memory to accommodate a recursive stack containing h // blocks. P = block_number_of_the_root_of_the_0th_subtree. B). B is a // global array—if it were local to Traverse.

J++.Key <= High)) { Visit D[J]. the function may need to search a chain of blocks to find the correct item. find the inorder predecessor’s data block number. } PData++. Visit each record in the chain (one or more block reads. allocating a new block if necessary. and traverse the chain starting with that value. Deleting from the data file now involves moving records to fill the empty slot. use the B-tree to find the location of first record. RootNum.D). while(!Done) { ReadBlock(TData.151 ReadBlock (TIndex. with the following differences: We are shifting only until the end of the chain rather than the end of file. find_first_Key_value_higher_than_Low. except that we start at Low and continue until we reached High. J = location_of_the_first_record_in_D_with_Key_not_smaller_than_Low. etc. For a traverse. B). and note the key value of the last record. Using the B-tree. TraverseTable and RangeQuery can take advantage of the sorted chains of blocks to do fewer block accesses than would be necessary if the data file were kept unsorted. it is no longer possible to use any free slot to insert NewItem. TData. NewItem) where X is the search key of NewItem. then of size 4. and we cannot assume that the last block in the chain will have room (it is possible that we must call AllocateBlock). and insert NewItem into that block (or successor blocks) in the data file. 11 Sort size elements in array A using an iterative merging technique. Repeat the process until all records have been visited. . PData = pointer_in_the_appropriate_index_record of_B. find the inorder successor of that key value. This could involve moving records across block boundaries (similar to the shifting data necessary in TableInsert). Since TData is now kept sorted. } } // end RangeQuery c TableInsert (TIndex. depending on the length of the chain). Inserting into the data file can be done using the shift data algorithm developed in Exercise 1. The same process can be used for RangeQuery. Done = FALSE. It is probably easier to insert NewItem’s Key into the index. and could result in returning a block to the system if the last block in the chain becomes empty.PData. while ((J <= RecordsPerBlock) && (D[J]. We start by merging runs of size one (already sorted) into sorted runs of size 2. locate_the_leaf_where_Key_value_Low_would_be. Done = ((J != RecordsPerBlock) && (PData > LastBlock)). TableRetrieve need not be changed much.

S2++. R. S3++. R. // pointer to beginning of second run R = P. P = P + (RunSize * 2). int I. Q.152 void Merge(int S1. (P+RunSize-1). S1++. The first starts at s1 and ends at F1. S1++. false indicates B is source. S3++. } } // end Merge void Mergesort (arrayType& A. Q+RunSize-1. I++) A[I] = B[I]. Assumptions: each run is sorted { if (F2 > Size) F2 = Size. B) else Merge (P. } // end while RunSize < Size if (!Which) // sorted array is in B for (I=0. R = P. S2++. RunSize = 1. int Size) { int RunSize. arrayType B. Which = !Which. // True indicates A is source. } else { Dest[S3] = Source[S2]. S3++. Q. // Flag to distinguish the source array from the destination array. A. while (RunSize < Size) { P = 1. int S3. B. (P+RunSize-1). // pointer to location in second array to place merged run while (Q <= Size) { if (Which) Merge (P. Q+RunSize-1. int S2. Q = P + RunSize. } while (S2 <= F2) { Dest[S3] = Source[S2]. int F2. P. } RunSize := RunSize * 2. Q. while ((S1 <= F1) && (S2 <= F2)) { if (Source[S1] <= Source[S2]) { Dest[S3] = Source[S1]. // pointer to beginning of first run Q = P + RunSize. } // end MergeSort . arrayType& Source. A). // Mergesort needs extra storage boolean Which. int F1. arrayType& Dest) // Merge: Merge two runs in array Source and place in Dest (starting at // s3). I<Size. R. S3++. the second starts at F2 // and ends at s2. Which = TRUE. } } while (S1 <= F1) { Dest[S3] = Source[S1].

0. while((LetterGrade != ‘Q’) || (LetterGrade != ‘q’)) { QPV = QualityPointValue(LetterGrade). return TriangleType. QPV. int Side3) { char TriangleType. } 4. } else cout << “You did not enter any grades. break. cout << “Enter a letter grade: ”. cin >> LetterGrade. break. break. int Side2.h> #include <iomanip. cout << setprecision(2) << (QPVSum/(float)GradeCount) << endl. else TriangleType = ‘S’. if (QPV<0) cout << “Invalid letter grade. ++GradeCount. } 2 int QualityPointValue(char LetterGrade) { int QPV. switch(LetterGrade) { case ‘A’: case ‘a’: QPV = case ‘B’: case ‘b’: QPV = case ‘C’: case ‘c’: QPV = case ‘D’: case ‘d’: QPV = case ‘F’: case ‘f’: QPV = case ‘I’: case ‘i’: QPV = default: QPV = -1. 2. break. 3 #include <iostream. else { QPVSum += (float)QPV. if ((Side1 == Side2) && (Side2 == Side3)) TriangleType = ‘E’. cout << “Your quality point average is: ”. 0.0. 3. break. break. else if((Side1 == Side2) || (Side1 == Side3) || (Side2 == Side3)) TriangleType = ‘I’.153 Appendix A Review of C++ Fundamentals 1 char WhatTypeOfTriangle(int Side1.” << endl.setf(ios::showpoint). . } if (GradeCount > 0) { cout. char LetterGrade.” << endl. double QPVSum = 0. break. 1. } return QPV. cin >> LetterGrade.h> int GradeCount = 0. } cout << “Enter a letter grade: ”.

Name[0]. NewName. } 7 char* CreateName(char *FirstName. for (int i = 0. it is assigned the character ‘E’. char *Result = new char[Length]. i++) Total += Week[i]. i < DAYS_PER_WEEK. char *LastName) { // length of new string must allow for the space between // the names as well as the null terminator. CSC212[3]. in this example 4 * 52 + 11 = 219.GPA. CSC212[3]. LastName). return (Total/(double)DAYS_PER_WEEK). strcat(Result. b.154 4 After the character ‘Y’ is read into Response. return Result.Name. The next time the cin stream variable is assigned a value. 8b for (int I = 0. 5a To map the two-dimensional array into a one-dimensional array as described in Part a of this exercise. int Length = strlen(FirstName) + strlen(LastName + 2.Address. in this example 11 * 7 + 5 = 81. “ “). use the following formula: ArrayIndex = ColumnIndex * TheNumberOfRows + RowIndex Therefore. strcat(Result.Address. 6 double Average(weekType Week) { double Total = 0. The ‘E’ in this case will cause the loop to terminate. FirstName).State. CSC212[3].Address.Zip[0]. } // end CreateName 8a Repeating Self-Test Exercise 9: a. I < MAX. To map the two-dimensional array into a one-dimensional array as described in Part b of this exercise. strncpy(CSC212[I]. NAME_LENGTH). d.0. the characters ‘E’ and ‘S’ are still in the input stream. CSC212[3]. I++) { cin >> NewName. strcpy(Result. use the following formula: ArrayIndex = RowIndex * TheNumberOfColumns + ColumnIndex 5b Therefore. c. } // end for .

155 .

struct person { nameType Name. double NextSalary. person& DesiredPerson.156 9 struct nameType { stringType First. boolean& Found) { ifstream InFile(FileName). }.h> enum boolean {FALSE. 10 #include <fstream. while ( (Found == false) && (Done == false) && (InFile. Found = false. }. // end struct void SearchFileSequentially(nameType FileName. DesiredName) == 0) Found = true. nameType NextName. stringType Last. else if (strcmp(NextName. struct person { nameType Name. const int MAX_NAME_LENGTH = 40. nameType DesiredName. stringType Middle. addr Address. TRUE}. int Age. } // end while . }. // skip trailing eoln if (strcmp(NextName. boolean Done = false. MAX_NAME_LENGTH)) ) { InFile >> NextSalary >> ws. typedef char nameType[MAX_NAME_LENGTH+1]. double Salary. double GPA.h> #include <string. DesiredName) == 1) Done = true.getline(NextName.

peek() != EOF) { InFile1 >> X1. } // end if else { OutFile << X2 << endl. OutFile. if (InFile2. Ch) == 0) Found = true.peek() == EOF) Done = true. X2. else InFile1 >> X1. ” << InFileName1 “ and ” << InFileName2. OutFile << X1 << endl. nameType OutFileName) { ifstream InFile1(InFileName1). } // end else } // end while while(InFile1. InFile2. boolean Done = false. ofstream OutFile(OutFileName).close(). while((Found == false) && F.peek() == EOF) Done = true. } // end MergeTwoSortedFiles 12 void AdvanceTo(ifstream& F. char Ch. char Target) { boolean Found = false. void MergeTwoSortedFiles(nameType InFileName1.157 11 Note the following function assumes that the input files have at least one integer. else InFile2 >> X2. } // end AdvanceTo .” << endl. ifstream InFile2(InFileName2). } InFile1. // get the first integer in each file >> X2. OutFile << X2 << endl. nameType InFileName2. while(Done == false) { if (X1 < X2) { OutFile << X1 << endl.get(Ch)) if (strcmp(Target.close(). if (!Found) cout << “The character ” << Target << “ is not in the file. int X1. into one file.peek() != EOF) { InFile1 >> X2. ” << OutFileName << endl. } while(InFile2.close(). “. >> X1. if (InFile1. cout << << cout << InFile1 InFile2 “Merging the files.

txt”). char Temp. Outfile << Temp << endl. I <= 20. ++I) { cout << “Enter character “ << I << “: cin. ofstream Outfile(“temp. Outfile.get(Temp)) Outfile << Temp. 13b void AppendTwentyIntegers(char *Filename) { ifstream Infile(Filename).open(Filename). ios::app). for (int I = 1. // copy the temp file to the original Infile. I <= 20. } // end AppendTwentyIntegers “.open(“temp. ++I) { cout << “Enter integer “ << I << “: cin >> Temp.158 13a void AppendTwentyIntegers(char *Filename) { ofstream Outfile(Filename. // read in original file while(Infile. int Temp. } // end for } // end AppendTwentyIntegers “. Outfile << Temp << endl.close().get(Temp). } // end for Infile. Outfile. .txt”).get(Temp)) Outfile << Temp.close(). for (int I = 1. while(Infile.

12 + 22 + … + n2 + (n + 1)2 = = (n = (n = (n = (n = (n 2n + 1 < n2 ≥ 3. Let 12 + 22 + … + n2 = n(n + 1)(2n + 1)/6. 12 + 22 + … + n2 = n(n + 1)(2n + 1)/6. 02 + 0 = 0 = 0*2. 2 Prove: Inductive basis: Inductive hypothesis: Now. 1(1 + 1)(2(1) + 1)/6 = 6/6 = 1 = 12. n3 . where 2(n + 1) is the (n + 1)st eve And so the identity is proved. 2(n + 1) + 1 = ≤2n + + n 2n 3 since n ≥ 3 < 2n + n 2 < n 2 + 2n + 1 = (n + 1)2. n(n + 1)(2n + 1)/6 + (n + 1)2 by hypothesi + 1)/6[n(2n + 1) + 6(n + 1)] + 1)/6[2n 2 + n + 6n + 6] + 1)/6[2n 2 + 7n + 6] + 1)/6(n + 2)(2n + 3) + 1)[(n + 1) + 1][2(n + 1) + 1]/6.2 for all n n Inductive hypothesis: Now.159 Appendix D Mathematical Induction 1 Inductive basis: The first positive even integer is 2 = 1(1 + 1). n2 + n is divisible by 2 for all n ≥ 0. (n + 1)2 + (n + 1) = n2 + 2n + 1 + n + 1 = (n 2 + n) + (2n + 2) = 2k + 2(n + 1). Let 2n + 1≥ < 3.n is divisible≥by 6 for all n 0.all n for 3 Prove: Inductive basis: For n = 3. (n + 1)(n + 2) = n(n + 1) + 2(n + 1). 2(3) + 1 = 7 < 9 = 32. For n = 0. where n = 1 Let the sum of the first n positive even integers be n(n Inductive hypothesis: Now. . Assume 2 | (n 2 + n) such that n2 + n = 2k for some 4 Prove: Lemma: Inductive basis: Inductive hypothesis: Now. For n = 1.

0) + C(n. n)] + [C(n. C(1. n)] = 2n + 2n + 0 = 2(2n) = 2n+1.n) + (3n 2 + 3n) = (n 3 . n) = 2n. For n = 1. C(n + 1. n) = 2 n. 0) + C(n.k)xk yn−k . (x + y)1 = x + y = xC (. 1) + … + C(n. n + 1) = C(n.n = 2j for some integ Inductive hypothesis: Now. (n + 1)! = (n + 1)n! > (n + 1)n3 by hypothesis = n4 + n3 > n 3 + 3n 2 + 3n≥ + 61 since n = (n + 1)3 . For n = 6. n + 1) = [C(n. 0) + C(n. 1) + … + C(n. 0) + C(n.(n + 1) = (n3 + 3n2 + 3n + 1) .0 = 0 = 0*6. 1) + … + C(n. 0) + C(n. 03 . 0) + C(1. C(n. n! > n3 since n 6 Prove: when n is large enough. 2n+1 = 2(2n) > 2n3 = n3 + n3 > n 3 + 3n 2 + 3n ≥ 10 + 1 = (n + 1)3 . (n + 1)3 .n) + 3(n 2 + n) = 6j + 3(2k) = 6(j + k). 7b Prove: x + ( y) = ∑ C (n. 1) + … + C(n + 1. Inductive basis: Inductive hypothesis: Now. Assume n! > n 3.160 Inductive basis: For n = 0. n n k=0 1 For n = 1. 0) + C(n + 1. 7a Prove: Inductive basis: Inductive hypothesis: Now. For n = 10. 1) + … + C(n. Assume 6 | (n3 . 1) + … + C(n. 1) = 1 + 1 = 2 = 21. 2n > n3 when n ≥ 10. n) + C(n.n) such that n3 . (x + Assume the identity holds for n. n y)n+1 = (x + y)( + y) x . 210 = 1024 > 1000 = 103. Assume C(n. 5 Prove: Inductive basis: Inductive hypothesis: Now. Assume 2n > n3.(n + 1) = (n 3 . 6! = 720 > 216 = 6 3.y) =yx1k 0 + x0y1 = + k1 xk 1− y 1 Inductive basis: ∑ 1 k= 0 Inductive hypothesis: Now. 0) + C(n.

)xk+1 yn− k + ∑ C (n. Rabbit(n + 1) = Rabbit(n) + Rabbit(n . .2 n . R(n) = 2. kx k=0 k=0 n+1 k=0 k= 0 n+1 k=0 n 8 Prove that Rabbit(n) when≥ n 1 and a = (1 )/2.161 = x∑ C (n. (n+1)+1 Then.2 2 a a an a (n + 1) .1 . Let R(n) = 2n+1.+ a (n -n 1) 2.k) y x k k=0 n n n− k + y∑ C (n. R(n + 1) = 2 = 2(2n+1) = 2R(n).1 5 + Inductive basis: Rabbit(1) = 1 5 )/2]0 = [(1 )/2]1 ≤ 1 = [(1 + 5+ Assume ≤Rabbit(n) a n .2 ((6 )/4) 5+ 2 a ((1)/2)2 5 + n .k) k yn+1−k + ∑ C (n.k)xk yn−k+1 x = ∑ C (n + 1.k)xk yn−k k=0 n n = ∑ C (n.2 9 For n = 0. . Inductive hypothesis: Now.1.k) k yn− k+1 k x = ∑ C (n.1 1 = a + a n . ≤ an .2 ((1)/2 + 1) 5 + ((3)/2) 5 + n . Assume this is true for n. ) k yn+1−k . n .n1 .1) ≤ an .1.2 = a (a + 1) = an = a = a = = = = .

Sign up to vote on this title
UsefulNot useful